DNA-CASINO / dna-slot-machine.html
openfree's picture
Update dna-slot-machine.html
b5e52cd verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>๐Ÿงฌ DNA CASINO - Protein Synthesis Slot Machine</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Bebas+Neue&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
color: #fff;
font-family: 'Orbitron', monospace;
overflow-x: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
min-height: 100vh;
position: relative;
padding-top: 10px;
}
/* Casino carpet pattern background */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(circle at 20% 30%, #ff0066 0px, transparent 100px),
radial-gradient(circle at 80% 70%, #00ffff 0px, transparent 100px),
radial-gradient(circle at 50% 50%, #ffff00 0px, transparent 150px),
radial-gradient(circle at 30% 80%, #ff00ff 0px, transparent 80px),
radial-gradient(circle at 70% 20%, #00ff00 0px, transparent 90px);
opacity: 0.1;
z-index: 0;
animation: casinoLights 10s ease-in-out infinite;
}
@keyframes casinoLights {
0%, 100% { filter: hue-rotate(0deg) brightness(1); }
50% { filter: hue-rotate(180deg) brightness(1.5); }
}
/* Static TV noise overlay */
body::after {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
repeating-linear-gradient(
0deg,
transparent 0px,
rgba(255,255,255,0.08) 1px,
transparent 1px,
transparent 2px
),
repeating-linear-gradient(
90deg,
transparent 0px,
rgba(0,0,0,0.05) 1px,
transparent 1px,
transparent 2px
);
background-size: 2px 2px, 2px 2px;
pointer-events: none;
z-index: 1;
opacity: 0.8;
animation: staticNoise 0.1s steps(8) infinite;
}
@keyframes staticNoise {
0%, 100% { transform: translate(0, 0); }
50% { transform: translate(-1px, -1px); }
}
/* Main casino cabinet */
.machine-container {
background: linear-gradient(145deg, #1a0000, #330000);
border: 5px solid #ffd700;
border-radius: 30px;
padding: 20px;
padding-right: 120px;
box-shadow:
0 0 50px rgba(255, 215, 0, 0.5),
0 20px 40px rgba(0,0,0,0.8),
inset 0 0 30px rgba(255, 215, 0, 0.2);
width: 95vw;
max-width: 1400px;
position: relative;
z-index: 2;
}
/* Blinking lights around the machine */
.machine-container::before {
content: '';
position: absolute;
top: -10px;
left: -10px;
right: -10px;
bottom: -10px;
border-radius: 35px;
background: conic-gradient(
from 0deg,
#ff0000, #ff7700, #ffff00, #00ff00,
#0000ff, #ff00ff, #ff0000
);
z-index: -1;
animation: rotateGradient 3s linear infinite;
filter: blur(10px);
opacity: 0.7;
}
@keyframes rotateGradient {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Neon title */
.title {
text-align: center;
font-family: 'Bebas Neue', cursive;
font-size: 4rem;
margin-bottom: 10px;
position: relative;
text-transform: uppercase;
letter-spacing: 0.2em;
}
.title a {
text-decoration: none;
color: #fff;
text-shadow:
0 0 10px #ff00ff,
0 0 20px #ff00ff,
0 0 30px #ff00ff,
0 0 40px #ff00ff,
0 0 70px #ff00ff,
0 0 80px #ff00ff,
0 0 100px #ff00ff,
0 0 150px #ff00ff;
animation: neonFlicker 1.5s infinite alternate;
}
@keyframes neonFlicker {
0%, 100% { opacity: 1; }
33% { opacity: 0.8; }
66% { opacity: 0.9; }
}
.subtitle {
text-align: center;
font-size: 1.5rem;
color: #ffd700;
margin-bottom: 20px;
text-shadow: 0 0 10px #ffd700;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
/* Jackpot counter */
.jackpot-display {
background: #000;
border: 3px solid #ffd700;
border-radius: 10px;
padding: 10px 20px;
margin: 10px auto;
text-align: center;
max-width: 400px;
box-shadow:
inset 0 0 20px rgba(255, 215, 0, 0.3),
0 0 20px rgba(255, 215, 0, 0.5);
}
.jackpot-label {
font-size: 0.9rem;
color: #ffd700;
text-transform: uppercase;
letter-spacing: 2px;
}
.jackpot-value {
font-size: 2rem;
color: #00ff00;
font-weight: bold;
text-shadow: 0 0 10px #00ff00;
font-family: 'Orbitron', monospace;
}
/* Language selector */
.language-selector {
display: flex;
justify-content: center;
margin: 20px 0;
}
.language-toggle {
position: relative;
display: inline-block;
width: 120px;
height: 40px;
cursor: pointer;
}
.language-toggle input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(145deg, #444, #222);
border: 2px solid #ffd700;
border-radius: 40px;
transition: all 0.4s;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10px;
box-shadow:
0 5px 15px rgba(0,0,0,0.5),
inset 0 2px 5px rgba(255,255,255,0.1);
}
.lang-label {
font-size: 0.9rem;
font-weight: bold;
color: #888;
transition: all 0.3s;
z-index: 2;
}
.lang-label[data-lang="en"] {
margin-left: 5px;
}
.lang-label[data-lang="ko"] {
margin-right: 5px;
}
.toggle-slider::after {
content: '';
position: absolute;
height: 32px;
width: 50px;
left: 4px;
bottom: 4px;
background: linear-gradient(145deg, #00ff00, #00cc00);
border-radius: 32px;
transition: all 0.4s;
box-shadow:
0 2px 10px rgba(0,255,0,0.5),
inset 0 -2px 5px rgba(0,0,0,0.3);
}
.language-toggle input:checked + .toggle-slider::after {
transform: translateX(62px);
background: linear-gradient(145deg, #ff4444, #cc0000);
box-shadow:
0 2px 10px rgba(255,0,0,0.5),
inset 0 -2px 5px rgba(0,0,0,0.3);
}
.language-toggle input:checked + .toggle-slider .lang-label[data-lang="ko"] {
color: #fff;
}
.language-toggle input:not(:checked) + .toggle-slider .lang-label[data-lang="en"] {
color: #fff;
}
.cell-type-selector {
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
margin-bottom: 20px;
}
.radio-group {
display: flex;
gap: 30px;
}
.chip-label {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
cursor: pointer;
transition: all 0.3s ease;
}
.chip-label input[type="radio"] {
position: absolute;
opacity: 0;
}
.chip {
position: relative;
width: 100%;
height: 100%;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #ff4444, #cc0000);
box-shadow:
0 5px 15px rgba(0,0,0,0.5),
inset 0 0 20px rgba(0,0,0,0.3),
inset -3px -3px 10px rgba(255,255,255,0.2);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.1rem;
color: #fff;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
border: 8px dashed #fff;
animation: chipRotate 20s linear infinite;
}
@keyframes chipRotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.chip-label:hover .chip {
transform: scale(1.1);
box-shadow:
0 10px 30px rgba(255,0,0,0.5),
inset 0 0 30px rgba(255,255,255,0.3);
}
.chip-label input[type="radio"]:checked + .chip {
background: radial-gradient(circle at 30% 30%, #44ff44, #00cc00);
box-shadow:
0 0 30px rgba(0,255,0,0.7),
inset 0 0 20px rgba(0,0,0,0.3);
animation: chipRotate 5s linear infinite, winPulse 1s ease-in-out infinite;
}
@keyframes winPulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}
/* Slot machine reels */
.reels-container {
background: #000;
border: 5px solid #ffd700;
border-radius: 15px;
padding: 20px;
max-width: 100%;
position: relative;
box-shadow:
inset 0 0 50px rgba(0,0,0,0.8),
0 0 30px rgba(255, 215, 0, 0.5);
overflow: visible;
margin: 20px 0;
}
.reels-wrapper {
display: flex;
gap: 2px;
min-width: fit-content;
padding: 5px 0;
justify-content: center;
flex-wrap: wrap;
max-width: 1200px;
margin: 0 auto;
background: linear-gradient(90deg,
transparent 0%,
rgba(255, 215, 0, 0.1) 50%,
transparent 100%);
}
.reel {
width: 20px;
height: 50px;
background: #ffffff;
border: 2px solid #ffd700;
border-radius: 3px;
overflow: hidden;
position: relative;
box-shadow:
inset 0 0 10px rgba(0,0,0,0.3),
0 0 5px rgba(255, 215, 0, 0.5);
}
.reel-strip {
position: absolute;
width: 100%;
transition: transform 0.5s ease-out;
}
.nucleotide {
height: 50px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
font-weight: bold;
background: #ffffff;
text-shadow: 0 0 5px currentColor;
}
.nucleotide.A { color: #00ff00; background: #001100; }
.nucleotide.T { color: #ff0000; background: #110000; }
.nucleotide.C { color: #00ffff; background: #001111; }
.nucleotide.G { color: #ffff00; background: #111100; }
/* Casino style lever */
.lever-container {
position: absolute;
right: -90px;
top: 50%;
transform: translateY(-50%);
z-index: 3;
width: 80px;
height: 250px;
}
.lever {
width: 100%;
height: 100%;
position: relative;
cursor: pointer;
}
.lever-mount {
position: absolute;
top: 100px;
left: -15px;
width: 60px;
height: 80px;
background: linear-gradient(180deg, #ffd700, #b8860b);
border-radius: 10px 0 0 10px;
box-shadow:
0 5px 20px rgba(0,0,0,0.5),
inset 0 2px 5px rgba(255,255,255,0.5);
}
.lever-arm {
position: absolute;
top: 30px;
left: 10px;
width: 15px;
height: 100px;
background: linear-gradient(180deg, #c0c0c0, #808080);
border-radius: 8px;
box-shadow: 0 3px 10px rgba(0,0,0,0.5);
transition: all 0.6s cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
.lever-ball {
position: absolute;
top: -40px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 80px;
background: radial-gradient(circle at 35% 35%, #ff4444, #cc0000, #800000);
border-radius: 50%;
box-shadow:
0 10px 30px rgba(0,0,0,0.6),
inset -10px -10px 20px rgba(0,0,0,0.5),
inset 5px 5px 10px rgba(255,255,255,0.6);
position: relative;
overflow: hidden;
}
.lever-ball::before {
content: '777';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
font-weight: bold;
color: #ffd700;
text-shadow: 0 2px 4px rgba(0,0,0,0.8);
}
.lever.pulled .lever-arm {
transform: translateY(100px);
height: 20px;
}
/* Generate button */
.spin-button {
background: linear-gradient(145deg, #ff0000, #cc0000);
border: 3px solid #ffd700;
padding: 25px 80px;
font-size: 2rem;
font-weight: bold;
border-radius: 50px;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 3px;
box-shadow:
0 10px 30px rgba(0,0,0,0.7),
0 0 30px rgba(255, 0, 0, 0.5),
inset 0 2px 10px rgba(255,255,255,0.3);
transition: all 0.3s ease;
color: #fff;
text-shadow: 0 3px 6px rgba(0,0,0,0.5);
position: relative;
overflow: hidden;
font-family: 'Bebas Neue', cursive;
}
.spin-button::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg,
transparent 30%,
rgba(255,255,255,0.3) 50%,
transparent 70%);
animation: buttonShine 3s infinite;
}
@keyframes buttonShine {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spin-button:hover {
transform: translateY(-3px) scale(1.05);
box-shadow:
0 15px 40px rgba(0,0,0,0.8),
0 0 50px rgba(255, 0, 0, 0.7);
}
.spin-button:active {
transform: translateY(0) scale(0.98);
}
.spin-button:disabled {
background: linear-gradient(145deg, #444, #222);
cursor: not-allowed;
box-shadow: none;
animation: none;
}
/* Sequence display */
.sequence-display {
background: #000;
border: 3px solid #00ff00;
border-radius: 15px;
padding: 25px;
font-family: 'Orbitron', monospace;
font-size: 0.9rem;
letter-spacing: 2px;
width: 100%;
max-width: 1200px;
margin: 20px auto;
box-shadow:
0 0 30px rgba(0, 255, 0, 0.5),
inset 0 0 20px rgba(0, 255, 0, 0.1);
position: relative;
min-height: 100px;
word-wrap: break-word;
word-break: break-all;
white-space: pre-wrap;
overflow-wrap: break-word;
}
.sequence-display::before {
content: '๐Ÿงฌ SYNTHETIC REGULATORY ELEMENT';
position: absolute;
top: -15px;
left: 50%;
transform: translateX(-50%);
background: #000;
padding: 0 20px;
font-size: 0.8rem;
color: #00ff00;
letter-spacing: 3px;
text-shadow: 0 0 10px #00ff00;
}
/* Protein analysis panel */
.protein-panel {
background: linear-gradient(145deg, #1a0033, #000033);
border: 3px solid #00ffff;
border-radius: 15px;
padding: 25px;
margin: 20px auto;
max-width: 1200px;
box-shadow:
0 0 30px rgba(0, 255, 255, 0.5),
inset 0 0 20px rgba(0, 255, 255, 0.1);
display: none;
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.protein-panel.active {
display: block;
}
.protein-header {
font-size: 1.5rem;
color: #00ffff;
margin-bottom: 15px;
text-align: center;
text-transform: uppercase;
letter-spacing: 3px;
text-shadow: 0 0 15px #00ffff;
}
.protein-sequence {
background: #000;
border: 1px solid #00ffff;
border-radius: 10px;
padding: 15px;
margin-bottom: 20px;
font-family: 'Courier New', monospace;
font-size: 0.85rem;
word-wrap: break-word;
word-break: break-all;
color: #00ff00;
max-height: 150px;
overflow-y: auto;
white-space: pre-wrap;
line-height: 1.4;
}
.protein-analysis {
background: rgba(0, 255, 255, 0.05);
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 10px;
padding: 20px;
color: #fff;
line-height: 1.8;
font-size: 0.95rem;
}
.protein-analysis h3 {
color: #00ffff;
margin-bottom: 10px;
text-shadow: 0 0 10px #00ffff;
}
.protein-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: rgba(255, 215, 0, 0.1);
border: 1px solid #ffd700;
border-radius: 10px;
padding: 15px;
text-align: center;
}
.stat-label {
font-size: 0.8rem;
color: #ffd700;
text-transform: uppercase;
letter-spacing: 1px;
}
.stat-value {
font-size: 1.5rem;
color: #fff;
font-weight: bold;
margin-top: 5px;
}
/* Info footer */
.info {
text-align: center;
margin-top: 20px;
color: #ffd700;
font-size: 1rem;
text-transform: uppercase;
letter-spacing: 2px;
text-shadow: 0 0 10px #ffd700;
}
.lab-credit {
text-align: center;
margin-top: 15px;
font-size: 1.3rem;
display: flex;
justify-content: center;
gap: 40px;
flex-wrap: wrap;
}
.lab-credit a {
color: #00ff00;
text-decoration: none;
font-weight: bold;
letter-spacing: 2px;
padding: 10px 30px;
border: 2px solid #00ff00;
border-radius: 30px;
display: inline-block;
transition: all 0.3s ease;
text-shadow: 0 0 10px #00ff00;
}
.lab-credit a:nth-child(2) {
color: #ffff00;
border-color: #ffff00;
text-shadow: 0 0 10px #ffff00;
}
.lab-credit a:hover {
background: #00ff00;
color: #000;
box-shadow: 0 0 30px #00ff00;
transform: scale(1.1);
}
.lab-credit a:nth-child(2):hover {
background: #ffff00;
box-shadow: 0 0 30px #ffff00;
}
/* Winning animation */
.jackpot-win {
animation: jackpotFlash 2s ease-out;
}
@keyframes jackpotFlash {
0%, 100% { background-color: transparent; }
10%, 30%, 50%, 70%, 90% { background-color: rgba(255, 215, 0, 0.3); }
20%, 40%, 60%, 80% { background-color: transparent; }
}
/* Loading animation */
@keyframes continuousSpin {
from { transform: translateY(0); }
to { transform: translateY(-200px); }
}
.reel-strip.loading {
animation: continuousSpin 1s linear infinite;
}
/* Scrollbar styling */
::-webkit-scrollbar {
width: 10px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.5);
border-radius: 5px;
}
::-webkit-scrollbar-thumb {
background: #00ffff;
border-radius: 5px;
}
::-webkit-scrollbar-thumb:hover {
background: #00cccc;
}
</style>
</head>
<body>
<div class="machine-container">
<h1 class="title"><a href="https://github.com/pinellolab/DNA-Diffusion" target="_blank" rel="noopener noreferrer">๐ŸŽฐ DNA CASINO ๐Ÿงฌ</a></h1>
<div class="subtitle">โ˜… PROTEIN SYNTHESIS JACKPOT โ˜…</div>
<div class="jackpot-display">
<div class="jackpot-label">Current Sequence Length</div>
<div class="jackpot-value" id="sequenceCounter">0 BP</div>
</div>
<div class="cell-type-selector">
<div class="radio-group">
<label class="chip-label">
<input type="radio" name="cellType" value="K562" checked>
<div class="chip">K562</div>
</label>
<label class="chip-label">
<input type="radio" name="cellType" value="GM12878">
<div class="chip">GM12878</div>
</label>
<label class="chip-label">
<input type="radio" name="cellType" value="HepG2">
<div class="chip">HepG2</div>
</label>
</div>
</div>
<div class="language-selector">
<label class="language-toggle">
<input type="checkbox" id="languageToggle">
<span class="toggle-slider">
<span class="lang-label" data-lang="en">EN</span>
<span class="lang-label" data-lang="ko">ํ•œ๊ธ€</span>
</span>
</label>
</div>
<div class="reels-container" id="reelsContainer">
<div class="reels-wrapper" id="reelsWrapper"></div>
<div class="lever-container">
<div class="lever" id="lever">
<div class="lever-mount"></div>
<div class="lever-arm">
<div class="lever-ball"></div>
</div>
</div>
</div>
</div>
<div class="controls">
<button class="spin-button" id="spinButton"><span>๐ŸŽฒ SPIN TO WIN ๐ŸŽฒ</span></button>
<div class="sequence-display" id="sequenceDisplay">
๐ŸŽฏ <span id="initialMessage">Pull the lever or press SPIN to generate your winning sequence!</span> ๐ŸŽฏ
</div>
<div class="protein-panel" id="proteinPanel">
<div class="protein-header" id="proteinHeader">๐Ÿ”ฌ Protein Analysis Report ๐Ÿ”ฌ</div>
<div class="protein-stats" id="proteinStats"></div>
<div class="protein-sequence" id="proteinSequence"></div>
<div class="protein-analysis" id="proteinAnalysis">
<h3>Analyzing protein structure...</h3>
</div>
</div>
</div>
<div class="info">
๐ŸŽฐ 200BP REGULATORY ELEMENTS โ€ข CELL-TYPE SPECIFIC โ€ข SYNTHETIC BIOLOGY JACKPOT ๐ŸŽฐ
</div>
<div class="lab-credit">
<a href="https://pinellolab.org" target="_blank" rel="noopener noreferrer">
๐Ÿงฌ Based by PINELLO LAB ๐Ÿงฌ
</a>
<a href="https://discord.gg/openfreeai" target="_blank" rel="noopener noreferrer">
โšก Powered by VIDraft โšก
</a>
</div>
</div>
<script>
const NUCLEOTIDES = ['A', 'T', 'C', 'G'];
const REEL_COUNT = 200;
let TARGET_SEQUENCE = '';
let reels = [];
let isSpinning = false;
function generateRandomSequence() {
let sequence = '';
for (let i = 0; i < REEL_COUNT; i++) {
sequence += NUCLEOTIDES[Math.floor(Math.random() * 4)];
}
return sequence;
}
function createReel(index) {
const reel = document.createElement('div');
reel.className = 'reel';
const strip = document.createElement('div');
strip.className = 'reel-strip';
// Create multiple nucleotides for smooth spinning effect
for (let i = 0; i < 10; i++) {
NUCLEOTIDES.forEach(n => {
const nucleotide = document.createElement('div');
nucleotide.className = `nucleotide ${n}`;
nucleotide.textContent = n;
strip.appendChild(nucleotide);
});
}
reel.appendChild(strip);
return { element: reel, strip: strip, currentPosition: 0 };
}
function initializeReels() {
const wrapper = document.getElementById('reelsWrapper');
wrapper.innerHTML = '';
reels = [];
for (let i = 0; i < REEL_COUNT; i++) {
const reel = createReel(i);
reels.push(reel);
wrapper.appendChild(reel.element);
// Set initial position to show a random nucleotide
const randomIndex = Math.floor(Math.random() * 4);
const initialOffset = -randomIndex * 50;
reel.strip.style.transform = `translateY(${initialOffset}px)`;
reel.currentPosition = randomIndex * 50;
}
}
function updateSequenceCounter(length) {
const counter = document.getElementById('sequenceCounter');
counter.textContent = `${length} BP`;
counter.style.animation = 'pulse 0.5s ease-out';
}
function startContinuousSpinning() {
reels.forEach((reel, index) => {
// Add continuous spinning animation
reel.strip.style.transition = 'none';
reel.strip.classList.add('loading');
// Add slight delay variation for visual effect
const delay = (index % 10) * 0.1;
reel.strip.style.animationDelay = `${delay}s`;
});
// Update counter animation
let count = 0;
const counterInterval = setInterval(() => {
if (!isSpinning) {
clearInterval(counterInterval);
return;
}
count = (count + 7) % 201;
updateSequenceCounter(count);
}, 100);
}
function displayProteinAnalysis(metadata) {
const panel = document.getElementById('proteinPanel');
const header = document.getElementById('proteinHeader');
const statsDiv = document.getElementById('proteinStats');
const sequenceDiv = document.getElementById('proteinSequence');
const analysisDiv = document.getElementById('proteinAnalysis');
// Show panel
panel.classList.add('active');
// Check language
const isKorean = document.getElementById('languageToggle').checked;
// Update header
header.textContent = isKorean ? '๐Ÿ”ฌ ๋‹จ๋ฐฑ์งˆ ๋ถ„์„ ๋ณด๊ณ ์„œ ๐Ÿ”ฌ' : '๐Ÿ”ฌ Protein Analysis Report ๐Ÿ”ฌ';
// Display stats
statsDiv.innerHTML = `
<div class="stat-card">
<div class="stat-label">${isKorean ? '์„ธํฌ ํƒ€์ž…' : 'Cell Type'}</div>
<div class="stat-value">${metadata.cell_type || 'Unknown'}</div>
</div>
<div class="stat-card">
<div class="stat-label">${isKorean ? 'DNA ๊ธธ์ด' : 'DNA Length'}</div>
<div class="stat-value">${TARGET_SEQUENCE.length} bp</div>
</div>
<div class="stat-card">
<div class="stat-label">${isKorean ? '๋‹จ๋ฐฑ์งˆ ๊ธธ์ด' : 'Protein Length'}</div>
<div class="stat-value">${metadata.protein_length || 0} aa</div>
</div>
<div class="stat-card">
<div class="stat-label">${isKorean ? '์ƒ์„ฑ ์‹œ๊ฐ„' : 'Generation Time'}</div>
<div class="stat-value">${(metadata.generation_time || 0).toFixed(1)}s</div>
</div>
`;
// Display protein sequence
if (metadata.protein_sequence) {
sequenceDiv.innerHTML = `<strong>${isKorean ? '๋‹จ๋ฐฑ์งˆ ์‹œํ€€์Šค:' : 'Protein Sequence:'}</strong><br>${metadata.protein_sequence}`;
}
// Display analysis
if (metadata.protein_analysis) {
analysisDiv.innerHTML = `<h3>๐Ÿงฌ ${isKorean ? '๊ตฌ์กฐ ๋ฐ ๊ธฐ๋Šฅ ๋ถ„์„' : 'Structural & Functional Analysis'}</h3>${metadata.protein_analysis}`;
} else {
analysisDiv.innerHTML = `<h3>โณ ${isKorean ? '๋‹จ๋ฐฑ์งˆ ๋ถ„์„ ๋Œ€๊ธฐ ์ค‘...' : 'Protein analysis pending...'}</h3>`;
}
}
function stopAndShowSequence(sequence, metadata = {}) {
TARGET_SEQUENCE = sequence;
updateSequenceCounter(sequence.length);
reels.forEach((reel, index) => {
// Remove continuous spinning
reel.strip.classList.remove('loading');
// Calculate target position
const targetNucleotide = TARGET_SEQUENCE[index];
const targetIndex = NUCLEOTIDES.indexOf(targetNucleotide);
const finalPosition = targetIndex * 50;
// Set up the final positioning animation
setTimeout(() => {
reel.strip.style.transition = `transform ${1000 + index * 5}ms cubic-bezier(0.17, 0.67, 0.12, 0.99)`;
reel.strip.style.transform = `translateY(${-finalPosition}px)`;
reel.currentPosition = finalPosition;
}, index * 2);
});
// Show the complete sequence after animation
setTimeout(() => {
const container = document.getElementById('reelsContainer');
const display = document.getElementById('sequenceDisplay');
const button = document.getElementById('spinButton');
const lever = document.getElementById('lever');
container.classList.remove('spinning');
container.classList.add('jackpot-win');
const isKorean = document.getElementById('languageToggle').checked;
display.innerHTML = `<strong>๐ŸŽŠ ${isKorean ? '์žญํŒŸ ์‹œํ€€์Šค' : 'JACKPOT SEQUENCE'} ๐ŸŽŠ</strong><br><span style="color: #00ff00; font-family: 'Courier New', monospace; word-wrap: break-word; word-break: break-all; display: block; margin-top: 10px;">${TARGET_SEQUENCE}</span>`;
// Display protein analysis if available
if (metadata) {
displayProteinAnalysis(metadata);
}
button.disabled = false;
isSpinning = false;
// Release the lever
lever.classList.remove('pulled');
setTimeout(() => {
container.classList.remove('jackpot-win');
}, 2000);
}, 1500);
}
function startGeneration() {
if (isSpinning) return;
isSpinning = true;
const button = document.getElementById('spinButton');
const display = document.getElementById('sequenceDisplay');
const container = document.getElementById('reelsContainer');
const lever = document.getElementById('lever');
const proteinPanel = document.getElementById('proteinPanel');
// Hide protein panel from previous spin
proteinPanel.classList.remove('active');
// Pull the lever
lever.classList.add('pulled');
// Check language
const isKorean = document.getElementById('languageToggle').checked;
button.disabled = true;
display.innerHTML = isKorean ?
'๐ŸŽฒ <strong>์œ ์ „์ž ๋ฃฐ๋ ›์„ ๋Œ๋ฆฌ๋Š” ์ค‘...</strong> ๐ŸŽฒ' :
'๐ŸŽฒ <strong>SPINNING THE GENETIC WHEEL OF FORTUNE...</strong> ๐ŸŽฒ';
container.classList.add('spinning');
// Start continuous spinning immediately
startContinuousSpinning();
// Get selected cell type and language
const cellType = document.querySelector('input[name="cellType"]:checked').value;
const language = document.getElementById('languageToggle').checked ? 'ko' : 'en';
// Send request to parent window
window.parent.postMessage({
type: 'generate_request',
cellType: cellType,
language: language
}, '*');
}
// Initialize
initializeReels();
// Language toggle event listener
document.getElementById('languageToggle').addEventListener('change', function() {
const isKorean = this.checked;
const initialMessage = document.getElementById('initialMessage');
if (initialMessage && !isSpinning) {
initialMessage.textContent = isKorean ?
'๋ ˆ๋ฒ„๋ฅผ ๋‹น๊ธฐ๊ฑฐ๋‚˜ SPIN์„ ๋ˆŒ๋Ÿฌ ๋‹น์ฒจ ์‹œํ€€์Šค๋ฅผ ์ƒ์„ฑํ•˜์„ธ์š”!' :
'Pull the lever or press SPIN to generate your winning sequence!';
}
});
// Event listeners
document.getElementById('spinButton').addEventListener('click', startGeneration);
// Lever click functionality
document.getElementById('lever').addEventListener('click', function() {
if (!isSpinning) {
startGeneration();
}
});
// Listen for messages from parent window
window.addEventListener('message', (event) => {
if (event.data.type === 'sequence_generated') {
// Stop spinning and show the actual sequence with metadata
stopAndShowSequence(event.data.sequence, event.data.metadata);
} else if (event.data.type === 'generation_error') {
// Stop spinning and show error
reels.forEach(reel => {
reel.strip.classList.remove('loading');
});
const container = document.getElementById('reelsContainer');
const display = document.getElementById('sequenceDisplay');
const button = document.getElementById('spinButton');
const lever = document.getElementById('lever');
const isKorean = document.getElementById('languageToggle').checked;
container.classList.remove('spinning');
display.innerHTML = `<strong style="color: #ff0000;">โŒ ${isKorean ? '์‹คํŒจ!' : 'BUST!'} โŒ</strong><br>` + event.data.error;
button.disabled = false;
isSpinning = false;
lever.classList.remove('pulled');
updateSequenceCounter(0);
}
});
// Keyboard support
document.addEventListener('keydown', (e) => {
if (e.code === 'Space' && !isSpinning) {
e.preventDefault();
startGeneration();
}
});
// Add some casino ambiance
setInterval(() => {
if (!isSpinning && Math.random() > 0.7) {
const chips = document.querySelectorAll('.chip');
const randomChip = chips[Math.floor(Math.random() * chips.length)];
randomChip.style.animation = 'none';
setTimeout(() => {
randomChip.style.animation = 'chipRotate 20s linear infinite';
}, 10);
}
}, 5000);
</script>
</body>
</html>