Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
169 changes: 169 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ISSUE NO. 001: THE VALENTINE PROTOCOL</title>

<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>

<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Anton&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">

<!-- GSAP -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>

<!-- Canvas Confetti -->
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>

<!-- Custom Styles Link -->
<link rel="stylesheet" href="style.css">

<script>
tailwind.config = {
theme: {
extend: {
colors: {
'obsidian': '#000000',
'stark-white': '#FFFFFF',
'electric-pink': '#FF0055',
},
fontFamily: {
'header': ['Anton', 'sans-serif'],
'mono': ['JetBrains Mono', 'monospace'],
}
}
}
}
</script>
</head>
<body class="bg-electric-pink min-h-screen overflow-hidden flex items-center justify-center cursor-crosshair">

<!-- Background Grid -->
<div class="fixed inset-0 grid-background pointer-events-none z-0"></div>

<!-- Floating 3D Elements Container -->
<div id="floating-elements" class="fixed inset-0 pointer-events-none z-10 overflow-hidden">
<!-- Elements will be added here via JS or simple CSS elements -->
<div class="floating-icon absolute top-10 left-10 text-4xl opacity-50">❤️</div>
<div class="floating-icon absolute bottom-20 right-20 text-4xl opacity-50">⚡</div>
<div class="floating-icon absolute top-1/2 left-10 text-4xl opacity-50">💾</div>
<div class="floating-icon absolute top-20 right-1/4 text-4xl opacity-50">🔒</div>
</div>

<!-- Main Container -->
<main class="relative z-20 w-full max-w-4xl p-4">

<!-- Product Box -->
<div id="product-box" class="bg-stark-white border-4 border-obsidian shadow-[8px_8px_0px_#000] p-6 md:p-12 relative transform transition-transform duration-300">

<!-- Technical Specs Header -->
<div class="flex justify-between items-start mb-8 font-mono text-sm md:text-base border-b-2 border-obsidian pb-4">
<div class="flex flex-col">
<span class="font-bold">ISSUE NO. 001</span>
<span>THE VALENTINE PROTOCOL</span>
</div>
<div class="text-right flex flex-col items-end">
<div id="countdown" class="font-bold text-electric-pink">00:00:00</div>
<span class="text-xs">TIME REMAINING TO ACCEPT</span>
</div>
</div>

<!-- Main Content -->
<div class="text-center space-y-8">
<h2 class="font-header text-2xl md:text-3xl tracking-wide text-obsidian/80">
BRIAN x [HER NAME] // OFFICIAL COLLABORATION
</h2>

<h1 class="font-header text-6xl md:text-8xl leading-none text-obsidian transform hover:scale-105 transition-transform duration-300">
WILL YOU BE<br>MY VALENTINE?
</h1>

<!-- Interaction Area -->
<div class="flex flex-col md:flex-row items-center justify-center gap-8 mt-12 relative h-32 md:h-40">

<!-- YES Button -->
<button id="yes-btn" class="w-full md:w-3/5 bg-[#00FF00] hover:bg-[#00DD00] text-obsidian font-header text-4xl py-4 border-4 border-obsidian shadow-[4px_4px_0px_#000] active:translate-y-1 active:shadow-none transition-all duration-100 uppercase tracking-wider relative overflow-hidden group">
<span class="relative z-10">YES [ACCEPT]</span>
<div class="absolute inset-0 bg-white/20 translate-y-full group-hover:translate-y-0 transition-transform duration-300"></div>
</button>

<!-- NO Button (The Unclickable) -->
<button id="no-btn" class="bg-stark-white text-obsidian font-mono font-bold text-xl py-4 px-8 border-4 border-obsidian shadow-[4px_4px_0px_#000] uppercase tracking-wider absolute md:static">
NO
</button>

</div>
</div>

<!-- Footer / Terminal -->
<div class="mt-12 border-t-2 border-obsidian pt-4">
<div class="bg-obsidian text-[#00FF00] font-mono text-xs p-4 h-32 overflow-y-auto shadow-inner" id="terminal">
<div>> Initializing Valentine Protocol v1.0...</div>
<div>> Target acquired: [HER NAME]</div>
<div>> Analysis: 100% Compatible</div>
</div>
</div>

<!-- Absolute decorative corner markers -->
<div class="absolute top-0 left-0 w-4 h-4 border-t-4 border-l-4 border-obsidian -mt-1 -ml-1"></div>
<div class="absolute top-0 right-0 w-4 h-4 border-t-4 border-r-4 border-obsidian -mt-1 -mr-1"></div>
<div class="absolute bottom-0 left-0 w-4 h-4 border-b-4 border-l-4 border-obsidian -mb-1 -ml-1"></div>
<div class="absolute bottom-0 right-0 w-4 h-4 border-b-4 border-r-4 border-obsidian -mb-1 -mr-1"></div>

