[object Object]

← 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

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">🌴&nbsp;💎&nbsp;🌴</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">🌴&nbsp;💎&nbsp;🌴</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 →