diff --git a/hamster/images/heart.jpg b/hamster/images/heart.jpg new file mode 100644 index 0000000..75f010f Binary files /dev/null and b/hamster/images/heart.jpg differ diff --git a/hamster/images/sorry.jpg b/hamster/images/sorry.jpg new file mode 100644 index 0000000..f4650e9 Binary files /dev/null and b/hamster/images/sorry.jpg differ diff --git a/hamster/index.html b/hamster/index.html new file mode 100644 index 0000000..592d972 --- /dev/null +++ b/hamster/index.html @@ -0,0 +1,171 @@ + + + + + + For My Favorite Person 💖 + + + + + + + +
+ + +
+
+ HammiePlayer.exe +
+ + +
+
+
+ + ♪ Lo-Fi Radio (Click Play!) ♪ +
+
+ + + +
+
+

⭐ FOR MY HAMMIE ⭐

+

Click the cards to see some reasons why you are so special to me...

+
+ + +
+
+ + +
+
+
+
⭐⭐⭐
+ Hammie Heart +

Reason #1

+

[ Click to Flip! ]

+
+
+

The Little Things 🌸

+

I love how you remember the tiny details, like the little things we talk about, or how your face lights up whenever you see a cute dog. Your thoughtfulness makes every normal day feel special.

+
🌸 🌸 🌸
+
+
+
+ + +
+
+
+
⭐⭐⭐
+ Hammie Heart +

Reason #2

+

[ Click to Flip! ]

+
+
+

Your Warmth 🤗

+

Whenever I have a bad day, just sitting next to you makes all the stress melt away. You have this quiet warmth that makes me feel safer than anywhere else in the world.

+
✨ ✨ ✨
+
+
+
+ + +
+
+
+
⭐⭐⭐
+ Hammie Heart +

Reason #3

+

[ Click to Flip! ]

+
+
+

Our Laughs 💬

+

Nobody makes me laugh the way you do. I cherish all our dumb inside jokes and late-night talks that probably don't make sense to anyone else, but mean everything to me.

+
🌻 🌻 🌻
+
+
+
+ + +
+
+
+
⭐⭐⭐
+ Hammie Heart +

Reason #4

+

[ Click to Flip! ]

+
+
+

Your Kindness 🧸

+

You support me through everything and care so deeply for everyone around you. Your heart is so big, and seeing how you treat others makes me want to be a better person every day.

+
💖 💖 💖
+
+
+
+ + +
+
+
+
🩹🩹🩹
+ Hammie Sorry +

For You...

+

[ Click to Open ]

+
+
+

A Note From Me 💌

+

I know things have been really tough between us lately and we've been fighting. I wanted to tell you how truly sorry I am...

+ +
+
+
+ +
+
+ + +
+ +
+ 💖 + 💖 + 💖 + 💖 + 💖 +
+ +
+
+ + +
+
+
+ sorry_letter.txt + +
+
+
+ Hammie Sorry +

Hi baby,

+ +

I hate that I've been neglecting you and not being a good boyfriend lately, and the thought of losing you makes my heart ache. I know we might not make it, but even if this is the end and you decide not to get back with me, I wanted to build one last cute thing for you to show you how much you mean to me.

+ +

I know I haven't been perfect, and I've made mistakes that hurt you. I want to apologize from the bottom of my heart. You mean the absolute world to me. I made this little website just to remind you of the happy memories we've built.

+ +

Whatever happens next, please know that my love for you has always been real, and I cherish every second I got to spend with you.

+ +

Love always,
Sho