</div>

<!-- Voucher Overlay (Hidden by default) -->
<div id="voucher-overlay" class="fixed inset-0 z-50 flex items-center justify-center bg-obsidian/90 hidden opacity-0 transition-opacity duration-500">
<div class="bg-stark-white border-4 border-electric-pink p-8 max-w-2xl w-full mx-4 shadow-[16px_16px_0px_#FF0055] transform rotate-1 relative">

<!-- StockX Style Tag -->
<div class="absolute -right-4 -top-4 bg-[#00FF00] text-obsidian font-bold font-mono text-xs px-2 py-1 border-2 border-obsidian shadow-[2px_2px_0px_#000] rotate-12">
VERIFIED AUTHENTIC
</div>

<div class="border-b-4 border-dashed border-obsidian pb-6 mb-6 text-center">
<h2 class="font-header text-6xl text-obsidian mb-2">CONFIRMED</h2>
<p class="font-mono text-sm text-gray-600">TRANSACTION ID: #L0V3-2024-FOREVER</p>
</div>

<div class="space-y-4 font-mono">
<div class="flex justify-between border-b border-gray-300 pb-2">
<span class="font-bold">ITEM:</span>
<span>VALENTINE'S DINNER DATE</span>
</div>
<div class="flex justify-between border-b border-gray-300 pb-2">
<span class="font-bold">LOCATION:</span>
<span>THAT TEMPE RESTAURANT</span>
</div>
<div class="flex justify-between border-b border-gray-300 pb-2">
<span class="font-bold">DATE:</span>
<span>FEB 14, 2024</span>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Voucher displays hardcoded date from 2 years ago

Low Severity

The voucher confirmation screen displays a hardcoded date of "FEB 14, 2024" which is 2 years in the past. Unlike the obvious [HER NAME] placeholders, this date appears legitimate but is incorrect for the intended use as a Valentine's Day dinner invitation. The recipient would see a past date, making the invitation confusing or appearing like an old/expired voucher.

Fix in Cursor Fix in Web

</div>
<div class="flex justify-between border-b border-gray-300 pb-2">
<span class="font-bold">PRICE:</span>
<span>$0.00 (MY TREAT)</span>
</div>
</div>

<div class="mt-8 text-center">
<p class="font-header text-2xl text-electric-pink">SEE YOU THERE</p>
</div>

<!-- Barcode -->
<div class="mt-6 h-12 bg-obsidian w-full relative overflow-hidden">
<div class="absolute inset-0 flex justify-between items-center px-2 text-white font-mono text-xs tracking-[0.5em]">
||| || ||| | |||| ||| | || ||
</div>
</div>
</div>
</div>

</main>

