← back to Games Agentabrams
Add 90210 Trivia game — glam Beverly Hills pop quiz, timed w/ streak multiplier + scorecard
4a984871d6c78a98cdbe08d1d9a43462dc7e65a8 · 2026-07-25 08:45:59 -0700 · Steve Abrams
Files touched
M games-data.jsM games.jsonA games/trivia-90210/90210Trivia.aaA games/trivia-90210/index.html
Diff
commit 4a984871d6c78a98cdbe08d1d9a43462dc7e65a8
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 25 08:45:59 2026 -0700
Add 90210 Trivia game — glam Beverly Hills pop quiz, timed w/ streak multiplier + scorecard
---
games-data.js | 9 +
games.json | 9 +
games/trivia-90210/90210Trivia.aa | 544 ++++++++++++++++++++++++++++++++++++++
games/trivia-90210/index.html | 544 ++++++++++++++++++++++++++++++++++++++
4 files changed, 1106 insertions(+)
diff --git a/games-data.js b/games-data.js
index 1415bee..dbbf714 100644
--- a/games-data.js
+++ b/games-data.js
@@ -283,6 +283,15 @@ window.__GAMES_DATA__ = {
"desc": "Guess the hidden five-letter word in six tries — green marks the right letter in the right spot, yellow the right letter misplaced, gray the misses. On-screen + physical keyboard, persistent stats. Pure vanilla JS, zero dependencies.",
"path": "games/wordle/",
"added": "2026-07-25"
+ },
+ {
+ "id": "trivia-90210",
+ "file": "90210Trivia.aa",
+ "title": "90210 Trivia",
+ "icon": "🌴",
+ "desc": "A glam Beverly Hills pop-culture trivia game — beat the clock on 90210, Rodeo Drive, and Pink Palace questions, stack a streak multiplier, and climb from Peach Pit Local to Rodeo Drive Royalty. Self-contained HTML, zero runtime dependencies.",
+ "path": "games/trivia-90210/",
+ "added": "2026-07-25"
}
]
};
diff --git a/games.json b/games.json
index 90d3dba..da087cc 100644
--- a/games.json
+++ b/games.json
@@ -281,6 +281,15 @@
"desc": "Guess the hidden five-letter word in six tries — green marks the right letter in the right spot, yellow the right letter misplaced, gray the misses. On-screen + physical keyboard, persistent stats. Pure vanilla JS, zero dependencies.",
"path": "games/wordle/",
"added": "2026-07-25"
+ },
+ {
+ "id": "trivia-90210",
+ "file": "90210Trivia.aa",
+ "title": "90210 Trivia",
+ "icon": "🌴",
+ "desc": "A glam Beverly Hills pop-culture trivia game — beat the clock on 90210, Rodeo Drive, and Pink Palace questions, stack a streak multiplier, and climb from Peach Pit Local to Rodeo Drive Royalty. Self-contained HTML, zero runtime dependencies.",
+ "path": "games/trivia-90210/",
+ "added": "2026-07-25"
}
]
}
diff --git a/games/trivia-90210/90210Trivia.aa b/games/trivia-90210/90210Trivia.aa
new file mode 100644
index 0000000..d606217
--- /dev/null
+++ b/games/trivia-90210/90210Trivia.aa
@@ -0,0 +1,544 @@
+<!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>90210 Trivia — Beverly Hills Pop Quiz</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700;900&family=Inter:wght@400;500;600;700&display=swap');
+
+*{margin:0;padding:0;box-sizing:border-box;}
+html{-webkit-text-size-adjust:100%;text-size-adjust:100%;height:100%;}
+/* iOS / iPad touch hardening: no double-tap zoom, no 300ms delay,
+ no grey tap-flash, no long-press callout on the answer tiles */
+body{touch-action:manipulation;-webkit-tap-highlight-color:transparent;}
+.opt,button,.btn{
+ touch-action:manipulation;-webkit-tap-highlight-color:transparent;
+ -webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
+}
+
+:root{
+ --ivory:#efe3c8; --ivory-dk:#c9b07a;
+ --gold:#e9c46a; --gold-hi:#f6de9b; --peach:#f4a261; --burnt:#e76f51; --teal:#2a9d8f;
+ --ink:#1a1410; --ink-soft:#2a211a;
+ --green:#41c98a; --red:#e5604f;
+ --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 18% 0%, rgba(233,196,106,.22), transparent 42%),
+ 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,28px);
+ overflow:hidden;
+}
+
+.stage{
+ width:min(560px, 100%);
+ max-height:100%;
+ display:flex; flex-direction:column;
+}
+
+/* ---- Card ---- */
+.card{
+ position:relative;
+ background:
+ linear-gradient(180deg, rgba(255,255,255,.05), rgba(0,0,0,.18)),
+ linear-gradient(155deg, #2c2118, #1b140e);
+ border:1px solid rgba(233,196,106,.35);
+ border-radius:22px;
+ padding:clamp(18px,4vw,30px);
+ box-shadow:0 30px 70px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.06);
+ overflow:hidden;
+}
+.card::before{ /* gold hairline frame */
+ content:""; position:absolute; inset:8px; border:1px solid rgba(233,196,106,.18);
+ border-radius:15px; pointer-events:none;
+}
+
+/* ---- Header ---- */
+.head{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:14px; }
+.brand{ display:flex; align-items:center; gap:9px; }
+.brand .mark{
+ font-size:20px; filter:drop-shadow(0 2px 4px rgba(0,0,0,.5));
+}
+.brand .zip{
+ font-family:var(--font-disp); font-weight:900; letter-spacing:.02em;
+ font-size:clamp(17px,4.5vw,22px);
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+}
+.brand .sub{ font-size:10px; letter-spacing:.28em; text-transform:uppercase; color:var(--ivory-dk); margin-top:2px; }
+.hud{ display:flex; align-items:center; gap:10px; }
+.pill{
+ font-size:12px; font-weight:700; letter-spacing:.04em;
+ padding:6px 11px; border-radius:999px;
+ border:1px solid rgba(233,196,106,.4);
+ background:linear-gradient(180deg, rgba(233,196,106,.16), rgba(0,0,0,.25));
+ white-space:nowrap;
+}
+.pill .lab{ color:var(--ivory-dk); font-weight:600; font-size:10px; text-transform:uppercase; letter-spacing:.12em; margin-right:5px; }
+.streak.hot{ border-color:var(--peach); box-shadow:0 0 16px rgba(244,162,97,.5); }
+
+/* ---- Timer bar ---- */
+.timer-wrap{ height:6px; border-radius:999px; background:rgba(0,0,0,.35); overflow:hidden; margin-bottom:16px; border:1px solid rgba(233,196,106,.14); }
+.timer-bar{ height:100%; width:100%; transform-origin:left;
+ background:linear-gradient(90deg,var(--gold),var(--peach),var(--burnt));
+ transition:transform .1s linear, background .3s; }
+.timer-bar.warn{ background:linear-gradient(90deg,var(--burnt),#c0392b); }
+
+/* ---- Question ---- */
+.qmeta{ font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--ivory-dk); margin-bottom:8px; }
+.qmeta b{ color:var(--gold); }
+.question{
+ font-family:var(--font-disp); font-weight:700;
+ font-size:clamp(19px,5vw,26px); line-height:1.22;
+ margin-bottom:clamp(16px,3.5vw,22px); min-height:2.4em;
+ display:flex; align-items:center;
+}
+
+/* ---- Options ---- */
+.opts{ display:grid; gap:10px; }
+.opt{
+ position:relative; text-align:left; cursor:pointer;
+ font-family:var(--font-body); font-size:clamp(14px,3.6vw,16px); font-weight:600; color:var(--ivory);
+ padding:14px 16px 14px 48px; border-radius:13px;
+ background:linear-gradient(160deg, rgba(255,255,255,.045), rgba(0,0,0,.14));
+ border:1px solid rgba(233,196,106,.24);
+ transition:transform .12s ease, border-color .18s, background .18s, box-shadow .18s;
+}
+.opt .key{
+ position:absolute; left:12px; top:50%; transform:translateY(-50%);
+ width:26px; height:26px; border-radius:8px;
+ display:flex; align-items:center; justify-content:center;
+ font-family:var(--font-disp); font-weight:700; font-size:13px; color:var(--ink);
+ background:linear-gradient(180deg,var(--gold-hi),var(--ivory-dk));
+}
+.opt:hover:not(.locked){ transform:translateY(-2px); border-color:var(--gold); box-shadow:0 10px 24px rgba(0,0,0,.4); }
+.opt:active:not(.locked){ transform:translateY(0) scale(.99); }
+.opt.locked{ cursor:default; }
+.opt.correct{ border-color:var(--green); background:linear-gradient(160deg, rgba(65,201,138,.28), rgba(65,201,138,.08)); box-shadow:0 0 22px rgba(65,201,138,.35); }
+.opt.correct .key{ background:linear-gradient(180deg,#8ff0c2,var(--green)); }
+.opt.wrong{ border-color:var(--red); background:linear-gradient(160deg, rgba(229,96,79,.24), rgba(229,96,79,.06)); }
+.opt.wrong .key{ background:linear-gradient(180deg,#f2a397,var(--red)); }
+.opt.dim{ opacity:.42; }
+.opt .verdict{ position:absolute; right:14px; top:50%; transform:translateY(-50%); font-size:16px; opacity:0; transition:opacity .2s; }
+.opt.correct .verdict, .opt.wrong .verdict{ opacity:1; }
+
+.footline{ display:flex; align-items:center; justify-content:space-between; margin-top:14px; min-height:20px;
+ font-size:12px; color:var(--ivory-dk); }
+.footline .pts{ color:var(--gold); font-weight:700; }
+
+/* ---- Buttons ---- */
+.btn{
+ font-family:var(--font-body); font-weight:700; font-size:14px; letter-spacing:.06em;
+ color:var(--ink); cursor:pointer; border:none;
+ padding:14px 26px; border-radius:999px;
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ box-shadow:0 12px 28px rgba(0,0,0,.45); text-transform:uppercase;
+ transition:transform .14s, box-shadow .14s, filter .14s;
+}
+.btn:hover{ transform:translateY(-2px); filter:brightness(1.06); }
+.btn:active{ transform:translateY(0); }
+.btn.ghost{ background:transparent; color:var(--ivory); border:1px solid rgba(233,196,106,.4); box-shadow:none; }
+.btn.ghost:hover{ border-color:var(--gold); }
+
+/* ---- Screens ---- */
+.screen{ display:none; }
+.screen.on{ display:block; animation:fade .4s ease both; }
+@keyframes fade{ from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:none;} }
+
+/* Start screen */
+.start{ text-align:center; padding:clamp(6px,2vw,14px) 0; }
+.start .big{
+ font-family:var(--font-disp); font-weight:900; line-height:.98;
+ font-size:clamp(38px,11vw,64px);
+ background:linear-gradient(180deg,var(--gold-hi) 0%, var(--peach) 70%, var(--burnt) 100%);
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+ filter:drop-shadow(0 4px 10px rgba(0,0,0,.4));
+ margin:6px 0 2px;
+}
+.start .kicker{ font-size:12px; letter-spacing:.34em; text-transform:uppercase; color:var(--ivory-dk); }
+.start .tag{ font-family:var(--font-disp); font-style:italic; font-size:clamp(15px,4.2vw,19px); color:var(--ivory); margin:14px 0 6px; }
+.start .blurb{ font-size:13px; color:var(--ivory-dk); max-width:38ch; margin:0 auto 22px; line-height:1.6; }
+.start .row{ display:flex; gap:10px; justify-content:center; flex-wrap:wrap; }
+.palms{ font-size:26px; letter-spacing:6px; margin:16px 0 4px; opacity:.9; }
+.best-note{ font-size:12px; color:var(--gold); margin-top:16px; min-height:18px; letter-spacing:.04em; }
+
+/* Result screen */
+.result{ text-align:center; }
+.result .rank{
+ font-family:var(--font-disp); font-weight:900; font-size:clamp(26px,7vw,40px);
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+ margin:4px 0 2px;
+}
+.result .rank-sub{ font-size:12px; letter-spacing:.24em; text-transform:uppercase; color:var(--ivory-dk); margin-bottom:18px; }
+.scorecard{
+ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin:14px 0 22px;
+}
+.stat{
+ background:linear-gradient(160deg, rgba(255,255,255,.05), rgba(0,0,0,.2));
+ border:1px solid rgba(233,196,106,.28); border-radius:14px; padding:14px 8px;
+}
+.stat .n{ font-family:var(--font-disp); font-weight:900; font-size:clamp(22px,6vw,30px); color:var(--gold); }
+.stat .l{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--ivory-dk); margin-top:4px; }
+.new-best{ display:inline-block; font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
+ color:var(--ink); background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ padding:4px 12px; border-radius:999px; margin-bottom:14px; }
+</style>
+</head>
+<body>
+<div class="stage">
+ <div class="card">
+
+ <!-- ===== START ===== -->
+ <section class="screen on" id="scr-start">
+ <div class="start">
+ <div class="kicker">Beverly Hills Pop Quiz</div>
+ <div class="big">90210</div>
+ <div class="palms">🌴 💎 🌴</div>
+ <div class="tag">How well do you know the Zip?</div>
+ <p class="blurb">Ten glam questions on the show, the street, and the Pink Palace. Beat the clock, stack a streak, and climb from Peach Pit Regular to Rodeo Drive Royalty.</p>
+ <div class="row">
+ <button class="btn" id="startBtn">Start Quiz</button>
+ </div>
+ <div class="best-note" id="bestNote"></div>
+ </div>
+ </section>
+
+ <!-- ===== QUIZ ===== -->
+ <section class="screen" id="scr-quiz">
+ <div class="head">
+ <div class="brand">
+ <span class="mark">🌴</span>
+ <div>
+ <div class="zip">90210 Trivia</div>
+ <div class="sub">Beverly Hills</div>
+ </div>
+ </div>
+ <div class="hud">
+ <div class="pill streak" id="streakPill"><span class="lab">Streak</span><span id="streakVal">🔥0</span></div>
+ <div class="pill"><span class="lab">Score</span><span id="scoreVal">0</span></div>
+ </div>
+ </div>
+
+ <div class="timer-wrap"><div class="timer-bar" id="timerBar"></div></div>
+
+ <div class="qmeta">Question <b id="qNum">1</b> / <span id="qTot">10</span></div>
+ <div class="question" id="questionText">—</div>
+ <div class="opts" id="opts"></div>
+
+ <div class="footline">
+ <span id="feedback">Tap the answer you think is right.</span>
+ <span class="pts" id="ptsGain"></span>
+ </div>
+ </section>
+
+ <!-- ===== RESULT ===== -->
+ <section class="screen" id="scr-result">
+ <div class="result">
+ <div class="kicker" style="font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--ivory-dk)">Your Verdict</div>
+ <div id="newBestWrap"></div>
+ <div class="rank" id="rankTitle">—</div>
+ <div class="rank-sub" id="rankSub"></div>
+ <div class="scorecard">
+ <div class="stat"><div class="n" id="rScore">0</div><div class="l">Score</div></div>
+ <div class="stat"><div class="n" id="rAcc">0%</div><div class="l">Accuracy</div></div>
+ <div class="stat"><div class="n" id="rStreak">0</div><div class="l">Best Streak</div></div>
+ </div>
+ <div class="row" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
+ <button class="btn" id="againBtn">Play Again</button>
+ <button class="btn ghost" id="shareBtn">Share Score</button>
+ </div>
+ <div class="best-note" id="shareNote"></div>
+ </div>
+ </section>
+
+ </div>
+</div>
+
+<script>
+(function(){
+ "use strict";
+
+ // ---------- Question bank (every answer verified) ----------
+ // correct: index into the ORIGINAL a[] array (options are shuffled at render).
+ var BANK = [
+ { q:"Which zip code is famously synonymous with Beverly Hills glamour?",
+ a:["90210","90211","90209","90212"], correct:0 },
+ { q:'The teen drama "Beverly Hills, 90210" originally aired on which network?',
+ a:["Fox","NBC","ABC","CBS"], correct:0 },
+ { q:"What was the name of the gang's favorite diner hangout on the show?",
+ a:["The Peach Pit","The Max","Arnold's","The Bronze"], correct:0 },
+ { q:"Which street is the global symbol of Beverly Hills luxury shopping?",
+ a:["Rodeo Drive","Melrose Avenue","Sunset Boulevard","Mulholland Drive"], correct:0 },
+ { q:"Twins Brandon and Brenda Walsh moved to Beverly Hills from which state?",
+ a:["Minnesota","Ohio","Kansas","Michigan"], correct:0 },
+ { q:"Who played the brooding bad boy Dylan McKay?",
+ a:["Luke Perry","Jason Priestley","Ian Ziering","Brian Austin Green"], correct:0 },
+ { q:"What was the name of the high school the characters attended?",
+ a:["West Beverly Hills High","Bayside High","Sunnydale High","Rydell High"], correct:0 },
+ { q:'The mega-producer behind "Beverly Hills, 90210" was?',
+ a:["Aaron Spelling","Dick Wolf","Norman Lear","J.J. Abrams"], correct:0 },
+ { q:"Which actress played Donna Martin — and is the producer's daughter?",
+ a:["Tori Spelling","Jennie Garth","Shannen Doherty","Gabrielle Carteris"], correct:0 },
+ { q:'The famous pink "Beverly Hills Hotel" is nicknamed the?',
+ a:["Pink Palace","Flamingo","Rose House","Peninsula"], correct:0 },
+ { q:'"Pretty Woman" (1990) famously filmed at which Beverly Hills hotel?',
+ a:["The Beverly Wilshire","Chateau Marmont","The Roosevelt","Hotel Bel-Air"], correct:0 },
+ { q:"The city of Beverly Hills is bordered largely by which bigger city?",
+ a:["Los Angeles","Santa Monica","Culver City","Burbank"], correct:0 },
+ { q:"Ian Ziering played which wisecracking, well-off character?",
+ a:["Steve Sanders","David Silver","Brandon Walsh","Dylan McKay"], correct:0 },
+ { q:"David Silver, who becomes a DJ, was played by?",
+ a:["Brian Austin Green","Jason Priestley","Luke Perry","Ian Ziering"], correct:0 },
+ { q:"The 2008 reboot of the franchise was titled simply?",
+ a:['"90210"',"Melrose Place","The O.C.","One Tree Hill"], correct:0 },
+ { q:"Which sister show was a 90210 spinoff set in an L.A. apartment complex?",
+ a:["Melrose Place","Dawson's Creek","Party of Five","The O.C."], correct:0 },
+ { q:"Kelly Taylor was portrayed by which actress?",
+ a:["Jennie Garth","Shannen Doherty","Tori Spelling","Tiffani Thiessen"], correct:0 },
+ { q:"Brenda Walsh was played by?",
+ a:["Shannen Doherty","Jennie Garth","Tori Spelling","Tiffani Thiessen"], correct:0 },
+ { q:"Rodeo Drive sits in which U.S. state?",
+ a:["California","New York","Florida","Nevada"], correct:0 },
+ { q:"Which trees line the boulevards and are iconic to Beverly Hills?",
+ a:["Palm trees","Oak trees","Redwoods","Cherry blossoms"], correct:0 }
+ ];
+
+ var ROUND = 10; // questions per game
+ var TIME = 12; // seconds per question
+ var BASE = 100; // base points for a correct answer
+ var LS_BEST = "trivia90210.best";
+
+ // ---------- State ----------
+ var deck = [], idx = 0, score = 0, streak = 0, bestStreak = 0, correctCount = 0;
+ var timer = null, timeLeft = TIME, locked = false, curCorrectIdx = -1, tickStart = 0;
+
+ // ---------- Elements ----------
+ var $ = function(id){ return document.getElementById(id); };
+ var scr = { start:$("scr-start"), quiz:$("scr-quiz"), result:$("scr-result") };
+
+ function show(name){
+ Object.keys(scr).forEach(function(k){ scr[k].classList.toggle("on", k===name); });
+ }
+
+ function shuffle(arr){
+ var a = arr.slice();
+ for(var i=a.length-1;i>0;i--){ var j=Math.floor(Math.random()*(i+1)); var t=a[i];a[i]=a[j];a[j]=t; }
+ return a;
+ }
+
+ function bestScore(){ var v = parseInt(localStorage.getItem(LS_BEST)||"0",10); return isNaN(v)?0:v; }
+
+ function refreshBestNote(){
+ var b = bestScore();
+ $("bestNote").textContent = b>0 ? ("👑 Your best score: " + b) : "";
+ }
+
+ // ---------- Game flow ----------
+ function startGame(){
+ deck = shuffle(BANK).slice(0, ROUND);
+ idx = 0; score = 0; streak = 0; bestStreak = 0; correctCount = 0;
+ $("qTot").textContent = deck.length;
+ $("scoreVal").textContent = "0";
+ updateStreak();
+ show("quiz");
+ renderQuestion();
+ }
+
+ function renderQuestion(){
+ locked = false;
+ var item = deck[idx];
+ $("qNum").textContent = (idx+1);
+ $("questionText").textContent = item.q;
+ $("feedback").textContent = "Tap the answer you think is right.";
+ $("ptsGain").textContent = "";
+
+ // Shuffle options, remember where the correct one landed.
+ var order = shuffle(item.a.map(function(_,i){ return i; }));
+ curCorrectIdx = order.indexOf(item.correct);
+
+ var host = $("opts");
+ host.innerHTML = "";
+ var keys = ["A","B","C","D"];
+ order.forEach(function(origIndex, pos){
+ var btn = document.createElement("button");
+ btn.className = "opt";
+ btn.type = "button";
+ btn.dataset.pos = pos;
+ btn.innerHTML = '<span class="key">'+keys[pos]+'</span>' +
+ '<span class="txt"></span>' +
+ '<span class="verdict"></span>';
+ btn.querySelector(".txt").textContent = item.a[origIndex];
+ btn.addEventListener("click", function(){ answer(pos); });
+ host.appendChild(btn);
+ });
+
+ startTimer();
+ }
+
+ function startTimer(){
+ timeLeft = TIME; tickStart = Date.now();
+ setBar(1, false);
+ clearInterval(timer);
+ timer = setInterval(function(){
+ var elapsed = (Date.now()-tickStart)/1000;
+ var frac = Math.max(0, 1 - elapsed/TIME);
+ timeLeft = TIME*frac;
+ setBar(frac, frac < 0.3);
+ if(frac<=0){ clearInterval(timer); timeUp(); }
+ }, 80);
+ }
+
+ function setBar(frac, warn){
+ var bar = $("timerBar");
+ bar.style.transform = "scaleX(" + frac + ")";
+ bar.classList.toggle("warn", !!warn);
+ }
+
+ function answer(pos){
+ if(locked) return;
+ locked = true;
+ clearInterval(timer);
+ var elapsed = (Date.now()-tickStart)/1000;
+ var opts = Array.prototype.slice.call(document.querySelectorAll(".opt"));
+ opts.forEach(function(o){ o.classList.add("locked"); });
+
+ var right = (pos === curCorrectIdx);
+ var correctEl = opts[curCorrectIdx];
+ correctEl.classList.add("correct");
+ correctEl.querySelector(".verdict").textContent = "✓";
+
+ if(right){
+ correctCount++;
+ streak++;
+ if(streak>bestStreak) bestStreak = streak;
+ // Points: base + speed bonus (up to +80) + streak multiplier.
+ var speed = Math.round(80 * Math.max(0, 1 - elapsed/TIME));
+ var mult = 1 + Math.max(0, streak-1) * 0.25; // 1x,1.25x,1.5x...
+ var gain = Math.round((BASE + speed) * mult);
+ score += gain;
+ $("scoreVal").textContent = score;
+ $("ptsGain").textContent = "+" + gain + (streak>1 ? " ("+mult.toFixed(2)+"×)" : "");
+ $("feedback").textContent = streak>=3 ? ("🔥 On fire — "+streak+" in a row!") : "Correct!";
+ } else {
+ streak = 0;
+ opts[pos].classList.add("wrong");
+ opts[pos].querySelector(".verdict").textContent = "✕";
+ opts.forEach(function(o,i){ if(i!==pos && i!==curCorrectIdx) o.classList.add("dim"); });
+ $("feedback").textContent = "Not quite — streak reset.";
+ $("ptsGain").textContent = "";
+ }
+ updateStreak();
+ setTimeout(nextQuestion, right ? 950 : 1500);
+ }
+
+ function timeUp(){
+ if(locked) return;
+ locked = true;
+ var opts = Array.prototype.slice.call(document.querySelectorAll(".opt"));
+ opts.forEach(function(o,i){
+ o.classList.add("locked");
+ if(i===curCorrectIdx){ o.classList.add("correct"); o.querySelector(".verdict").textContent="✓"; }
+ else o.classList.add("dim");
+ });
+ streak = 0; updateStreak();
+ $("feedback").textContent = "⏰ Out of time — streak reset.";
+ setTimeout(nextQuestion, 1500);
+ }
+
+ function nextQuestion(){
+ idx++;
+ if(idx >= deck.length) finish();
+ else renderQuestion();
+ }
+
+ function updateStreak(){
+ $("streakVal").textContent = "🔥" + streak;
+ $("streakPill").classList.toggle("hot", streak>=3);
+ }
+
+ // ---------- Result ----------
+ function rankFor(pct, sc){
+ if(pct>=100) return { t:"Rodeo Drive Royalty", s:"Flawless — the Zip bows to you" };
+ if(pct>=80) return { t:"Beverly Hills A-Lister", s:"Valet's got your Bentley out front" };
+ if(pct>=60) return { t:"Pink Palace Regular", s:"Poolside with the in-crowd" };
+ if(pct>=40) return { t:"Peach Pit Local", s:"A booth's always waiting for you" };
+ if(pct>=20) return { t:"West Bev Freshman", s:"New in town — keep studying" };
+ return { t:"Tourist on Rodeo", s:"Just window shopping for now" };
+ }
+
+ function finish(){
+ clearInterval(timer);
+ var pct = Math.round(100 * correctCount / deck.length);
+ var rk = rankFor(pct, score);
+ $("rankTitle").textContent = rk.t;
+ $("rankSub").textContent = rk.s;
+ $("rScore").textContent = score;
+ $("rAcc").textContent = pct + "%";
+ $("rStreak").textContent = bestStreak;
+
+ var prevBest = bestScore();
+ if(score > prevBest){
+ localStorage.setItem(LS_BEST, String(score));
+ $("newBestWrap").innerHTML = '<span class="new-best">✨ New Personal Best</span>';
+ } else {
+ $("newBestWrap").innerHTML = "";
+ }
+ $("shareNote").textContent = "";
+ show("result");
+ }
+
+ // ---------- Share ----------
+ function share(){
+ var pct = Math.round(100 * correctCount / deck.length);
+ var rk = rankFor(pct, score);
+ var text = "I scored " + score + " on 90210 Trivia — " + rk.t + " (" + pct + "% right). Can you beat the Zip? 🌴💎";
+ if(navigator.share){
+ navigator.share({ title:"90210 Trivia", text:text }).catch(function(){});
+ } else if(navigator.clipboard && navigator.clipboard.writeText){
+ navigator.clipboard.writeText(text).then(function(){ $("shareNote").textContent = "📋 Score copied — paste it anywhere."; })
+ .catch(function(){ $("shareNote").textContent = text; });
+ } else {
+ $("shareNote").textContent = text;
+ }
+ }
+
+ // ---------- Wire up ----------
+ $("startBtn").addEventListener("click", startGame);
+ $("againBtn").addEventListener("click", startGame);
+ $("shareBtn").addEventListener("click", share);
+
+ // Keyboard: 1-4 / A-D to answer, Enter to start/replay.
+ document.addEventListener("keydown", function(e){
+ if(scr.quiz.classList.contains("on") && !locked){
+ var map = { "1":0,"2":1,"3":2,"4":3,"a":0,"b":1,"c":2,"d":3 };
+ var k = e.key.toLowerCase();
+ if(map[k]!=null){ var el=document.querySelector('.opt[data-pos="'+map[k]+'"]'); if(el){ e.preventDefault(); el.click(); } }
+ } else if(e.key==="Enter"){
+ if(scr.start.classList.contains("on")) startGame();
+ else if(scr.result.classList.contains("on")) startGame();
+ }
+ });
+
+ refreshBestNote();
+
+ // Test/automation hook (mirrors the hub's other games exposing a window handle).
+ window.__trivia = {
+ start:startGame,
+ answerCorrect:function(){ if(!locked){ var el=document.querySelector('.opt[data-pos="'+curCorrectIdx+'"]'); if(el) el.click(); } },
+ state:function(){ return { screen:(scr.quiz.classList.contains("on")?"quiz":scr.result.classList.contains("on")?"result":"start"), idx:idx, total:deck.length, score:score, streak:streak, correct:correctCount, locked:locked }; }
+ };
+})();
+</script>
+</body>
+</html>
diff --git a/games/trivia-90210/index.html b/games/trivia-90210/index.html
new file mode 100644
index 0000000..d606217
--- /dev/null
+++ b/games/trivia-90210/index.html
@@ -0,0 +1,544 @@
+<!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>90210 Trivia — Beverly Hills Pop Quiz</title>
+<style>
+@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:wght@600;700;900&family=Inter:wght@400;500;600;700&display=swap');
+
+*{margin:0;padding:0;box-sizing:border-box;}
+html{-webkit-text-size-adjust:100%;text-size-adjust:100%;height:100%;}
+/* iOS / iPad touch hardening: no double-tap zoom, no 300ms delay,
+ no grey tap-flash, no long-press callout on the answer tiles */
+body{touch-action:manipulation;-webkit-tap-highlight-color:transparent;}
+.opt,button,.btn{
+ touch-action:manipulation;-webkit-tap-highlight-color:transparent;
+ -webkit-user-select:none;user-select:none;-webkit-touch-callout:none;
+}
+
+:root{
+ --ivory:#efe3c8; --ivory-dk:#c9b07a;
+ --gold:#e9c46a; --gold-hi:#f6de9b; --peach:#f4a261; --burnt:#e76f51; --teal:#2a9d8f;
+ --ink:#1a1410; --ink-soft:#2a211a;
+ --green:#41c98a; --red:#e5604f;
+ --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 18% 0%, rgba(233,196,106,.22), transparent 42%),
+ 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,28px);
+ overflow:hidden;
+}
+
+.stage{
+ width:min(560px, 100%);
+ max-height:100%;
+ display:flex; flex-direction:column;
+}
+
+/* ---- Card ---- */
+.card{
+ position:relative;
+ background:
+ linear-gradient(180deg, rgba(255,255,255,.05), rgba(0,0,0,.18)),
+ linear-gradient(155deg, #2c2118, #1b140e);
+ border:1px solid rgba(233,196,106,.35);
+ border-radius:22px;
+ padding:clamp(18px,4vw,30px);
+ box-shadow:0 30px 70px rgba(0,0,0,.6), inset 0 1px 0 rgba(255,255,255,.06);
+ overflow:hidden;
+}
+.card::before{ /* gold hairline frame */
+ content:""; position:absolute; inset:8px; border:1px solid rgba(233,196,106,.18);
+ border-radius:15px; pointer-events:none;
+}
+
+/* ---- Header ---- */
+.head{ display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:14px; }
+.brand{ display:flex; align-items:center; gap:9px; }
+.brand .mark{
+ font-size:20px; filter:drop-shadow(0 2px 4px rgba(0,0,0,.5));
+}
+.brand .zip{
+ font-family:var(--font-disp); font-weight:900; letter-spacing:.02em;
+ font-size:clamp(17px,4.5vw,22px);
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+}
+.brand .sub{ font-size:10px; letter-spacing:.28em; text-transform:uppercase; color:var(--ivory-dk); margin-top:2px; }
+.hud{ display:flex; align-items:center; gap:10px; }
+.pill{
+ font-size:12px; font-weight:700; letter-spacing:.04em;
+ padding:6px 11px; border-radius:999px;
+ border:1px solid rgba(233,196,106,.4);
+ background:linear-gradient(180deg, rgba(233,196,106,.16), rgba(0,0,0,.25));
+ white-space:nowrap;
+}
+.pill .lab{ color:var(--ivory-dk); font-weight:600; font-size:10px; text-transform:uppercase; letter-spacing:.12em; margin-right:5px; }
+.streak.hot{ border-color:var(--peach); box-shadow:0 0 16px rgba(244,162,97,.5); }
+
+/* ---- Timer bar ---- */
+.timer-wrap{ height:6px; border-radius:999px; background:rgba(0,0,0,.35); overflow:hidden; margin-bottom:16px; border:1px solid rgba(233,196,106,.14); }
+.timer-bar{ height:100%; width:100%; transform-origin:left;
+ background:linear-gradient(90deg,var(--gold),var(--peach),var(--burnt));
+ transition:transform .1s linear, background .3s; }
+.timer-bar.warn{ background:linear-gradient(90deg,var(--burnt),#c0392b); }
+
+/* ---- Question ---- */
+.qmeta{ font-size:11px; letter-spacing:.2em; text-transform:uppercase; color:var(--ivory-dk); margin-bottom:8px; }
+.qmeta b{ color:var(--gold); }
+.question{
+ font-family:var(--font-disp); font-weight:700;
+ font-size:clamp(19px,5vw,26px); line-height:1.22;
+ margin-bottom:clamp(16px,3.5vw,22px); min-height:2.4em;
+ display:flex; align-items:center;
+}
+
+/* ---- Options ---- */
+.opts{ display:grid; gap:10px; }
+.opt{
+ position:relative; text-align:left; cursor:pointer;
+ font-family:var(--font-body); font-size:clamp(14px,3.6vw,16px); font-weight:600; color:var(--ivory);
+ padding:14px 16px 14px 48px; border-radius:13px;
+ background:linear-gradient(160deg, rgba(255,255,255,.045), rgba(0,0,0,.14));
+ border:1px solid rgba(233,196,106,.24);
+ transition:transform .12s ease, border-color .18s, background .18s, box-shadow .18s;
+}
+.opt .key{
+ position:absolute; left:12px; top:50%; transform:translateY(-50%);
+ width:26px; height:26px; border-radius:8px;
+ display:flex; align-items:center; justify-content:center;
+ font-family:var(--font-disp); font-weight:700; font-size:13px; color:var(--ink);
+ background:linear-gradient(180deg,var(--gold-hi),var(--ivory-dk));
+}
+.opt:hover:not(.locked){ transform:translateY(-2px); border-color:var(--gold); box-shadow:0 10px 24px rgba(0,0,0,.4); }
+.opt:active:not(.locked){ transform:translateY(0) scale(.99); }
+.opt.locked{ cursor:default; }
+.opt.correct{ border-color:var(--green); background:linear-gradient(160deg, rgba(65,201,138,.28), rgba(65,201,138,.08)); box-shadow:0 0 22px rgba(65,201,138,.35); }
+.opt.correct .key{ background:linear-gradient(180deg,#8ff0c2,var(--green)); }
+.opt.wrong{ border-color:var(--red); background:linear-gradient(160deg, rgba(229,96,79,.24), rgba(229,96,79,.06)); }
+.opt.wrong .key{ background:linear-gradient(180deg,#f2a397,var(--red)); }
+.opt.dim{ opacity:.42; }
+.opt .verdict{ position:absolute; right:14px; top:50%; transform:translateY(-50%); font-size:16px; opacity:0; transition:opacity .2s; }
+.opt.correct .verdict, .opt.wrong .verdict{ opacity:1; }
+
+.footline{ display:flex; align-items:center; justify-content:space-between; margin-top:14px; min-height:20px;
+ font-size:12px; color:var(--ivory-dk); }
+.footline .pts{ color:var(--gold); font-weight:700; }
+
+/* ---- Buttons ---- */
+.btn{
+ font-family:var(--font-body); font-weight:700; font-size:14px; letter-spacing:.06em;
+ color:var(--ink); cursor:pointer; border:none;
+ padding:14px 26px; border-radius:999px;
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ box-shadow:0 12px 28px rgba(0,0,0,.45); text-transform:uppercase;
+ transition:transform .14s, box-shadow .14s, filter .14s;
+}
+.btn:hover{ transform:translateY(-2px); filter:brightness(1.06); }
+.btn:active{ transform:translateY(0); }
+.btn.ghost{ background:transparent; color:var(--ivory); border:1px solid rgba(233,196,106,.4); box-shadow:none; }
+.btn.ghost:hover{ border-color:var(--gold); }
+
+/* ---- Screens ---- */
+.screen{ display:none; }
+.screen.on{ display:block; animation:fade .4s ease both; }
+@keyframes fade{ from{opacity:0; transform:translateY(10px);} to{opacity:1; transform:none;} }
+
+/* Start screen */
+.start{ text-align:center; padding:clamp(6px,2vw,14px) 0; }
+.start .big{
+ font-family:var(--font-disp); font-weight:900; line-height:.98;
+ font-size:clamp(38px,11vw,64px);
+ background:linear-gradient(180deg,var(--gold-hi) 0%, var(--peach) 70%, var(--burnt) 100%);
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+ filter:drop-shadow(0 4px 10px rgba(0,0,0,.4));
+ margin:6px 0 2px;
+}
+.start .kicker{ font-size:12px; letter-spacing:.34em; text-transform:uppercase; color:var(--ivory-dk); }
+.start .tag{ font-family:var(--font-disp); font-style:italic; font-size:clamp(15px,4.2vw,19px); color:var(--ivory); margin:14px 0 6px; }
+.start .blurb{ font-size:13px; color:var(--ivory-dk); max-width:38ch; margin:0 auto 22px; line-height:1.6; }
+.start .row{ display:flex; gap:10px; justify-content:center; flex-wrap:wrap; }
+.palms{ font-size:26px; letter-spacing:6px; margin:16px 0 4px; opacity:.9; }
+.best-note{ font-size:12px; color:var(--gold); margin-top:16px; min-height:18px; letter-spacing:.04em; }
+
+/* Result screen */
+.result{ text-align:center; }
+.result .rank{
+ font-family:var(--font-disp); font-weight:900; font-size:clamp(26px,7vw,40px);
+ background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ -webkit-background-clip:text; background-clip:text; -webkit-text-fill-color:transparent;
+ margin:4px 0 2px;
+}
+.result .rank-sub{ font-size:12px; letter-spacing:.24em; text-transform:uppercase; color:var(--ivory-dk); margin-bottom:18px; }
+.scorecard{
+ display:grid; grid-template-columns:repeat(3,1fr); gap:10px; margin:14px 0 22px;
+}
+.stat{
+ background:linear-gradient(160deg, rgba(255,255,255,.05), rgba(0,0,0,.2));
+ border:1px solid rgba(233,196,106,.28); border-radius:14px; padding:14px 8px;
+}
+.stat .n{ font-family:var(--font-disp); font-weight:900; font-size:clamp(22px,6vw,30px); color:var(--gold); }
+.stat .l{ font-size:10px; letter-spacing:.14em; text-transform:uppercase; color:var(--ivory-dk); margin-top:4px; }
+.new-best{ display:inline-block; font-size:11px; font-weight:700; letter-spacing:.14em; text-transform:uppercase;
+ color:var(--ink); background:linear-gradient(180deg,var(--gold-hi),var(--peach));
+ padding:4px 12px; border-radius:999px; margin-bottom:14px; }
+</style>
+</head>
+<body>
+<div class="stage">
+ <div class="card">
+
+ <!-- ===== START ===== -->
+ <section class="screen on" id="scr-start">
+ <div class="start">
+ <div class="kicker">Beverly Hills Pop Quiz</div>
+ <div class="big">90210</div>
+ <div class="palms">🌴 💎 🌴</div>
+ <div class="tag">How well do you know the Zip?</div>
+ <p class="blurb">Ten glam questions on the show, the street, and the Pink Palace. Beat the clock, stack a streak, and climb from Peach Pit Regular to Rodeo Drive Royalty.</p>
+ <div class="row">
+ <button class="btn" id="startBtn">Start Quiz</button>
+ </div>
+ <div class="best-note" id="bestNote"></div>
+ </div>
+ </section>
+
+ <!-- ===== QUIZ ===== -->
+ <section class="screen" id="scr-quiz">
+ <div class="head">
+ <div class="brand">
+ <span class="mark">🌴</span>
+ <div>
+ <div class="zip">90210 Trivia</div>
+ <div class="sub">Beverly Hills</div>
+ </div>
+ </div>
+ <div class="hud">
+ <div class="pill streak" id="streakPill"><span class="lab">Streak</span><span id="streakVal">🔥0</span></div>
+ <div class="pill"><span class="lab">Score</span><span id="scoreVal">0</span></div>
+ </div>
+ </div>
+
+ <div class="timer-wrap"><div class="timer-bar" id="timerBar"></div></div>
+
+ <div class="qmeta">Question <b id="qNum">1</b> / <span id="qTot">10</span></div>
+ <div class="question" id="questionText">—</div>
+ <div class="opts" id="opts"></div>
+
+ <div class="footline">
+ <span id="feedback">Tap the answer you think is right.</span>
+ <span class="pts" id="ptsGain"></span>
+ </div>
+ </section>
+
+ <!-- ===== RESULT ===== -->
+ <section class="screen" id="scr-result">
+ <div class="result">
+ <div class="kicker" style="font-size:12px;letter-spacing:.3em;text-transform:uppercase;color:var(--ivory-dk)">Your Verdict</div>
+ <div id="newBestWrap"></div>
+ <div class="rank" id="rankTitle">—</div>
+ <div class="rank-sub" id="rankSub"></div>
+ <div class="scorecard">
+ <div class="stat"><div class="n" id="rScore">0</div><div class="l">Score</div></div>
+ <div class="stat"><div class="n" id="rAcc">0%</div><div class="l">Accuracy</div></div>
+ <div class="stat"><div class="n" id="rStreak">0</div><div class="l">Best Streak</div></div>
+ </div>
+ <div class="row" style="display:flex;gap:10px;justify-content:center;flex-wrap:wrap">
+ <button class="btn" id="againBtn">Play Again</button>
+ <button class="btn ghost" id="shareBtn">Share Score</button>
+ </div>
+ <div class="best-note" id="shareNote"></div>
+ </div>
+ </section>
+
+ </div>
+</div>
+
+<script>
+(function(){
+ "use strict";
+
+ // ---------- Question bank (every answer verified) ----------
+ // correct: index into the ORIGINAL a[] array (options are shuffled at render).
+ var BANK = [
+ { q:"Which zip code is famously synonymous with Beverly Hills glamour?",
+ a:["90210","90211","90209","90212"], correct:0 },
+ { q:'The teen drama "Beverly Hills, 90210" originally aired on which network?',
+ a:["Fox","NBC","ABC","CBS"], correct:0 },
+ { q:"What was the name of the gang's favorite diner hangout on the show?",
+ a:["The Peach Pit","The Max","Arnold's","The Bronze"], correct:0 },
+ { q:"Which street is the global symbol of Beverly Hills luxury shopping?",
+ a:["Rodeo Drive","Melrose Avenue","Sunset Boulevard","Mulholland Drive"], correct:0 },
+ { q:"Twins Brandon and Brenda Walsh moved to Beverly Hills from which state?",
+ a:["Minnesota","Ohio","Kansas","Michigan"], correct:0 },
+ { q:"Who played the brooding bad boy Dylan McKay?",
+ a:["Luke Perry","Jason Priestley","Ian Ziering","Brian Austin Green"], correct:0 },
+ { q:"What was the name of the high school the characters attended?",
+ a:["West Beverly Hills High","Bayside High","Sunnydale High","Rydell High"], correct:0 },
+ { q:'The mega-producer behind "Beverly Hills, 90210" was?',
+ a:["Aaron Spelling","Dick Wolf","Norman Lear","J.J. Abrams"], correct:0 },
+ { q:"Which actress played Donna Martin — and is the producer's daughter?",
+ a:["Tori Spelling","Jennie Garth","Shannen Doherty","Gabrielle Carteris"], correct:0 },
+ { q:'The famous pink "Beverly Hills Hotel" is nicknamed the?',
+ a:["Pink Palace","Flamingo","Rose House","Peninsula"], correct:0 },
+ { q:'"Pretty Woman" (1990) famously filmed at which Beverly Hills hotel?',
+ a:["The Beverly Wilshire","Chateau Marmont","The Roosevelt","Hotel Bel-Air"], correct:0 },
+ { q:"The city of Beverly Hills is bordered largely by which bigger city?",
+ a:["Los Angeles","Santa Monica","Culver City","Burbank"], correct:0 },
+ { q:"Ian Ziering played which wisecracking, well-off character?",
+ a:["Steve Sanders","David Silver","Brandon Walsh","Dylan McKay"], correct:0 },
+ { q:"David Silver, who becomes a DJ, was played by?",
+ a:["Brian Austin Green","Jason Priestley","Luke Perry","Ian Ziering"], correct:0 },
+ { q:"The 2008 reboot of the franchise was titled simply?",
+ a:['"90210"',"Melrose Place","The O.C.","One Tree Hill"], correct:0 },
+ { q:"Which sister show was a 90210 spinoff set in an L.A. apartment complex?",
+ a:["Melrose Place","Dawson's Creek","Party of Five","The O.C."], correct:0 },
+ { q:"Kelly Taylor was portrayed by which actress?",
+ a:["Jennie Garth","Shannen Doherty","Tori Spelling","Tiffani Thiessen"], correct:0 },
+ { q:"Brenda Walsh was played by?",
+ a:["Shannen Doherty","Jennie Garth","Tori Spelling","Tiffani Thiessen"], correct:0 },
+ { q:"Rodeo Drive sits in which U.S. state?",
+ a:["California","New York","Florida","Nevada"], correct:0 },
+ { q:"Which trees line the boulevards and are iconic to Beverly Hills?",
+ a:["Palm trees","Oak trees","Redwoods","Cherry blossoms"], correct:0 }
+ ];
+
+ var ROUND = 10; // questions per game
+ var TIME = 12; // seconds per question
+ var BASE = 100; // base points for a correct answer
+ var LS_BEST = "trivia90210.best";
+
+ // ---------- State ----------
+ var deck = [], idx = 0, score = 0, streak = 0, bestStreak = 0, correctCount = 0;
+ var timer = null, timeLeft = TIME, locked = false, curCorrectIdx = -1, tickStart = 0;
+
+ // ---------- Elements ----------
+ var $ = function(id){ return document.getElementById(id); };
+ var scr = { start:$("scr-start"), quiz:$("scr-quiz"), result:$("scr-result") };
+
+ function show(name){
+ Object.keys(scr).forEach(function(k){ scr[k].classList.toggle("on", k===name); });
+ }
+
+ function shuffle(arr){
+ var a = arr.slice();
+ for(var i=a.length-1;i>0;i--){ var j=Math.floor(Math.random()*(i+1)); var t=a[i];a[i]=a[j];a[j]=t; }
+ return a;
+ }
+
+ function bestScore(){ var v = parseInt(localStorage.getItem(LS_BEST)||"0",10); return isNaN(v)?0:v; }
+
+ function refreshBestNote(){
+ var b = bestScore();
+ $("bestNote").textContent = b>0 ? ("👑 Your best score: " + b) : "";
+ }
+
+ // ---------- Game flow ----------
+ function startGame(){
+ deck = shuffle(BANK).slice(0, ROUND);
+ idx = 0; score = 0; streak = 0; bestStreak = 0; correctCount = 0;
+ $("qTot").textContent = deck.length;
+ $("scoreVal").textContent = "0";
+ updateStreak();
+ show("quiz");
+ renderQuestion();
+ }
+
+ function renderQuestion(){
+ locked = false;
+ var item = deck[idx];
+ $("qNum").textContent = (idx+1);
+ $("questionText").textContent = item.q;
+ $("feedback").textContent = "Tap the answer you think is right.";
+ $("ptsGain").textContent = "";
+
+ // Shuffle options, remember where the correct one landed.
+ var order = shuffle(item.a.map(function(_,i){ return i; }));
+ curCorrectIdx = order.indexOf(item.correct);
+
+ var host = $("opts");
+ host.innerHTML = "";
+ var keys = ["A","B","C","D"];
+ order.forEach(function(origIndex, pos){
+ var btn = document.createElement("button");
+ btn.className = "opt";
+ btn.type = "button";
+ btn.dataset.pos = pos;
+ btn.innerHTML = '<span class="key">'+keys[pos]+'</span>' +
+ '<span class="txt"></span>' +
+ '<span class="verdict"></span>';
+ btn.querySelector(".txt").textContent = item.a[origIndex];
+ btn.addEventListener("click", function(){ answer(pos); });
+ host.appendChild(btn);
+ });
+
+ startTimer();
+ }
+
+ function startTimer(){
+ timeLeft = TIME; tickStart = Date.now();
+ setBar(1, false);
+ clearInterval(timer);
+ timer = setInterval(function(){
+ var elapsed = (Date.now()-tickStart)/1000;
+ var frac = Math.max(0, 1 - elapsed/TIME);
+ timeLeft = TIME*frac;
+ setBar(frac, frac < 0.3);
+ if(frac<=0){ clearInterval(timer); timeUp(); }
+ }, 80);
+ }
+
+ function setBar(frac, warn){
+ var bar = $("timerBar");
+ bar.style.transform = "scaleX(" + frac + ")";
+ bar.classList.toggle("warn", !!warn);
+ }
+
+ function answer(pos){
+ if(locked) return;
+ locked = true;
+ clearInterval(timer);
+ var elapsed = (Date.now()-tickStart)/1000;
+ var opts = Array.prototype.slice.call(document.querySelectorAll(".opt"));
+ opts.forEach(function(o){ o.classList.add("locked"); });
+
+ var right = (pos === curCorrectIdx);
+ var correctEl = opts[curCorrectIdx];
+ correctEl.classList.add("correct");
+ correctEl.querySelector(".verdict").textContent = "✓";
+
+ if(right){
+ correctCount++;
+ streak++;
+ if(streak>bestStreak) bestStreak = streak;
+ // Points: base + speed bonus (up to +80) + streak multiplier.
+ var speed = Math.round(80 * Math.max(0, 1 - elapsed/TIME));
+ var mult = 1 + Math.max(0, streak-1) * 0.25; // 1x,1.25x,1.5x...
+ var gain = Math.round((BASE + speed) * mult);
+ score += gain;
+ $("scoreVal").textContent = score;
+ $("ptsGain").textContent = "+" + gain + (streak>1 ? " ("+mult.toFixed(2)+"×)" : "");
+ $("feedback").textContent = streak>=3 ? ("🔥 On fire — "+streak+" in a row!") : "Correct!";
+ } else {
+ streak = 0;
+ opts[pos].classList.add("wrong");
+ opts[pos].querySelector(".verdict").textContent = "✕";
+ opts.forEach(function(o,i){ if(i!==pos && i!==curCorrectIdx) o.classList.add("dim"); });
+ $("feedback").textContent = "Not quite — streak reset.";
+ $("ptsGain").textContent = "";
+ }
+ updateStreak();
+ setTimeout(nextQuestion, right ? 950 : 1500);
+ }
+
+ function timeUp(){
+ if(locked) return;
+ locked = true;
+ var opts = Array.prototype.slice.call(document.querySelectorAll(".opt"));
+ opts.forEach(function(o,i){
+ o.classList.add("locked");
+ if(i===curCorrectIdx){ o.classList.add("correct"); o.querySelector(".verdict").textContent="✓"; }
+ else o.classList.add("dim");
+ });
+ streak = 0; updateStreak();
+ $("feedback").textContent = "⏰ Out of time — streak reset.";
+ setTimeout(nextQuestion, 1500);
+ }
+
+ function nextQuestion(){
+ idx++;
+ if(idx >= deck.length) finish();
+ else renderQuestion();
+ }
+
+ function updateStreak(){
+ $("streakVal").textContent = "🔥" + streak;
+ $("streakPill").classList.toggle("hot", streak>=3);
+ }
+
+ // ---------- Result ----------
+ function rankFor(pct, sc){
+ if(pct>=100) return { t:"Rodeo Drive Royalty", s:"Flawless — the Zip bows to you" };
+ if(pct>=80) return { t:"Beverly Hills A-Lister", s:"Valet's got your Bentley out front" };
+ if(pct>=60) return { t:"Pink Palace Regular", s:"Poolside with the in-crowd" };
+ if(pct>=40) return { t:"Peach Pit Local", s:"A booth's always waiting for you" };
+ if(pct>=20) return { t:"West Bev Freshman", s:"New in town — keep studying" };
+ return { t:"Tourist on Rodeo", s:"Just window shopping for now" };
+ }
+
+ function finish(){
+ clearInterval(timer);
+ var pct = Math.round(100 * correctCount / deck.length);
+ var rk = rankFor(pct, score);
+ $("rankTitle").textContent = rk.t;
+ $("rankSub").textContent = rk.s;
+ $("rScore").textContent = score;
+ $("rAcc").textContent = pct + "%";
+ $("rStreak").textContent = bestStreak;
+
+ var prevBest = bestScore();
+ if(score > prevBest){
+ localStorage.setItem(LS_BEST, String(score));
+ $("newBestWrap").innerHTML = '<span class="new-best">✨ New Personal Best</span>';
+ } else {
+ $("newBestWrap").innerHTML = "";
+ }
+ $("shareNote").textContent = "";
+ show("result");
+ }
+
+ // ---------- Share ----------
+ function share(){
+ var pct = Math.round(100 * correctCount / deck.length);
+ var rk = rankFor(pct, score);
+ var text = "I scored " + score + " on 90210 Trivia — " + rk.t + " (" + pct + "% right). Can you beat the Zip? 🌴💎";
+ if(navigator.share){
+ navigator.share({ title:"90210 Trivia", text:text }).catch(function(){});
+ } else if(navigator.clipboard && navigator.clipboard.writeText){
+ navigator.clipboard.writeText(text).then(function(){ $("shareNote").textContent = "📋 Score copied — paste it anywhere."; })
+ .catch(function(){ $("shareNote").textContent = text; });
+ } else {
+ $("shareNote").textContent = text;
+ }
+ }
+
+ // ---------- Wire up ----------
+ $("startBtn").addEventListener("click", startGame);
+ $("againBtn").addEventListener("click", startGame);
+ $("shareBtn").addEventListener("click", share);
+
+ // Keyboard: 1-4 / A-D to answer, Enter to start/replay.
+ document.addEventListener("keydown", function(e){
+ if(scr.quiz.classList.contains("on") && !locked){
+ var map = { "1":0,"2":1,"3":2,"4":3,"a":0,"b":1,"c":2,"d":3 };
+ var k = e.key.toLowerCase();
+ if(map[k]!=null){ var el=document.querySelector('.opt[data-pos="'+map[k]+'"]'); if(el){ e.preventDefault(); el.click(); } }
+ } else if(e.key==="Enter"){
+ if(scr.start.classList.contains("on")) startGame();
+ else if(scr.result.classList.contains("on")) startGame();
+ }
+ });
+
+ refreshBestNote();
+
+ // Test/automation hook (mirrors the hub's other games exposing a window handle).
+ window.__trivia = {
+ start:startGame,
+ answerCorrect:function(){ if(!locked){ var el=document.querySelector('.opt[data-pos="'+curCorrectIdx+'"]'); if(el) el.click(); } },
+ state:function(){ return { screen:(scr.quiz.classList.contains("on")?"quiz":scr.result.classList.contains("on")?"result":"start"), idx:idx, total:deck.length, score:score, streak:streak, correct:correctCount, locked:locked }; }
+ };
+})();
+</script>
+</body>
+</html>
← 5c7f274 deploy: add rsync --delete so retired games are purged from
·
back to Games Agentabrams
·
90210 Trivia: expand bank to 35 verified questions, swap til 2c42688 →