+
+
+
+
+ + + + diff --git a/hamster/script.js b/hamster/script.js new file mode 100644 index 0000000..718c208 --- /dev/null +++ b/hamster/script.js @@ -0,0 +1,176 @@ +// ============================================================================== +// State Variables +// ============================================================================== +let currentIndex = 1; +const totalCards = 5; + +// ============================================================================== +// DOM Element Selectors +// ============================================================================== +const cards = document.querySelectorAll('.flip-card'); +const hearts = document.querySelectorAll('.progress-indicator .heart'); +const prevBtn = document.getElementById('prev-btn'); +const nextBtn = document.getElementById('next-btn'); +const sparklesBg = document.getElementById('sparkles-bg'); +const letterModal = document.getElementById('letter-modal'); + +// ============================================================================== +// Card Deck Navigation Logic +// ============================================================================== +function updateDeck() { + // Update card classes + cards.forEach(card => { + const index = parseInt(card.getAttribute('data-index')); + if (index === currentIndex) { + card.classList.add('active'); + } else { + card.classList.remove('active'); + card.classList.remove('flipped'); // Reset flip state when moving away + } + }); + + // Update heart progress indicator + hearts.forEach((heart, idx) => { + if (idx < currentIndex) { + heart.classList.add('active'); + } else { + heart.classList.remove('active'); + } + }); + + // Manage buttons disabled states + prevBtn.disabled = (currentIndex === 1); + nextBtn.disabled = (currentIndex === totalCards); +} + +// Event Listeners for Nav Buttons +prevBtn.addEventListener('click', () => { + if (currentIndex > 1) { + currentIndex--; + updateDeck(); + spawnSparklesCluster(window.innerWidth / 2, window.innerHeight / 2, 8); + } +}); + +nextBtn.addEventListener('click', () => { + if (currentIndex < totalCards) { + currentIndex++; + updateDeck(); + spawnSparklesCluster(window.innerWidth / 2, window.innerHeight / 2, 8); + } +}); + +// ============================================================================== +// Card Flip Interaction +// ============================================================================== +cards.forEach(card => { + card.addEventListener('click', (e) => { + // Prevent flipping if they clicked the "Open Letter" button inside the card + if (e.target.classList.contains('open-letter-btn')) { + return; + } + card.classList.toggle('flipped'); + spawnSparklesCluster(e.clientX, e.clientY, 6); + }); +}); + +// ============================================================================== +// Sorry Letter Modal Control +// ============================================================================== +function openLetter() { + letterModal.classList.add('open'); + spawnSparklesCluster(window.innerWidth / 2, window.innerHeight / 2, 20); +} + +function closeLetter() { + letterModal.classList.remove('open'); +} + +// ============================================================================== +// Y2K Interactive Sparkle Particles +// ============================================================================== +const sparkleIcons = ['⭐', '✨', '🌸', '💖', '🌼', '🩹']; + +function spawnSparkle(x, y) { + const particle = document.createElement('span'); + particle.className = 'sparkle-particle'; + + // Choose a random icon + particle.innerText = sparkleIcons[Math.floor(Math.random() * sparkleIcons.length)]; + + // Randomize starting location slightly + const offsetX = (Math.random() - 0.5) * 20; + const offsetY = (Math.random() - 0.5) * 20; + particle.style.left = `${x + offsetX}px`; + particle.style.top = `${y + offsetY}px`; + + // Randomize trajectory direction + const travelX = (Math.random() - 0.5) * 100; + particle.style.setProperty('--travel-x', `${travelX}px`); + + sparklesBg.appendChild(particle); + + // Remove element after animation finishes + setTimeout(() => { + particle.remove(); + }, 1500); +} + +function spawnSparklesCluster(x, y, count) { + for (let i = 0; i < count; i++) { + setTimeout(() => spawnSparkle(x, y), i * 30); + } +} + +// Spawn sparkles on click +window.addEventListener('click', (e) => { + // Only spawn if they didn't click a button (which handles its own bursts) + if (!e.target.closest('button')) { + spawnSparkle(e.clientX, e.clientY); + } +}); + +// Initialize deck state on load +updateDeck(); + +// ============================================================================== +// Audio Player Controls +// ============================================================================== +const bgMusic = document.getElementById('bg-music'); +const playPauseBtn = document.getElementById('play-pause-btn'); +const musicStatus = document.getElementById('music-status'); + +// Set volume slightly lower for pleasant background level +bgMusic.volume = 0.35; + +// Function to start music playback +function startMusic() { + bgMusic.play().then(() => { + playPauseBtn.innerText = '⏸'; + musicStatus.innerText = '♪ Playing: Lo-Fi 24/7 Live Radio ♪'; + }).catch(err => { + console.error("Playback blocked:", err); + musicStatus.innerText = '♪ Audio blocked - Click Play! ♪'; + }); +} + +// 1. Play/Pause toggle button listener +playPauseBtn.addEventListener('click', (e) => { + e.stopPropagation(); // prevent document click listener from double-triggering + if (bgMusic.paused) { + startMusic(); + } else { + bgMusic.pause(); + playPauseBtn.innerText = '▶'; + musicStatus.innerText = '♪ Lo-Fi Radio (Paused) ♪'; + } +}); + +// 2. Autoplay unlock: Start music automatically on the very first click anywhere on the page +window.addEventListener('click', () => { + if (bgMusic.paused) { + startMusic(); + } +}, { once: true }); // triggers only once + + diff --git a/hamster/style.css b/hamster/style.css new file mode 100644 index 0000000..016071c --- /dev/null +++ b/hamster/style.css @@ -0,0 +1,511 @@ +/* ============================================================================== + Y2K Retro-Kawaii Theme Variables + ============================================================================== */ +:root { + --pink-dark: #ff477e; + --pink-medium: #ff85a2; + --pink-light: #fbb1bd; + --pink-bg: #fff0f3; + --yellow-accent: #ffeb99; + --text-color: #5c1d30; + --border-width: 3px; + --shadow-offset: 4px; + --font-heading: 'Press Start 2P', cursive; + --font-body: 'Fredoka', sans-serif; +} + +/* ============================================================================== + Global Styles + ============================================================================== */ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + background-color: var(--pink-bg); + /* Y2K Grid Pattern */ + background-image: + linear-gradient(to right, rgba(255, 133, 162, 0.1) 1px, transparent 1px), + linear-gradient(to bottom, rgba(255, 133, 162, 0.1) 1px, transparent 1px); + background-size: 25px 25px; + font-family: var(--font-body); + color: var(--text-color); + min-height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + overflow-x: hidden; + padding: 20px; +} + +/* ============================================================================== + Floating Sparkles & Particle Effects + ============================================================================== */ +.sparkles-container { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 0; +} + +.sparkle-particle { + position: absolute; + font-size: 20px; + pointer-events: none; + animation: floatUp 1.5s ease-out forwards; +} + +@keyframes floatUp { + 0% { + transform: translateY(0) scale(1) rotate(0deg); + opacity: 1; + } + 100% { + transform: translateY(-80px) scale(0) rotate(180deg); + opacity: 0; + } +} + +/* ============================================================================== + Retro Windows & Widgets + ============================================================================== */ +.retro-widget { + background-color: white; + border: var(--border-width) solid var(--text-color); + border-radius: 12px; + box-shadow: var(--shadow-offset) var(--shadow-offset) 0px var(--text-color); + overflow: hidden; + z-index: 10; +} + +.widget-header { + background-color: var(--pink-dark); + color: white; + border-bottom: var(--border-width) solid var(--text-color); + padding: 8px 12px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.widget-title { + font-family: var(--font-heading); + font-size: 8px; + letter-spacing: 1px; +} + +.widget-controls { + display: flex; + gap: 6px; +} + +.widget-controls .dot { + width: 10px; + height: 10px; + border: 1.5px solid var(--text-color); + border-radius: 50%; + background-color: var(--yellow-accent); +} + +.widget-body { + padding: 10px; + display: flex; + align-items: center; + gap: 10px; + font-weight: 600; +} + +.music-widget { + position: fixed; + top: 20px; + right: 20px; + width: 250px; + font-size: 14px; +} + +.music-widget marquee { + color: var(--pink-dark); + font-weight: bold; +} + +/* ============================================================================== + Main Layout & Header + ============================================================================== */ +.main-container { + max-width: 600px; + width: 100%; + display: flex; + flex-direction: column; + align-items: center; + z-index: 5; +} + +.retro-header { + text-align: center; + margin-bottom: 25px; +} + +.retro-header h1 { + font-family: var(--font-heading); + font-size: 20px; + color: var(--pink-dark); + text-shadow: 3px 3px 0px var(--yellow-accent), 5px 5px 0px var(--text-color); + margin-bottom: 12px; + animation: heartbeat 2s infinite; +} + +@keyframes heartbeat { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.03); } +} + +.subtitle { + font-size: 15px; + font-weight: 600; + background-color: white; + padding: 6px 12px; + border: 2px solid var(--text-color); + border-radius: 20px; + box-shadow: 2px 2px 0px var(--text-color); +} + +/* ============================================================================== + Interactive Card Deck (3D Flip) + ============================================================================== */ +.card-deck-wrapper { + width: 320px; + height: 420px; + perspective: 1000px; + margin-bottom: 25px; +} + +.card-deck { + width: 100%; + height: 100%; + position: relative; +} + +.flip-card { + position: absolute; + width: 100%; + height: 100%; + cursor: pointer; + visibility: hidden; + opacity: 0; + transform: scale(0.9) translateY(20px); + transition: transform 0.4s ease, opacity 0.4s ease, visibility 0.4s ease; +} + +.flip-card.active { + visibility: visible; + opacity: 1; + transform: scale(1) translateY(0); +} + +.flip-card-inner { + position: relative; + width: 100%; + height: 100%; + text-align: center; + transition: transform 0.8s cubic-bezier(0.175, 0.885, 0.32, 1.275); + transform-style: preserve-3d; +} + +.flip-card.flipped .flip-card-inner { + transform: rotateY(180deg); +} + +.flip-card-front, .flip-card-back { + position: absolute; + width: 100%; + height: 100%; + backface-visibility: hidden; + border: var(--border-width) solid var(--text-color); + border-radius: 24px; + background-color: white; + box-shadow: var(--shadow-offset) var(--shadow-offset) 0px var(--text-color); + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 25px; + overflow: hidden; +} + +.flip-card-front { + background-image: radial-gradient(var(--pink-light) 10%, transparent 11%); + background-size: 15px 15px; +} + +.flip-card-back { + background-color: #fff9fa; + transform: rotateY(180deg); + border-color: var(--pink-dark); + box-shadow: var(--shadow-offset) var(--shadow-offset) 0px var(--pink-dark); +} + +.card-decor-top, .card-decor-bottom { + font-size: 18px; + letter-spacing: 5px; +} + +.card-hamster { + width: 160px; + height: 160px; + object-fit: cover; + border: var(--border-width) solid var(--text-color); + border-radius: 50%; + background-color: white; + padding: 5px; + box-shadow: 3px 3px 0px var(--text-color); +} + +.card-title-text { + font-family: var(--font-heading); + font-size: 14px; + color: var(--pink-dark); + background-color: var(--yellow-accent); + padding: 6px 16px; + border: 2px solid var(--text-color); + border-radius: 12px; + transform: rotate(-2deg); +} + +.click-hint { + font-size: 12px; + font-weight: 700; + letter-spacing: 1px; + color: var(--pink-dark); + animation: flash 1.5s infinite; +} + +@keyframes flash { + 0%, 100% { opacity: 0.6; } + 50% { opacity: 1; } +} + +/* Back Face Card Styles */ +.flip-card-back h2 { + font-family: var(--font-body); + font-weight: 800; + font-size: 22px; + color: var(--pink-dark); + border-bottom: 2px dashed var(--pink-light); + padding-bottom: 6px; + width: 100%; +} + +.card-desc { + font-size: 15px; + line-height: 1.6; + font-weight: 600; + text-align: center; + color: var(--text-color); + margin: 15px 0; +} + +/* ============================================================================== + Button Physics & Controls + ============================================================================== */ +.retro-btn { + background-color: var(--yellow-accent); + color: var(--text-color); + font-family: var(--font-body); + font-weight: 700; + font-size: 16px; + padding: 10px 20px; + border: var(--border-width) solid var(--text-color); + border-radius: 12px; + box-shadow: 3px 3px 0px var(--text-color); + cursor: pointer; + outline: none; + transition: transform 0.1s ease, box-shadow 0.1s ease; +} + +.retro-btn:active { + transform: translate(2px, 2px); + box-shadow: 1px 1px 0px var(--text-color); +} + +.retro-btn:disabled { + background-color: #e0e0e0; + color: #999; + box-shadow: none; + cursor: not-allowed; + transform: none; +} + +.open-letter-btn { + background-color: var(--pink-medium); + color: white; + border-color: var(--pink-dark); + box-shadow: 3px 3px 0px var(--pink-dark); +} + +.open-letter-btn:active { + transform: translate(2px, 2px); + box-shadow: 1px 1px 0px var(--pink-dark); +} + +/* Deck Control Panel */ +.deck-controls { + display: flex; + align-items: center; + gap: 20px; +} + +.nav-btn { + width: 100px; +} + +.progress-indicator { + display: flex; + gap: 8px; +} + +.progress-indicator .heart { + font-size: 18px; + filter: grayscale(1); + opacity: 0.3; + transition: filter 0.3s ease, opacity 0.3s ease, transform 0.3s ease; +} + +.progress-indicator .heart.active { + filter: none; + opacity: 1; + transform: scale(1.2); +} + +/* ============================================================================== + Retro Letter Overlay / Modal (Lined Paper Vibe) + ============================================================================== */ +.letter-overlay { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background-color: rgba(254, 197, 217, 0.6); + backdrop-filter: blur(5px); + display: flex; + align-items: center; + justify-content: center; + z-index: 100; + opacity: 0; + pointer-events: none; + transition: opacity 0.4s ease; +} + +.letter-overlay.open { + opacity: 1; + pointer-events: auto; +} + +.letter-window { + width: 90%; + max-width: 500px; + height: 80vh; + max-height: 600px; + display: flex; + flex-direction: column; +} + +.letter-window .close-btn { + background: none; + border: none; + color: white; + font-family: var(--font-heading); + font-size: 14px; + cursor: pointer; + padding: 0 4px; +} + +.letter-body { + flex-grow: 1; + background-color: white; + overflow-y: auto; + padding: 15px; +} + +.letter-paper { + background-color: #fffdec; + border: 1px solid #e0dbb8; + border-radius: 8px; + padding: 25px; + min-height: 100%; + box-shadow: inset 0 0 20px rgba(0, 0, 0, 0.05); + position: relative; + /* Red margins and blue lined-paper styling */ + background-image: + linear-gradient(rgba(173, 216, 230, 0.5) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 0, 0, 0.15) 1px, transparent 1px); + background-size: 100% 24px, 100% 100%; + background-position: 0 10px, 40px 0; + padding-left: 55px; /* offset for red margin line */ +} + +.letter-paper h3 { + font-weight: 800; + font-size: 20px; + margin-bottom: 20px; + color: var(--pink-dark); +} + +.letter-paper p { + font-size: 15px; + line-height: 24px; /* Align text to the blue notebook lines! */ + font-weight: 600; + margin-bottom: 24px; + color: var(--text-color); +} + +.letter-hamster { + width: 90px; + height: 90px; + border: 2px solid var(--text-color); + border-radius: 50%; + background-color: white; + padding: 3px; + float: right; + margin-left: 15px; + margin-bottom: 15px; + box-shadow: 2px 2px 0px var(--text-color); +} + +.letter-signature { + text-align: right; + font-weight: bold; + font-size: 16px; + margin-top: 30px; +} + +/* Play Button Styling */ +.music-play-btn { + background-color: var(--yellow-accent); + color: var(--text-color); + border: 2px solid var(--text-color); + border-radius: 50%; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + font-size: 11px; + cursor: pointer; + box-shadow: 2px 2px 0px var(--text-color); + outline: none; + transition: transform 0.1s, box-shadow 0.1s; + flex-shrink: 0; +} + +.music-play-btn:active { + transform: translate(1px, 1px); + box-shadow: 1px 1px 0px var(--text-color); +} + diff --git a/portal/index.html b/portal/index.html new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/portal/index.html @@ -0,0 +1 @@ +Hello World!