๐Ÿ’ป Cyber Monday Deal Sort - Real or Scam? ๐Ÿ›’

A new offer pops up every few seconds. Swipe or tap right (or press the right arrow key) if it's a real deal, swipe or tap left (or press the left arrow key) if it's a scam. Three mistakes and the sale is over!
Score: 0  |  Lives: โค๏ธโค๏ธโค๏ธ  |  Best: 0
๐Ÿ›๏ธ
Tap "Start" to see your first offer!
Press "Start", then sort each offer before the timer runs out!

The Cyber Monday sale is live, and the offers are flying in fast. Some are genuine discounts on things you actually want; others are scams dressed up to look like a deal. Can you tell them apart before the clock runs out?

Read each offer, then swipe or tap right (or press the right arrow key) if it looks like a real deal, or swipe or tap left (or press the left arrow key) if you think it is a scam. Every correct call shortens the timer a little, so the pace keeps rising. Three wrong calls, or three offers you were too slow to judge, and the sale is over. How many deals can you sort before that happens?

Here is the code
CSS
.deal-game {
    text-align: center;
}

.deal-game .instructions {
    max-width: 520px;
    margin: 0 auto 15px;
    font-size: 1em;
    color: #555;
}

.deal-info {
    margin-bottom: 10px;
    font-size: 1.1em;
}

.deal-timerbar {
    max-width: 320px;
    height: 8px;
    margin: 0 auto 14px;
    background: #e0e0e0;
    border-radius: 4px;
    overflow: hidden;
}

.deal-timerfill {
    height: 100%;
    width: 100%;
    background: #00897b;
    border-radius: 4px;
    transition: background-color 0.2s;
}

.deal-card {
    max-width: 320px;
    min-height: 150px;
    margin: 0 auto;
    padding: 20px;
    background: #10121c;
    border: 3px solid #00897b;
    border-radius: 14px;
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    touch-action: manipulation;
    user-select: none;
    cursor: pointer;
}

.deal-emoji {
    font-size: 2.6em;
    line-height: 1;
}

.deal-text {
    font-size: 1.05em;
    font-weight: bold;
    line-height: 1.3;
}

.deal-buttons {
    max-width: 320px;
    margin: 14px auto 0;
    display: flex;
    gap: 10px;
}

.deal-game .deal-btn {
    flex: 1;
    padding: 12px 10px;
    font-size: 1em;
    font-weight: bold;
    color: #fff;
    border: none;
    border-radius: 8px;
    cursor: pointer;
    touch-action: manipulation;
}

.deal-game .deal-scam {
    background-color: #c62828;
}

.deal-game .deal-scam:hover {
    background-color: #e53935;
}

.deal-game .deal-real {
    background-color: #2e7d32;
}

.deal-game .deal-real:hover {
    background-color: #43a047;
}

.deal-game .deal-btn:disabled {
    background-color: #ccc;
    cursor: default;
}

.deal-message {
    margin-top: 15px;
    font-weight: bold;
    min-height: 1.4em;
}

