Initial commit - Science Bowl buzzer system
This commit is contained in:
BIN
.gitignore
vendored
Normal file
BIN
.gitignore
vendored
Normal file
Binary file not shown.
22
config.js
Normal file
22
config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
// Import the functions you need from the SDKs you need
|
||||
import { initializeApp } from "firebase/app";
|
||||
import { getAnalytics } from "firebase/analytics";
|
||||
// TODO: Add SDKs for Firebase products that you want to use
|
||||
// https://firebase.google.com/docs/web/setup#available-libraries
|
||||
|
||||
// Your web app's Firebase configuration
|
||||
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
||||
const firebaseConfig = {
|
||||
apiKey: "AIzaSyDNk5EGWDPBr8MkUFNdfvhP1NvnDxWERq8",
|
||||
authDomain: "science-bowl-practice-8800a.firebaseapp.com",
|
||||
databaseURL: "https://science-bowl-practice-8800a-default-rtdb.firebaseio.com",
|
||||
projectId: "science-bowl-practice-8800a",
|
||||
storageBucket: "science-bowl-practice-8800a.firebasestorage.app",
|
||||
messagingSenderId: "240054855565",
|
||||
appId: "1:240054855565:web:2897ab544b9f1c1b3d3fc4",
|
||||
measurementId: "G-4TD0W788X5"
|
||||
};
|
||||
|
||||
// Initialize Firebase
|
||||
const app = initializeApp(firebaseConfig);
|
||||
const analytics = getAnalytics(app);
|
||||
90
display.html
Normal file
90
display.html
Normal file
@@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Science Bowl - Moderator Display</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="display-body">
|
||||
<div class="display-container">
|
||||
<div class="scoreboard">
|
||||
<div class="player-score player-1">
|
||||
<h3>🔴 Player 1</h3>
|
||||
<div class="score" id="score1">0</div>
|
||||
</div>
|
||||
<div class="player-score player-2">
|
||||
<h3>🔵 Player 2</h3>
|
||||
<div class="score" id="score2">0</div>
|
||||
</div>
|
||||
<div class="player-score player-3">
|
||||
<h3>🟢 Player 3</h3>
|
||||
<div class="score" id="score3">0</div>
|
||||
</div>
|
||||
<div class="player-score player-4">
|
||||
<h3>🟡 Player 4</h3>
|
||||
<div class="score" id="score4">0</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buzzer-indicator" id="buzzerIndicator">
|
||||
<div class="buzzer-lights">
|
||||
<div class="light player-1" id="light1"></div>
|
||||
<div class="light player-2" id="light2"></div>
|
||||
<div class="light player-3" id="light3"></div>
|
||||
<div class="light player-4" id="light4"></div>
|
||||
</div>
|
||||
<div class="buzzed-player" id="buzzedPlayer"></div>
|
||||
</div>
|
||||
|
||||
<div class="question-section">
|
||||
<div class="question-header">
|
||||
<span class="question-category" id="questionCategory">CATEGORY</span>
|
||||
<span class="question-number">Question <span id="questionNum">1</span>/25</span>
|
||||
</div>
|
||||
<div class="question-text" id="questionText">
|
||||
Click "New Question" to start
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="answer-section" id="answerSection" style="display: none;">
|
||||
<h3>Player Answer:</h3>
|
||||
<div class="player-answer" id="playerAnswer"></div>
|
||||
<h3>Correct Answer:</h3>
|
||||
<div class="correct-answer" id="correctAnswer"></div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button id="newQuestion" class="control-btn primary">New Question</button>
|
||||
<button id="activateBuzzer" class="control-btn" disabled>Activate Buzzers</button>
|
||||
<button id="markCorrect" class="control-btn success" style="display: none;">✓ Correct (+4)</button>
|
||||
<button id="markIncorrect" class="control-btn danger" style="display: none;">✗ Incorrect (-4)</button>
|
||||
<button id="showAnswer" class="control-btn" style="display: none;">Show Answer</button>
|
||||
<button id="resetGame" class="control-btn warning">Reset Game</button>
|
||||
</div>
|
||||
|
||||
<div class="category-filter">
|
||||
<label>
|
||||
<input type="checkbox" class="category-checkbox" value="PHYSICS" checked> Physics
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" class="category-checkbox" value="CHEMISTRY" checked> Chemistry
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" class="category-checkbox" value="BIOLOGY" checked> Biology
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" class="category-checkbox" value="MATH" checked> Math
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" class="category-checkbox" value="EARTH SCIENCE" checked> Earth Science
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://www.gstatic.com/firebasejs/9.17.1/firebase-app-compat.js"></script>
|
||||
<script src="https://www.gstatic.com/firebasejs/9.17.1/firebase-database-compat.js"></script>
|
||||
<script src="config.js"></script>
|
||||
<script src="display.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
199
display.js
Normal file
199
display.js
Normal file
@@ -0,0 +1,199 @@
|
||||
const API_URL = 'https://scibowldb.com/api/questions/random';
|
||||
|
||||
// DOM elements
|
||||
const questionText = document.getElementById('questionText');
|
||||
const questionCategory = document.getElementById('questionCategory');
|
||||
const questionNum = document.getElementById('questionNum');
|
||||
const correctAnswer = document.getElementById('correctAnswer');
|
||||
const playerAnswer = document.getElementById('playerAnswer');
|
||||
const answerSection = document.getElementById('answerSection');
|
||||
const buzzedPlayer = document.getElementById('buzzedPlayer');
|
||||
|
||||
const newQuestionBtn = document.getElementById('newQuestion');
|
||||
const activateBuzzerBtn = document.getElementById('activateBuzzer');
|
||||
const markCorrectBtn = document.getElementById('markCorrect');
|
||||
const markIncorrectBtn = document.getElementById('markIncorrect');
|
||||
const showAnswerBtn = document.getElementById('showAnswer');
|
||||
const resetGameBtn = document.getElementById('resetGame');
|
||||
|
||||
let currentQuestion = null;
|
||||
let currentQuestionNumber = 1;
|
||||
let currentBuzzedPlayer = null;
|
||||
|
||||
// Initialize game state
|
||||
database.ref('gameState').set({
|
||||
buzzerActive: false,
|
||||
currentQuestion: null,
|
||||
scores: {
|
||||
player1: 0,
|
||||
player2: 0,
|
||||
player3: 0,
|
||||
player4: 0
|
||||
},
|
||||
questionNumber: 1
|
||||
});
|
||||
|
||||
// Listen for game state changes
|
||||
database.ref('gameState').on('value', (snapshot) => {
|
||||
const state = snapshot.val();
|
||||
if (!state) return;
|
||||
|
||||
// Update scores
|
||||
document.getElementById('score1').textContent = state.scores.player1 || 0;
|
||||
document.getElementById('score2').textContent = state.scores.player2 || 0;
|
||||
document.getElementById('score3').textContent = state.scores.player3 || 0;
|
||||
document.getElementById('score4').textContent = state.scores.player4 || 0;
|
||||
|
||||
// Update buzzer lights
|
||||
if (state.buzzer && state.buzzer.playerId) {
|
||||
const playerNum = state.buzzer.playerId.replace('player', '');
|
||||
currentBuzzedPlayer = state.buzzer.playerId;
|
||||
|
||||
// Light up the buzzer
|
||||
document.querySelectorAll('.light').forEach(l => l.classList.remove('active'));
|
||||
document.getElementById(`light${playerNum}`).classList.add('active');
|
||||
|
||||
buzzedPlayer.textContent = `Player ${playerNum} buzzed in!`;
|
||||
|
||||
// Show grading buttons
|
||||
markCorrectBtn.style.display = 'inline-block';
|
||||
markIncorrectBtn.style.display = 'inline-block';
|
||||
showAnswerBtn.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
// Update player answer
|
||||
if (state.playerAnswer) {
|
||||
playerAnswer.textContent = state.playerAnswer.answer;
|
||||
answerSection.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch new question
|
||||
newQuestionBtn.addEventListener('click', async () => {
|
||||
const categories = Array.from(document.querySelectorAll('.category-checkbox:checked'))
|
||||
.map(cb => cb.value);
|
||||
|
||||
try {
|
||||
const response = await fetch(API_URL, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ categories })
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
currentQuestion = data;
|
||||
|
||||
questionText.textContent = currentQuestion.tossup_question;
|
||||
questionCategory.textContent = currentQuestion.category;
|
||||
correctAnswer.textContent = currentQuestion.tossup_answer;
|
||||
questionNum.textContent = currentQuestionNumber;
|
||||
|
||||
// Update Firebase
|
||||
database.ref('gameState').update({
|
||||
currentQuestion: currentQuestion,
|
||||
buzzerActive: false,
|
||||
buzzer: null,
|
||||
playerAnswer: null,
|
||||
questionNumber: currentQuestionNumber
|
||||
});
|
||||
|
||||
// Reset UI
|
||||
answerSection.style.display = 'none';
|
||||
buzzedPlayer.textContent = '';
|
||||
document.querySelectorAll('.light').forEach(l => l.classList.remove('active'));
|
||||
markCorrectBtn.style.display = 'none';
|
||||
markIncorrectBtn.style.display = 'none';
|
||||
showAnswerBtn.style.display = 'none';
|
||||
|
||||
activateBuzzerBtn.disabled = false;
|
||||
currentQuestionNumber++;
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching question:', error);
|
||||
alert('Error loading question. Check console.');
|
||||
}
|
||||
});
|
||||
|
||||
// Activate buzzers
|
||||
activateBuzzerBtn.addEventListener('click', () => {
|
||||
database.ref('gameState').update({
|
||||
buzzerActive: true,
|
||||
buzzer: null,
|
||||
playerAnswer: null
|
||||
});
|
||||
activateBuzzerBtn.disabled = true;
|
||||
answerSection.style.display = 'none';
|
||||
});
|
||||
|
||||
// Mark correct
|
||||
markCorrectBtn.addEventListener('click', () => {
|
||||
if (!currentBuzzedPlayer) return;
|
||||
|
||||
database.ref(`gameState/scores/${currentBuzzedPlayer}`).transaction((score) => {
|
||||
return (score || 0) + 4;
|
||||
});
|
||||
|
||||
resetForNextQuestion();
|
||||
});
|
||||
|
||||
// Mark incorrect
|
||||
markIncorrectBtn.addEventListener('click', () => {
|
||||
if (!currentBuzzedPlayer) return;
|
||||
|
||||
database.ref(`gameState/scores/${currentBuzzedPlayer}`).transaction((score) => {
|
||||
return (score || 0) - 4;
|
||||
});
|
||||
|
||||
// Reactivate buzzers for other players
|
||||
database.ref('gameState').update({
|
||||
buzzerActive: true,
|
||||
buzzer: null,
|
||||
playerAnswer: null
|
||||
});
|
||||
|
||||
answerSection.style.display = 'none';
|
||||
markCorrectBtn.style.display = 'none';
|
||||
markIncorrectBtn.style.display = 'none';
|
||||
showAnswerBtn.style.display = 'none';
|
||||
});
|
||||
|
||||
// Show answer
|
||||
showAnswerBtn.addEventListener('click', () => {
|
||||
answerSection.style.display = 'block';
|
||||
});
|
||||
|
||||
// Reset game
|
||||
resetGameBtn.addEventListener('click', () => {
|
||||
if (confirm('Reset all scores and start over?')) {
|
||||
currentQuestionNumber = 1;
|
||||
database.ref('gameState').set({
|
||||
buzzerActive: false,
|
||||
currentQuestion: null,
|
||||
scores: {
|
||||
player1: 0,
|
||||
player2: 0,
|
||||
player3: 0,
|
||||
player4: 0
|
||||
},
|
||||
questionNumber: 1
|
||||
});
|
||||
questionText.textContent = 'Click "New Question" to start';
|
||||
answerSection.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
function resetForNextQuestion() {
|
||||
database.ref('gameState').update({
|
||||
buzzerActive: false,
|
||||
buzzer: null,
|
||||
playerAnswer: null
|
||||
});
|
||||
|
||||
buzzedPlayer.textContent = '';
|
||||
document.querySelectorAll('.light').forEach(l => l.classList.remove('active'));
|
||||
markCorrectBtn.style.display = 'none';
|
||||
markIncorrectBtn.style.display = 'none';
|
||||
showAnswerBtn.style.display = 'none';
|
||||
answerSection.style.display = 'none';
|
||||
currentBuzzedPlayer = null;
|
||||
}
|
||||
47
index.html
Normal file
47
index.html
Normal file
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Science Bowl Buzzer System</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Science Bowl Buzzer System</h1>
|
||||
|
||||
<div class="role-selection">
|
||||
<h2>Select Your Role:</h2>
|
||||
|
||||
<a href="display.html" class="role-btn moderator-btn">
|
||||
<h3>📺 Moderator Display</h3>
|
||||
<p>Main screen showing questions and scores</p>
|
||||
</a>
|
||||
|
||||
<div class="player-buttons">
|
||||
<a href="player.html?id=1" class="role-btn player-btn player-1">
|
||||
<h3>🔴 Player 1</h3>
|
||||
</a>
|
||||
<a href="player.html?id=2" class="role-btn player-btn player-2">
|
||||
<h3>🔵 Player 2</h3>
|
||||
</a>
|
||||
<a href="player.html?id=3" class="role-btn player-btn player-3">
|
||||
<h3>🟢 Player 3</h3>
|
||||
</a>
|
||||
<a href="player.html?id=4" class="role-btn player-btn player-4">
|
||||
<h3>🟡 Player 4</h3>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="instructions">
|
||||
<h3>Setup Instructions:</h3>
|
||||
<ol>
|
||||
<li>Open the Moderator Display on your main screen</li>
|
||||
<li>Each player opens their assigned player page on their laptop</li>
|
||||
<li>Press spacebar or click the buzzer to buzz in!</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
42
player.html
Normal file
42
player.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Science Bowl - Player</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body class="player-body">
|
||||
<div class="player-container">
|
||||
<div class="player-header">
|
||||
<h1 id="playerName">Player</h1>
|
||||
<div class="score-display">
|
||||
Score: <span id="playerScore">0</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status-message" id="statusMessage">
|
||||
Waiting for question...
|
||||
</div>
|
||||
|
||||
<div class="buzzer-container">
|
||||
<button id="buzzer" class="buzzer-btn">
|
||||
<span class="buzzer-text">BUZZ</span>
|
||||
<span class="buzzer-hint">Press SPACE</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="answer-container" id="answerContainer" style="display: none;">
|
||||
<input type="text" id="answerInput" placeholder="Type your answer...">
|
||||
<button id="submitAnswer" class="submit-btn">Submit Answer</button>
|
||||
</div>
|
||||
|
||||
<div class="question-display" id="questionDisplay"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://www.gstatic.com/firebasejs/9.17.1/firebase-app-compat.js"></script>
|
||||
<script src="https://www.gstatic.com/firebasejs/9.17.1/firebase-database-compat.js"></script>
|
||||
<script src="config.js"></script>
|
||||
<script src="player.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
110
player.js
Normal file
110
player.js
Normal file
@@ -0,0 +1,110 @@
|
||||
// Get player ID from URL
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const playerId = urlParams.get('id') || '1';
|
||||
const playerColors = ['#ff4444', '#4444ff', '#44ff44', '#ffff44'];
|
||||
const playerNames = ['Player 1', 'Player 2', 'Player 3', 'Player 4'];
|
||||
|
||||
// DOM elements
|
||||
const playerName = document.getElementById('playerName');
|
||||
const playerScore = document.getElementById('playerScore');
|
||||
const statusMessage = document.getElementById('statusMessage');
|
||||
const buzzer = document.getElementById('buzzer');
|
||||
const answerContainer = document.getElementById('answerContainer');
|
||||
const answerInput = document.getElementById('answerInput');
|
||||
const submitAnswer = document.getElementById('submitAnswer');
|
||||
const questionDisplay = document.getElementById('questionDisplay');
|
||||
|
||||
// Set player identity
|
||||
playerName.textContent = playerNames[playerId - 1];
|
||||
playerName.style.color = playerColors[playerId - 1];
|
||||
document.body.style.setProperty('--player-color', playerColors[playerId - 1]);
|
||||
|
||||
// Game state
|
||||
let canBuzz = false;
|
||||
let hasBuzzed = false;
|
||||
|
||||
// Listen to game state
|
||||
database.ref('gameState').on('value', (snapshot) => {
|
||||
const state = snapshot.val();
|
||||
if (!state) return;
|
||||
|
||||
// Update score
|
||||
if (state.scores && state.scores[`player${playerId}`] !== undefined) {
|
||||
playerScore.textContent = state.scores[`player${playerId}`];
|
||||
}
|
||||
|
||||
// Update question
|
||||
if (state.currentQuestion) {
|
||||
questionDisplay.textContent = state.currentQuestion.tossup_question;
|
||||
}
|
||||
|
||||
// Update buzzer state
|
||||
if (state.buzzerActive) {
|
||||
canBuzz = true;
|
||||
hasBuzzed = false;
|
||||
buzzer.disabled = false;
|
||||
buzzer.classList.remove('locked');
|
||||
statusMessage.textContent = 'Ready to buzz!';
|
||||
answerContainer.style.display = 'none';
|
||||
} else {
|
||||
canBuzz = false;
|
||||
buzzer.disabled = true;
|
||||
buzzer.classList.add('locked');
|
||||
}
|
||||
|
||||
// Check if this player buzzed in
|
||||
if (state.buzzer && state.buzzer.playerId === `player${playerId}`) {
|
||||
statusMessage.textContent = 'You buzzed in! Answer now:';
|
||||
answerContainer.style.display = 'block';
|
||||
answerInput.focus();
|
||||
} else if (state.buzzer && state.buzzer.playerId) {
|
||||
statusMessage.textContent = `${state.buzzer.playerId} buzzed in`;
|
||||
buzzer.classList.add('locked');
|
||||
}
|
||||
});
|
||||
|
||||
// Buzzer click
|
||||
buzzer.addEventListener('click', buzzIn);
|
||||
|
||||
// Spacebar to buzz
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Space' && canBuzz && !hasBuzzed) {
|
||||
e.preventDefault();
|
||||
buzzIn();
|
||||
}
|
||||
});
|
||||
|
||||
function buzzIn() {
|
||||
if (!canBuzz || hasBuzzed) return;
|
||||
|
||||
hasBuzzed = true;
|
||||
database.ref('gameState/buzzer').set({
|
||||
playerId: `player${playerId}`,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
// Visual feedback
|
||||
buzzer.classList.add('buzzed');
|
||||
setTimeout(() => buzzer.classList.remove('buzzed'), 300);
|
||||
}
|
||||
|
||||
// Submit answer
|
||||
submitAnswer.addEventListener('click', submitPlayerAnswer);
|
||||
answerInput.addEventListener('keypress', (e) => {
|
||||
if (e.key === 'Enter') submitPlayerAnswer();
|
||||
});
|
||||
|
||||
function submitPlayerAnswer() {
|
||||
const answer = answerInput.value.trim();
|
||||
if (!answer) return;
|
||||
|
||||
database.ref('gameState/playerAnswer').set({
|
||||
playerId: `player${playerId}`,
|
||||
answer: answer,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
answerInput.value = '';
|
||||
answerContainer.style.display = 'none';
|
||||
statusMessage.textContent = 'Answer submitted! Waiting for moderator...';
|
||||
}
|
||||
375
styles.css
Normal file
375
styles.css
Normal file
@@ -0,0 +1,375 @@
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.role-selection {
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.role-btn {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
margin: 15px 0;
|
||||
background: #f8f9fa;
|
||||
border: 3px solid #dee2e6;
|
||||
border-radius: 10px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.role-btn:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
|
||||
}
|
||||
|
||||
.moderator-btn {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.player-buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 15px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.player-1 { border-color: #ff4444; }
|
||||
.player-2 { border-color: #4444ff; }
|
||||
.player-3 { border-color: #44ff44; }
|
||||
.player-4 { border-color: #ffff44; }
|
||||
|
||||
/* Player Screen */
|
||||
.player-body {
|
||||
background: var(--player-color, #667eea);
|
||||
}
|
||||
|
||||
.player-container {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.player-header {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.score-display {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: var(--player-color);
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.buzzer-container {
|
||||
margin: 50px 0;
|
||||
}
|
||||
|
||||
.buzzer-btn {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
border-radius: 50%;
|
||||
border: 10px solid white;
|
||||
background: linear-gradient(135deg, #ff6b6b, #ee5a6f);
|
||||
color: white;
|
||||
font-size: 3em;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.buzzer-btn:hover:not(:disabled) {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 25px 50px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.buzzer-btn:active:not(:disabled) {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.buzzer-btn.buzzed {
|
||||
animation: buzz 0.3s;
|
||||
}
|
||||
|
||||
@keyframes buzz {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.1); }
|
||||
}
|
||||
|
||||
.buzzer-btn.locked {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.buzzer-hint {
|
||||
display: block;
|
||||
font-size: 0.3em;
|
||||
margin-top: 10px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.answer-container {
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
#answerInput {
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
font-size: 1.2em;
|
||||
border: 3px solid var(--player-color);
|
||||
border-radius: 10px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding: 15px 40px;
|
||||
font-size: 1.2em;
|
||||
background: var(--player-color);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.submit-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.question-display {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-top: 30px;
|
||||
font-size: 1.1em;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* Display Screen */
|
||||
.display-body {
|
||||
background: #1a1a2e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.display-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.scoreboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.player-score {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
text-align: center;
|
||||
border: 3px solid;
|
||||
}
|
||||
|
||||
.player-score.player-1 { border-color: #ff4444; }
|
||||
.player-score.player-2 { border-color: #4444ff; }
|
||||
.player-score.player-3 { border-color: #44ff44; }
|
||||
.player-score.player-4 { border-color: #ffff44; }
|
||||
|
||||
.score {
|
||||
font-size: 3em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.buzzer-indicator {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.buzzer-lights {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 30px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.light {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.2);
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.light.player-1 { border: 5px solid #ff4444; }
|
||||
.light.player-2 { border: 5px solid #4444ff; }
|
||||
.light.player-3 { border: 5px solid #44ff44; }
|
||||
.light.player-4 { border: 5px solid #ffff44; }
|
||||
|
||||
.light.active {
|
||||
animation: pulse 1s infinite;
|
||||
box-shadow: 0 0 30px currentColor;
|
||||
}
|
||||
|
||||
.light.player-1.active { background: #ff4444; }
|
||||
.light.player-2.active { background: #4444ff; }
|
||||
.light.player-3.active { background: #44ff44; }
|
||||
.light.player-4.active { background: #ffff44; }
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.8; }
|
||||
}
|
||||
|
||||
.buzzed-player {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
color: #ffff44;
|
||||
}
|
||||
|
||||
.question-section {
|
||||
background: white;
|
||||
color: #333;
|
||||
padding: 40px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
.question-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
font-weight: bold;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.question-text {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.answer-section {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 30px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.player-answer, .correct-answer {
|
||||
background: rgba(255,255,255,0.2);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
font-size: 1.3em;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.control-btn {
|
||||
padding: 15px 30px;
|
||||
font-size: 1.1em;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.control-btn:hover:not(:disabled) {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
|
||||
}
|
||||
.control-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.primary { background: #667eea; color: white; }
|
||||
.success { background: #44ff44; color: #333; }
|
||||
.danger { background: #ff4444; color: white; }
|
||||
.warning { background: #ffaa00; color: #333; }
|
||||
.category-filter {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.category-filter label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.instructions {
|
||||
margin-top: 40px;
|
||||
padding: 20px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.instructions ol {
|
||||
margin-left: 20px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.instructions li {
|
||||
margin: 10px 0;
|
||||
}
|
||||
Reference in New Issue
Block a user