Skip to content
Merged
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
226 changes: 2 additions & 224 deletions web-app/js/projects.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ο»Ώ// Project Registry
// Project Registry
// Each project's HTML and logic lives in its own file under js/projects/


Expand Down Expand Up @@ -339,229 +339,7 @@ function initDiceRolling() {
// ============================================
// COIN FLIP
// ============================================
function getCoinFlipHTML() {
return `
<div class="project-content">
<h2>πŸͺ™ Coin Flip</h2>
<div class="coin-container">
<div class="coin-scene">
<div class="coin" id="coin">
<div class="coin-face heads">πŸ‘‘</div>
<div class="coin-edge"></div>
<div class="coin-face tails">πŸ¦…</div>
</div>
<div class="coin-shadow"></div>
</div>

<div class="coin-result" id="coinResult">Click to Flip!</div>

<button class="btn-flip" id="flipCoin">Flip Coin</button>

<div class="coin-stats">
<div class="stat-item">
<span>Heads:</span>
<span id="headsCount">0</span>
</div>
<div class="stat-item">
<span>Tails:</span>
<span id="tailsCount">0</span>
</div>
</div>
</div>
</div>

<style>
.coin-container {
text-align: center;
padding: 3rem 2rem;
}

.coin-scene {
position: relative;
width: 170px;
height: 205px;
perspective: 900px;
margin: 1rem auto 2rem;
transform-origin: center bottom;
}

.coin {
--flip-x: 0deg;
--flip-y: 0deg;
width: 170px;
height: 170px;
position: relative;
transform-style: preserve-3d;
transform: rotateX(var(--flip-x)) rotateY(var(--flip-y));
transition: transform 1.6s cubic-bezier(0.2, 0.8, 0.15, 1);
}

.coin-face {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
align-items: center;
justify-content: center;
font-size: 6rem;
background: radial-gradient(circle at 35% 30%, #fff5a6, #ffd54f 45%, #e1a600 100%);
border-radius: 50%;
border: 5px solid #d79e00;
box-shadow: inset -6px -8px 10px rgba(0, 0, 0, 0.22), inset 6px 8px 12px rgba(255, 255, 255, 0.45), 0 12px 25px rgba(0, 0, 0, 0.35);
}

.heads { transform: translateZ(8px); }

.tails {
transform: rotateY(180deg) translateZ(8px);
}

.coin-edge {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
border: 8px solid #b8860b;
transform: translateZ(0);
box-shadow: inset 0 0 0 2px rgba(255, 232, 145, 0.35);
}

.coin-shadow {
position: absolute;
left: 50%;
bottom: 6px;
width: 110px;
height: 16px;
transform: translateX(-50%);
border-radius: 50%;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.08) 72%, transparent 100%);
}

.coin-scene.rolling .coin-shadow {
animation: coinShadowFloat 1.6s ease;
}

.coin-scene.impact {
animation: coinLanding 0.45s ease-out;
}

.coin-scene.impact .coin-shadow {
animation: coinShadowImpact 0.45s ease-out;
}

.coin-result {
font-size: 2rem;
font-weight: bold;
margin: 2rem 0;
min-height: 3rem;
}

.btn-flip {
background: linear-gradient(135deg, #ffd700, #ffed4e);
color: #333;
border: none;
padding: 1rem 3rem;
border-radius: 50px;
font-size: 1.3rem;
font-weight: bold;
cursor: pointer;
transition: var(--transition);
}

.btn-flip:hover {
transform: scale(1.05);
box-shadow: 0 5px 20px rgba(255, 215, 0, 0.4);
}

.coin-stats {
display: flex;
gap: 3rem;
justify-content: center;
margin-top: 2rem;
font-size: 1.2rem;
}

.stat-item {
display: flex;
gap: 1rem;
align-items: center;
}

@keyframes coinLanding {
0% { transform: translateY(-12px); }
55% { transform: translateY(5px); }
82% { transform: translateY(-2px); }
100% { transform: translateY(0); }
}

@keyframes coinShadowFloat {
0%, 100% { transform: translateX(-50%) scale(1); opacity: 0.4; }
45% { transform: translateX(-50%) scale(0.68); opacity: 0.2; }
}

@keyframes coinShadowImpact {
0% { transform: translateX(-50%) scale(0.72); opacity: 0.2; }
55% { transform: translateX(-50%) scale(1.12); opacity: 0.44; }
100% { transform: translateX(-50%) scale(1); opacity: 0.4; }
}
</style>
`;
}

function initCoinFlip() {
const coin = document.getElementById('coin');
const coinScene = coin.closest('.coin-scene');
const flipBtn = document.getElementById('flipCoin');
const result = document.getElementById('coinResult');
const headsCountEl = document.getElementById('headsCount');
const tailsCountEl = document.getElementById('tailsCount');
let headsCount = 0;
let tailsCount = 0;
let spinCount = 0;

function setCoinFace(isHeads, seed) {
const targetY = isHeads ? 0 : 180;
const flipX = 360 * (4 + (seed % 3));
const flipY = 360 * (3 + (seed % 2)) + targetY;
coin.style.setProperty('--flip-x', `${flipX}deg`);
coin.style.setProperty('--flip-y', `${flipY}deg`);
}

function triggerCoinLanding() {
coinScene.classList.remove('impact');
void coinScene.offsetWidth;
coinScene.classList.add('impact');
setTimeout(() => {
coinScene.classList.remove('impact');
}, 460);
}

flipBtn.addEventListener('click', () => {
flipBtn.disabled = true;
result.textContent = 'Flipping...';
coinScene.classList.add('rolling');

const isHeads = Math.random() < 0.5;
spinCount += 1;
setCoinFace(isHeads, spinCount);

setTimeout(() => {
coinScene.classList.remove('rolling');
triggerCoinLanding();
if (isHeads) {
result.textContent = 'πŸ‘‘ Heads!';
headsCount++;
headsCountEl.textContent = headsCount;
} else {
result.textContent = 'πŸ¦… Tails!';
tailsCount++;
tailsCountEl.textContent = tailsCount;
}
flipBtn.disabled = false;
}, 1600);
});
}
// (Coin Flip HTML & Logic loaded from modular file js/projects/coin-flip.js)

// Continue with more projects in next message...
// Additional Project Implementations
Expand Down
Loading
Loading