.deal-game .start-btn {
    margin-top: 15px;
    padding: 10px 20px;
    font-size: 1em;
    background-color: #00897b;
    color: #fff;
    font-weight: bold;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

.deal-game .start-btn:hover {
    background-color: #00a896;
}

.deal-game .start-btn:disabled {
    background-color: #ccc;
    cursor: default;
}
HTML
<div class="deal-game">
    <h1>๐Ÿ’ป Cyber Monday Deal Sort - Real or Scam? ๐Ÿ›’</h1>
    <div class="instructions">
        A new offer pops up every few seconds. Swipe or tap right (or press the right arrow key) if it's a real deal, swipe or tap left (or press the left arrow key) if it's a scam. Three mistakes and the sale is over!
    </div>
    <div class="deal-info">Score: <span id="dealScore">0</span> &nbsp;|&nbsp; Lives: <span id="dealLives">โค๏ธโค๏ธโค๏ธ</span> &nbsp;|&nbsp; Best: <span id="dealBest">0</span></div>
    <div class="deal-timerbar"><div class="deal-timerfill" id="dealTimerFill"></div></div>
    <div class="deal-card" id="dealCard">
        <div class="deal-emoji" id="dealEmoji">๐Ÿ›๏ธ</div>
        <div class="deal-text" id="dealText">Tap "Start" to see your first offer!</div>
    </div>
    <div class="deal-buttons">
        <button class="deal-btn deal-scam" id="dealScamBtn">๐Ÿšฉ Scam</button>
        <button class="deal-btn deal-real" id="dealRealBtn">โœ… Real Deal</button>
    </div>
    <div class="deal-message" id="dealMessage">Press "Start", then sort each offer before the timer runs out!</div>
    <button class="start-btn" id="dealStart">Start</button>
</div>
JS
var dealCard = document.getElementById('dealCard');
var dealEmoji = document.getElementById('dealEmoji');
var dealText = document.getElementById('dealText');
var dealScoreDisplay = document.getElementById('dealScore');
var dealLivesDisplay = document.getElementById('dealLives');
var dealBestDisplay = document.getElementById('dealBest');
var dealMessage = document.getElementById('dealMessage');
var dealStartButton = document.getElementById('dealStart');
var dealScamBtn = document.getElementById('dealScamBtn');
var dealRealBtn = document.getElementById('dealRealBtn');
var dealTimerFill = document.getElementById('dealTimerFill');

var MAX_LIVES = 3;
var MAX_TIME = 4.2;
var MIN_TIME = 1.6;

var REAL_DEALS = [
    { emoji: '๐ŸŽง', text: 'Wireless headphones, 30% off this Cyber Monday' },
    { emoji: '๐Ÿ“ฑ', text: "Last year's phone model, 25% off at the official store" },
    { emoji: '๐Ÿ’ป', text: 'Laptop, 20% off from the store you already shop at' },
    { emoji: '๐Ÿ‘Ÿ', text: 'Sneakers, buy one pair get the second 50% off' },
    { emoji: '๐Ÿ“บ', text: 'TV, 35% off, normal shipping time' },
    { emoji: '๐Ÿงธ', text: 'Toys, 15% off storewide, no coupon needed' },
    { emoji: 'โŒš', text: 'Smartwatch, 40% off on the brandโ€™s own website' },
    { emoji: '๐ŸŽฎ', text: 'Game console bundle, 10% off plus a free controller' },
    { emoji: '๐Ÿงด', text: 'Skincare set, 20% off with free standard shipping' },
    { emoji: '๐ŸŽ’', text: 'Backpack, 25% off, reviews look genuine' }
];

var SCAM_DEALS = [
    { emoji: '๐ŸŽ', text: 'FREE iPhone, just pay $1.99 "shipping"!!!' },
    { emoji: '๐Ÿ’ณ', text: 'Your account is locked, click here to verify now' },
    { emoji: '๐Ÿ“ฆ', text: '99% off any order, only for the next 3 minutes' },
    { emoji: '๐Ÿ†', text: 'You won a $1000 gift card, claim it in 5 minutes' },
    { emoji: '๐Ÿ”‘', text: 'Enter your password here to unlock a secret deal' },
    { emoji: '๐Ÿ’ธ', text: 'Guaranteed to 10x your money this Cyber Monday' },
    { emoji: '๐Ÿ“ง', text: 'URGENT: confirm your bank details for instant cashback' },
    { emoji: '๐Ÿ–ฅ๏ธ', text: 'Get a free laptop for sharing this post 100 times' },
    { emoji: '๐Ÿšš', text: 'Pay a "customs fee" to release your surprise package' },
    { emoji: '๐Ÿ“ฒ', text: 'Install this app to double your Cyber Monday coupons' }
];

var score, lives, best, running, current, timeLeft, timeTotal, lastTime, animationId, dragStartX, dragging, deciding;

best = 0;
try {
    best = parseInt(localStorage.getItem('dealSortBest'), 10) || 0;
} catch (e) {
    best = 0;
}
dealBestDisplay.textContent = best;

function setShareButtons(display) {
    var shareButtons = document.querySelector('.share-buttons');
    if (shareButtons) {
        shareButtons.style.display = display;
    }
}

function setScore(value) {
    score = value;
    dealScoreDisplay.textContent = score;
    var hiddenScore = document.getElementById('hiddenScore');
    if (hiddenScore) {
        hiddenScore.value = score;
    }
}

function showLives() {
    var text = '';
    for (var i = 0; i < MAX_LIVES; i++) {
        text += i < lives ? 'โค๏ธ' : '๐Ÿค';
    }
    dealLivesDisplay.textContent = text;
}

function timeForScore(s) {
    var t = MAX_TIME - s * 0.18;
    return t < MIN_TIME ? MIN_TIME : t;
}

function pickDeal() {
    var isReal = Math.random() < 0.5;
    var list = isReal ? REAL_DEALS : SCAM_DEALS;
    var pick = list[Math.floor(Math.random() * list.length)];
    return { emoji: pick.emoji, text: pick.text, real: isReal };
}

function nextCard() {
    current = pickDeal();
    dealEmoji.textContent = current.emoji;
    dealText.textContent = current.text;
    dealCard.style.opacity = '1';
    timeTotal = timeForScore(score);
    timeLeft = timeTotal;
    deciding = true;
    dealTimerFill.style.background = '#00897b';
}

function reset() {
    lives = MAX_LIVES;
    deciding = false;
    dragging = false;
    setScore(0);
    showLives();
}

function loseLife(reasonText) {
    lives -= 1;
    showLives();
    dealMessage.textContent = reasonText;
    dealCard.style.opacity = '0.4';
    if (lives <= 0) {
        endGame();
        return false;
    }
    return true;
}

function decide(saidReal) {
    if (!running || !deciding) {
        return;
    }
    deciding = false;
    if (saidReal === current.real) {
        setScore(score + 1);
        dealMessage.textContent = current.real ? 'Correct, a real deal! โœ…' : 'Correct, that was a scam! ๐Ÿšฉ';
        setTimeout(function () {
            if (running) {
                nextCard();
            }
        }, 250);
    } else {
        var reason = current.real ? 'Oops, that was a real deal! โŒ' : 'Oops, that was a scam! โŒ';
        if (loseLife(reason)) {
            setTimeout(function () {
                if (running) {
                    nextCard();
                }
            }, 450);
        }
    }
}

function timeout() {
    if (!running || !deciding) {
        return;
    }
    deciding = false;
    if (loseLife('Too slow! That one got away. โฑ๏ธ')) {
        setTimeout(function () {
            if (running) {
                nextCard();
            }
        }, 450);
    }
}

function loop(time) {
    if (!running) {
        return;
    }
    var dt = Math.min(0.05, (time - lastTime) / 1000);
    lastTime = time;
    if (deciding) {
        timeLeft -= dt;
        var ratio = Math.max(0, timeLeft / timeTotal);
        dealTimerFill.style.width = (ratio * 100) + '%';
        if (ratio < 0.3) {
            dealTimerFill.style.background = '#c62828';
        }
        if (timeLeft <= 0) {
            timeout();
        }
    }
    animationId = requestAnimationFrame(loop);
}

function startGame() {
    reset();
    running = true;
    dealStartButton.disabled = true;
    setShareButtons('none');
    dealMessage.textContent = 'Sort each offer before the timer runs out!';
    nextCard();
    lastTime = performance.now();
    animationId = requestAnimationFrame(loop);
}

function endGame() {
    running = false;
    deciding = false;
    cancelAnimationFrame(animationId);
    dealTimerFill.style.width = '0%';
    if (score > best) {
        best = score;
        dealBestDisplay.textContent = best;
        try {
            localStorage.setItem('dealSortBest', String(best));
        } catch (e) {
            // ignore storage errors
        }
        dealMessage.textContent = 'Sale over! New best: ' + score + ' sorted! ๐Ÿ†';
    } else {
        dealMessage.textContent = 'Sale over! You sorted ' + score + ' offers. ๐Ÿ›’';
    }
    dealEmoji.textContent = '๐Ÿ';
    dealText.textContent = 'Final score: ' + score;
    dealStartButton.disabled = false;
    dealStartButton.textContent = 'Play again';
    setShareButtons('flex');
}

dealScamBtn.addEventListener('click', function () {
    decide(false);
});
dealRealBtn.addEventListener('click', function () {
    decide(true);
});

dealCard.addEventListener('touchstart', function (e) {
    dragging = true;
    dragStartX = e.touches[0].clientX;
}, { passive: true });
dealCard.addEventListener('touchend', function (e) {
    if (!dragging) {
        return;
    }
    dragging = false;
    var dx = e.changedTouches[0].clientX - dragStartX;
    if (Math.abs(dx) > 40) {
        decide(dx > 0);
    }
});
dealCard.addEventListener('mousedown', function (e) {
    dragging = true;
    dragStartX = e.clientX;
});
document.addEventListener('mouseup', function (e) {
    if (!dragging) {
        return;
    }
    dragging = false;
    var dx = e.clientX - dragStartX;
    if (Math.abs(dx) > 40) {
        decide(dx > 0);
    }
});
document.addEventListener('keydown', function (e) {
    if (!running) {
        return;
    }
    if (e.key === 'ArrowLeft') {
        e.preventDefault();
        decide(false);
    } else if (e.key === 'ArrowRight') {
        e.preventDefault();
        decide(true);
    }
});

dealStartButton.addEventListener('click', startGame);

reset();