<script src="script.js"></script>
</body>
</html>
209 changes: 209 additions & 0 deletions script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
document.addEventListener('DOMContentLoaded', () => {
// --- Elements ---
const noBtn = document.getElementById('no-btn');
const yesBtn = document.getElementById('yes-btn');
const productBox = document.getElementById('product-box');
const terminal = document.getElementById('terminal');
const countdownEl = document.getElementById('countdown');
const floatingElements = document.querySelectorAll('.floating-icon');
const voucherOverlay = document.getElementById('voucher-overlay');

// --- GSAP Entrance ---
gsap.from(productBox, {
duration: 1.5,
y: 100,
opacity: 0,
ease: "power4.out",
delay: 0.5
});

gsap.from("#product-box > *", {
duration: 1,
y: 20,
opacity: 0,
stagger: 0.1,
ease: "power2.out",
delay: 1
});

// --- Countdown Timer ---
// Set a random time between 2 to 5 hours from now
let remainingTime = (Math.floor(Math.random() * 3) + 2) * 60 * 60;

function updateCountdown() {
if (remainingTime <= 0) {
remainingTime = (Math.floor(Math.random() * 3) + 2) * 60 * 60; // Reset
}
remainingTime--;

const h = Math.floor(remainingTime / 3600);
const m = Math.floor((remainingTime % 3600) / 60);
const s = remainingTime % 60;

countdownEl.innerText = `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`;
}

setInterval(updateCountdown, 1000);
updateCountdown();

// --- Terminal Logic ---
const logMessages = [
"Analyzing relationship compatibility... 100%",
"Stress testing 'Yes' response... SUCCESS.",
"Error: 'No' option corrupted by Love.exe.",
"Optimizing romance algorithms...",
"Checking shipping availability... IMMEDIATE.",
"Heartbeat detected: ELEVATED.",
"Synchronizing emotional state...",
];

let logIndex = 0;

function addLog(message) {
const div = document.createElement('div');
div.innerText = `> ${message}`;
terminal.appendChild(div);
terminal.scrollTop = terminal.scrollHeight;
}

function runTerminal() {
if (logIndex < logMessages.length) {
addLog(logMessages[logIndex]);
logIndex++;
setTimeout(runTerminal, Math.random() * 2000 + 1000);
}
}

setTimeout(runTerminal, 2000);

// --- Evasive 'No' Button Logic ---
let noBtnScale = 1;
const noBtnTexts = ["NO", "ARE YOU SURE?", "REALLY?", "ERROR 404", "TRY AGAIN", "NOPE", "TOO SLOW"];
let textIndex = 0;

document.addEventListener('mousemove', (e) => {
const rect = noBtn.getBoundingClientRect();
const btnX = rect.left + rect.width / 2;
const btnY = rect.top + rect.height / 2;

const dist = Math.hypot(e.clientX - btnX, e.clientY - btnY);

// If mouse is within 100px (as per prompt "The Logic" 50px vs "Solid Prompt" 100px, taking 100px for better effect)
if (dist < 100) {
teleportButton();
}
});

function teleportButton() {
// Teleport
const maxX = window.innerWidth - noBtn.offsetWidth - 50;
const maxY = window.innerHeight - noBtn.offsetHeight - 50;

const newX = Math.random() * maxX;
const newY = Math.random() * maxY;

noBtn.style.position = 'fixed';
noBtn.style.left = `${newX}px`;
noBtn.style.top = `${newY}px`;
noBtn.style.zIndex = '50'; // Ensure it's above other things but below overlay

// Shrink
noBtnScale *= 0.9;
noBtn.style.transform = `scale(${noBtnScale})`;

// Change Text
textIndex = (textIndex + 1) % noBtnTexts.length;
noBtn.innerText = noBtnTexts[textIndex];

// Add log
addLog(`Warning: 'No' evasion triggered. Integrity: ${Math.floor(noBtnScale * 100)}%`);
}

// Just in case they tab to it or touch screen
noBtn.addEventListener('mouseover', teleportButton);
noBtn.addEventListener('click', (e) => {
e.preventDefault();
teleportButton();
});

// --- 'Yes' Button Logic ---
yesBtn.addEventListener('click', () => {
// 1. Confetti
const duration = 3000;
const end = Date.now() + duration;

(function frame() {
confetti({
particleCount: 5,
angle: 60,
spread: 55,
origin: { x: 0 },
colors: ['#FF0055', '#FFFFFF', '#000000']
});
confetti({
particleCount: 5,
angle: 120,
spread: 55,
origin: { x: 1 },
colors: ['#FF0055', '#FFFFFF', '#000000']
});

if (Date.now() < end) {
requestAnimationFrame(frame);
}
}());

// 2. Visual Feedback
addLog("RESPONSE CONFIRMED: YES.");
addLog("Initiating celebratory sequence...");

// 3. Transition
gsap.to(productBox, {
duration: 0.5,
scale: 0.9,
opacity: 0,
ease: "power2.in",
onComplete: () => {
productBox.style.display = 'none';
voucherOverlay.classList.remove('hidden');
// Trigger reflow to enable transition
void voucherOverlay.offsetWidth;
voucherOverlay.classList.remove('opacity-0');

// Animate voucher in
gsap.from("#voucher-overlay > div", {
duration: 1,
y: 50,
rotation: -5,
opacity: 0,
ease: "elastic.out(1, 0.5)"
});
}
});
});

// --- Interactive Parallax for Floating Elements ---
document.addEventListener('mousemove', (e) => {
const mouseX = e.clientX / window.innerWidth;
const mouseY = e.clientY / window.innerHeight;

floatingElements.forEach((el, index) => {
const speed = (index + 1) * 10;
const x = (window.innerWidth / 2 - e.clientX) / speed;
const y = (window.innerHeight / 2 - e.clientY) / speed;

// Apply parallax effect to the product box instead of floating elements
// to avoid conflict with CSS keyframe animations.
const depth = 20;
const moveX = (e.clientX - window.innerWidth / 2) / window.innerWidth * depth;
const moveY = (e.clientY - window.innerHeight / 2) / window.innerHeight * depth;

gsap.to(productBox, {
duration: 1,
x: moveX,
y: moveY,
ease: "power1.out"
});
});
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parallax loop redundantly animates same element multiple times

Medium Severity

The parallax mousemove handler iterates over floatingElements but applies the animation to productBox on each iteration instead. This causes gsap.to(productBox, ...) to be called 4 times per mouse move with identical values. Additionally, the calculated variables mouseX, mouseY, speed, x, and y are never used. The loop appears to be leftover from a refactor (per the comment about avoiding CSS keyframe conflicts) and the animation call needs to be moved outside the loop.

Fix in Cursor Fix in Web

});
Loading