Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Neon Tetris</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @keyframes explode { | |
| 0% { transform: scale(1); opacity: 1; } | |
| 100% { transform: scale(3); opacity: 0; } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(1); } | |
| 50% { transform: scale(1.1); } | |
| } | |
| @keyframes rainbow { | |
| 0% { background-color: #ff0000; } | |
| 14% { background-color: #ff7f00; } | |
| 28% { background-color: #ffff00; } | |
| 42% { background-color: #00ff00; } | |
| 57% { background-color: #0000ff; } | |
| 71% { background-color: #4b0082; } | |
| 85% { background-color: #9400d3; } | |
| 100% { background-color: #ff0000; } | |
| } | |
| .explosion { | |
| animation: explode 0.5s forwards; | |
| position: absolute; | |
| border-radius: 50%; | |
| pointer-events: none; | |
| } | |
| .cleared-row { | |
| animation: pulse 0.2s 3, rainbow 1.5s; | |
| } | |
| .tetris-grid { | |
| box-shadow: 0 0 20px rgba(0, 255, 255, 0.7); | |
| } | |
| .tetris-cell { | |
| transition: background-color 0.1s; | |
| box-shadow: inset 0 0 5px rgba(255, 255, 255, 0.3); | |
| } | |
| .ghost-piece { | |
| opacity: 0.3; | |
| } | |
| .next-piece-container { | |
| box-shadow: 0 0 15px rgba(255, 0, 255, 0.5); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4 font-mono"> | |
| <div class="flex flex-col md:flex-row items-center gap-8"> | |
| <!-- Game board --> | |
| <div class="relative"> | |
| <div class="tetris-grid bg-gray-800 border-2 border-cyan-400 rounded"> | |
| <div id="game-board" class="grid grid-cols-10 grid-rows-20 gap-0"></div> | |
| </div> | |
| <div id="game-over" class="absolute inset-0 bg-black bg-opacity-70 flex flex-col items-center justify-center hidden"> | |
| <h2 class="text-4xl font-bold mb-4 text-red-500">GAME OVER</h2> | |
| <button id="restart-btn" class="px-6 py-2 bg-cyan-500 hover:bg-cyan-600 rounded-lg font-bold transition">Play Again</button> | |
| </div> | |
| </div> | |
| <!-- Side panel --> | |
| <div class="flex flex-col gap-6"> | |
| <div class="bg-gray-800 p-4 rounded-lg border-2 border-purple-400"> | |
| <h2 class="text-xl font-bold mb-2 text-center text-purple-300">NEXT</h2> | |
| <div class="next-piece-container bg-gray-700 p-2 rounded"> | |
| <div id="next-piece" class="grid grid-cols-4 grid-rows-4 gap-1 w-24 h-24"></div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-400"> | |
| <h2 class="text-xl font-bold mb-2 text-center text-green-300">SCORE</h2> | |
| <div class="text-3xl font-bold text-center" id="score">0</div> | |
| </div> | |
| <div class="bg-gray-800 p-4 rounded-lg border-2 border-yellow-400"> | |
| <h2 class="text-xl font-bold mb-2 text-center text-yellow-300">LEVEL</h2> | |
| <div class="text-3xl font-bold text-center" id="level">1</div> | |
| </div> | |
| <div class="bg-gray-800 p-4 rounded-lg border-2 border-red-400"> | |
| <h2 class="text-xl font-bold mb-2 text-center text-red-300">LINES</h2> | |
| <div class="text-3xl font-bold text-center" id="lines">0</div> | |
| </div> | |
| <div class="flex flex-col gap-2 mt-2"> | |
| <button id="pause-btn" class="px-4 py-2 bg-yellow-500 hover:bg-yellow-600 rounded font-bold transition">Pause</button> | |
| <button id="sound-btn" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 rounded font-bold transition">Sound: ON</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Controls info --> | |
| <div class="mt-8 text-center text-gray-400"> | |
| <p class="mb-2">Controls: Arrow keys to move, Up to rotate, Space to drop, P to pause</p> | |
| <p>Made with HTML, CSS & JavaScript</p> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Game constants | |
| const COLS = 10; | |
| const ROWS = 20; | |
| const BLOCK_SIZE = 30; | |
| const EMPTY = 'empty'; | |
| // Scoring | |
| const SCORE = { | |
| SINGLE: 100, | |
| DOUBLE: 300, | |
| TRIPLE: 500, | |
| TETRIS: 800, | |
| SOFT_DROP: 1, | |
| HARD_DROP: 2 | |
| }; | |
| const LEVEL_SPEED = { | |
| 1: 800, | |
| 2: 720, | |
| 3: 630, | |
| 4: 550, | |
| 5: 470, | |
| 6: 380, | |
| 7: 300, | |
| 8: 220, | |
| 9: 130, | |
| 10: 100, | |
| 11: 80, | |
| 12: 80, | |
| 13: 80, | |
| 14: 70, | |
| 15: 70, | |
| 16: 70, | |
| 17: 50, | |
| 18: 50, | |
| 19: 50, | |
| 20: 30 | |
| }; | |
| // Tetromino shapes and colors | |
| const SHAPES = [ | |
| { shape: [[0, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]], color: 'bg-cyan-500' }, // I | |
| { shape: [[1, 0, 0], [1, 1, 1], [0, 0, 0]], color: 'bg-blue-500' }, // J | |
| { shape: [[0, 0, 1], [1, 1, 1], [0, 0, 0]], color: 'bg-orange-500' }, // L | |
| { shape: [[1, 1], [1, 1]], color: 'bg-yellow-500' }, // O | |
| { shape: [[0, 1, 1], [1, 1, 0], [0, 0, 0]], color: 'bg-green-500' }, // S | |
| { shape: [[0, 1, 0], [1, 1, 1], [0, 0, 0]], color: 'bg-purple-500' }, // T | |
| { shape: [[1, 1, 0], [0, 1, 1], [0, 0, 0]], color: 'bg-red-500' } // Z | |
| ]; | |
| // Game state | |
| let board = Array(ROWS).fill().map(() => Array(COLS).fill(EMPTY)); | |
| let currentPiece = null; | |
| let nextPiece = null; | |
| let currentPosition = { x: 0, y: 0 }; | |
| let score = 0; | |
| let lines = 0; | |
| let level = 1; | |
| let gameOver = false; | |
| let isPaused = false; | |
| let soundEnabled = true; | |
| let dropInterval = null; | |
| let ghostPiece = null; | |
| // DOM elements | |
| const gameBoard = document.getElementById('game-board'); | |
| const nextPieceDisplay = document.getElementById('next-piece'); | |
| const scoreDisplay = document.getElementById('score'); | |
| const linesDisplay = document.getElementById('lines'); | |
| const levelDisplay = document.getElementById('level'); | |
| const gameOverDisplay = document.getElementById('game-over'); | |
| const restartBtn = document.getElementById('restart-btn'); | |
| const pauseBtn = document.getElementById('pause-btn'); | |
| const soundBtn = document.getElementById('sound-btn'); | |
| // Initialize game board | |
| function initBoard() { | |
| gameBoard.innerHTML = ''; | |
| gameBoard.style.width = `${COLS * BLOCK_SIZE}px`; | |
| gameBoard.style.height = `${ROWS * BLOCK_SIZE}px`; | |
| for (let y = 0; y < ROWS; y++) { | |
| for (let x = 0; x < COLS; x++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'tetris-cell w-7 h-7 border border-gray-700'; | |
| cell.dataset.row = y; | |
| cell.dataset.col = x; | |
| gameBoard.appendChild(cell); | |
| } | |
| } | |
| } | |
| // Create a random tetromino | |
| function createPiece() { | |
| const randomIndex = Math.floor(Math.random() * SHAPES.length); | |
| return { | |
| shape: SHAPES[randomIndex].shape, | |
| color: SHAPES[randomIndex].color, | |
| width: SHAPES[randomIndex].shape[0].length, | |
| height: SHAPES[randomIndex].shape.length | |
| }; | |
| } | |
| // Draw the game board | |
| function drawBoard() { | |
| const cells = document.querySelectorAll('.tetris-cell'); | |
| // Clear all cells | |
| cells.forEach(cell => { | |
| cell.className = 'tetris-cell w-7 h-7 border border-gray-700'; | |
| cell.innerHTML = ''; | |
| }); | |
| // Draw locked pieces | |
| for (let y = 0; y < ROWS; y++) { | |
| for (let x = 0; x < COLS; x++) { | |
| if (board[y][x] !== EMPTY) { | |
| const cell = document.querySelector(`[data-row="${y}"][data-col="${x}"]`); | |
| cell.className = `tetris-cell w-7 h-7 border border-gray-700 ${board[y][x]}`; | |
| } | |
| } | |
| } | |
| // Draw current piece | |
| if (currentPiece) { | |
| for (let y = 0; y < currentPiece.height; y++) { | |
| for (let x = 0; x < currentPiece.width; x++) { | |
| if (currentPiece.shape[y][x]) { | |
| const boardX = currentPosition.x + x; | |
| const boardY = currentPosition.y + y; | |
| if (boardY >= 0) { | |
| const cell = document.querySelector(`[data-row="${boardY}"][data-col="${boardX}"]`); | |
| if (cell) { | |
| cell.className = `tetris-cell w-7 h-7 border border-gray-700 ${currentPiece.color}`; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Draw ghost piece | |
| if (ghostPiece) { | |
| for (let y = 0; y < ghostPiece.height; y++) { | |
| for (let x = 0; x < ghostPiece.width; x++) { | |
| if (ghostPiece.shape[y][x]) { | |
| const boardX = ghostPiece.position.x + x; | |
| const boardY = ghostPiece.position.y + y; | |
| if (boardY >= 0) { | |
| const cell = document.querySelector(`[data-row="${boardY}"][data-col="${boardX}"]`); | |
| if (cell) { | |
| cell.classList.add('ghost-piece', currentPiece.color); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Draw next piece preview | |
| function drawNextPiece() { | |
| nextPieceDisplay.innerHTML = ''; | |
| for (let y = 0; y < 4; y++) { | |
| for (let x = 0; x < 4; x++) { | |
| const cell = document.createElement('div'); | |
| cell.className = 'w-5 h-5 border border-gray-600'; | |
| if (nextPiece && y < nextPiece.height && x < nextPiece.width && nextPiece.shape[y][x]) { | |
| cell.className += ` ${nextPiece.color}`; | |
| } | |
| nextPieceDisplay.appendChild(cell); | |
| } | |
| } | |
| } | |
| // Check for collisions | |
| function collision(x, y, piece) { | |
| for (let py = 0; py < piece.height; py++) { | |
| for (let px = 0; px < piece.width; px++) { | |
| if (piece.shape[py][px]) { | |
| const boardX = x + px; | |
| const boardY = y + py; | |
| // Check boundaries | |
| if (boardX < 0 || boardX >= COLS || boardY >= ROWS) { | |
| return true; | |
| } | |
| // Check if already occupied (and not above the board) | |
| if (boardY >= 0 && board[boardY][boardX] !== EMPTY) { | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| // Rotate the current piece | |
| function rotate() { | |
| if (!currentPiece || gameOver || isPaused) return; | |
| const rotated = { | |
| shape: Array(currentPiece.width).fill().map(() => Array(currentPiece.height).fill(0)), | |
| color: currentPiece.color, | |
| width: currentPiece.height, | |
| height: currentPiece.width | |
| }; | |
| // Transpose and reverse rows to rotate 90 degrees | |
| for (let y = 0; y < currentPiece.height; y++) { | |
| for (let x = 0; x < currentPiece.width; x++) { | |
| rotated.shape[x][currentPiece.height - 1 - y] = currentPiece.shape[y][x]; | |
| } | |
| } | |
| // Check if rotation is possible | |
| if (!collision(currentPosition.x, currentPosition.y, rotated)) { | |
| currentPiece = rotated; | |
| updateGhostPiece(); | |
| drawBoard(); | |
| playSound('rotate'); | |
| } else { | |
| // Try wall kicks (move left/right if rotation hits wall) | |
| const kicks = [-1, 1, -2, 2]; | |
| for (const kick of kicks) { | |
| if (!collision(currentPosition.x + kick, currentPosition.y, rotated)) { | |
| currentPosition.x += kick; | |
| currentPiece = rotated; | |
| updateGhostPiece(); | |
| drawBoard(); | |
| playSound('rotate'); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| // Move the piece left/right | |
| function move(direction) { | |
| if (!currentPiece || gameOver || isPaused) return; | |
| const newX = currentPosition.x + direction; | |
| if (!collision(newX, currentPosition.y, currentPiece)) { | |
| currentPosition.x = newX; | |
| updateGhostPiece(); | |
| drawBoard(); | |
| if (direction !== 0) playSound('move'); | |
| } | |
| } | |
| // Move the piece down (soft drop) | |
| function softDrop() { | |
| if (!currentPiece || gameOver || isPaused) return; | |
| const newY = currentPosition.y + 1; | |
| if (!collision(currentPosition.x, newY, currentPiece)) { | |
| currentPosition.y = newY; | |
| updateGhostPiece(); | |
| drawBoard(); | |
| addScore(SCORE.SOFT_DROP); | |
| return false; // Didn't hit bottom | |
| } else { | |
| lockPiece(); | |
| return true; // Hit bottom | |
| } | |
| } | |
| // Hard drop (instant drop) | |
| function hardDrop() { | |
| if (!currentPiece || gameOver || isPaused) return; | |
| let dropDistance = 0; | |
| while (!collision(currentPosition.x, currentPosition.y + 1, currentPiece)) { | |
| currentPosition.y++; | |
| dropDistance++; | |
| } | |
| lockPiece(); | |
| addScore(SCORE.HARD_DROP * dropDistance); | |
| playSound('hardDrop'); | |
| } | |
| // Lock the piece in place | |
| function lockPiece() { | |
| for (let y = 0; y < currentPiece.height; y++) { | |
| for (let x = 0; x < currentPiece.width; x++) { | |
| if (currentPiece.shape[y][x]) { | |
| const boardY = currentPosition.y + y; | |
| const boardX = currentPosition.x + x; | |
| // If piece is locked above the board, game over | |
| if (boardY < 0) { | |
| endGame(); | |
| return; | |
| } | |
| board[boardY][boardX] = currentPiece.color; | |
| } | |
| } | |
| } | |
| // Check for completed lines | |
| checkLines(); | |
| // Get next piece | |
| spawnPiece(); | |
| playSound('lock'); | |
| } | |
| // Check for completed lines | |
| function checkLines() { | |
| const linesToClear = []; | |
| for (let y = ROWS - 1; y >= 0; y--) { | |
| if (board[y].every(cell => cell !== EMPTY)) { | |
| linesToClear.push(y); | |
| } | |
| } | |
| if (linesToClear.length > 0) { | |
| clearLines(linesToClear); | |
| } | |
| } | |
| // Clear completed lines with special effects | |
| function clearLines(linesToClear) { | |
| // Highlight lines about to be cleared | |
| linesToClear.forEach(y => { | |
| for (let x = 0; x < COLS; x++) { | |
| const cell = document.querySelector(`[data-row="${y}"][data-col="${x}"]`); | |
| if (cell) cell.classList.add('cleared-row'); | |
| } | |
| }); | |
| playSound('clear'); | |
| // Wait for animation then clear | |
| setTimeout(() => { | |
| // Create explosion effects | |
| linesToClear.forEach(y => { | |
| for (let x = 0; x < COLS; x++) { | |
| createExplosion(x, y); | |
| } | |
| }); | |
| // Remove lines and add new empty ones at top | |
| linesToClear.forEach(y => { | |
| board.splice(y, 1); | |
| board.unshift(Array(COLS).fill(EMPTY)); | |
| }); | |
| // Update score | |
| addScore( | |
| linesToClear.length === 1 ? SCORE.SINGLE * level : | |
| linesToClear.length === 2 ? SCORE.DOUBLE * level : | |
| linesToClear.length === 3 ? SCORE.TRIPLE * level : | |
| SCORE.TETRIS * level | |
| ); | |
| // Update lines and level | |
| lines += linesToClear.length; | |
| linesDisplay.textContent = lines; | |
| const newLevel = Math.floor(lines / 10) + 1; | |
| if (newLevel > level && newLevel <= 20) { | |
| level = newLevel; | |
| levelDisplay.textContent = level; | |
| updateDropSpeed(); | |
| playSound('levelUp'); | |
| } | |
| drawBoard(); | |
| }, 300); | |
| } | |
| // Create explosion effect at a cell | |
| function createExplosion(x, y) { | |
| const cell = document.querySelector(`[data-row="${y}"][data-col="${x}"]`); | |
| if (!cell) return; | |
| const rect = cell.getBoundingClientRect(); | |
| const colors = [ | |
| 'bg-red-500', 'bg-orange-500', 'bg-yellow-500', | |
| 'bg-green-500', 'bg-blue-500', 'bg-purple-500' | |
| ]; | |
| // Create multiple explosion particles | |
| for (let i = 0; i < 5; i++) { | |
| const particle = document.createElement('div'); | |
| const color = colors[Math.floor(Math.random() * colors.length)]; | |
| particle.className = `explosion ${color} w-3 h-3`; | |
| particle.style.left = `${rect.left + rect.width / 2}px`; | |
| particle.style.top = `${rect.top + rect.height / 2}px`; | |
| document.body.appendChild(particle); | |
| // Remove after animation | |
| setTimeout(() => { | |
| particle.remove(); | |
| }, 500); | |
| } | |
| } | |
| // Spawn a new piece | |
| function spawnPiece() { | |
| currentPiece = nextPiece || createPiece(); | |
| nextPiece = createPiece(); | |
| currentPosition = { x: Math.floor(COLS / 2) - Math.floor(currentPiece.width / 2), y: -2 }; | |
| // Check if game over (new piece collides immediately) | |
| if (collision(currentPosition.x, currentPosition.y, currentPiece)) { | |
| endGame(); | |
| } | |
| updateGhostPiece(); | |
| drawBoard(); | |
| drawNextPiece(); | |
| } | |
| // Update ghost piece position | |
| function updateGhostPiece() { | |
| if (!currentPiece) return; | |
| ghostPiece = { | |
| shape: currentPiece.shape, | |
| color: currentPiece.color, | |
| width: currentPiece.width, | |
| height: currentPiece.height, | |
| position: { ...currentPosition } | |
| }; | |
| // Drop ghost to lowest possible position | |
| while (!collision(ghostPiece.position.x, ghostPiece.position.y + 1, ghostPiece)) { | |
| ghostPiece.position.y++; | |
| } | |
| } | |
| // Update drop speed based on level | |
| function updateDropSpeed() { | |
| if (dropInterval) { | |
| clearInterval(dropInterval); | |
| } | |
| dropInterval = setInterval(() => { | |
| softDrop(); | |
| }, LEVEL_SPEED[level]); | |
| } | |
| // Add to score | |
| function addScore(points) { | |
| score += points; | |
| scoreDisplay.textContent = score; | |
| } | |
| // End the game | |
| function endGame() { | |
| gameOver = true; | |
| clearInterval(dropInterval); | |
| gameOverDisplay.classList.remove('hidden'); | |
| playSound('gameOver'); | |
| } | |
| // Reset the game | |
| function resetGame() { | |
| board = Array(ROWS).fill().map(() => Array(COLS).fill(EMPTY)); | |
| score = 0; | |
| lines = 0; | |
| level = 1; | |
| gameOver = false; | |
| scoreDisplay.textContent = score; | |
| linesDisplay.textContent = lines; | |
| levelDisplay.textContent = level; | |
| gameOverDisplay.classList.add('hidden'); | |
| nextPiece = createPiece(); | |
| spawnPiece(); | |
| updateDropSpeed(); | |
| drawBoard(); | |
| drawNextPiece(); | |
| } | |
| // Play sound effects | |
| function playSound(type) { | |
| if (!soundEnabled) return; | |
| const sounds = { | |
| rotate: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.3; | |
| audio.play(); | |
| }, | |
| move: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.2; | |
| audio.play(); | |
| }, | |
| hardDrop: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.4; | |
| audio.play(); | |
| }, | |
| lock: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.3; | |
| audio.play(); | |
| }, | |
| clear: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.5; | |
| audio.play(); | |
| }, | |
| levelUp: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.5; | |
| audio.play(); | |
| }, | |
| gameOver: () => { | |
| const audio = new Audio('data:audio/wav;base64,UklGRl9vT19XQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YU...'); | |
| audio.volume = 0.6; | |
| audio.play(); | |
| } | |
| }; | |
| if (sounds[type]) { | |
| sounds[type](); | |
| } | |
| } | |
| // Toggle pause | |
| function togglePause() { | |
| isPaused = !isPaused; | |
| pauseBtn.textContent = isPaused ? 'Resume' : 'Pause'; | |
| if (isPaused) { | |
| clearInterval(dropInterval); | |
| playSound('pause'); | |
| } else { | |
| updateDropSpeed(); | |
| playSound('resume'); | |
| } | |
| } | |
| // Toggle sound | |
| function toggleSound() { | |
| soundEnabled = !soundEnabled; | |
| soundBtn.textContent = `Sound: ${soundEnabled ? 'ON' : 'OFF'}`; | |
| } | |
| // Keyboard controls | |
| document.addEventListener('keydown', (e) => { | |
| if (gameOver) return; | |
| switch (e.key) { | |
| case 'ArrowLeft': | |
| move(-1); | |
| break; | |
| case 'ArrowRight': | |
| move(1); | |
| break; | |
| case 'ArrowDown': | |
| softDrop(); | |
| break; | |
| case 'ArrowUp': | |
| rotate(); | |
| break; | |
| case ' ': | |
| hardDrop(); | |
| break; | |
| case 'p': | |
| case 'P': | |
| togglePause(); | |
| break; | |
| } | |
| }); | |
| // Button controls | |
| restartBtn.addEventListener('click', resetGame); | |
| pauseBtn.addEventListener('click', togglePause); | |
| soundBtn.addEventListener('click', toggleSound); | |
| // Initialize game | |
| initBoard(); | |
| nextPiece = createPiece(); | |
| spawnPiece(); | |
| updateDropSpeed(); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=wangjinrambo/t-boy" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |