ExamTimer / index.html
SolarumAsteridion's picture
Update index.html
56fd759 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Neural Exam Interface</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;700&display=swap" rel="stylesheet">
<style>
:root {
--metal: #050506;
--brass: #c5a059;
--etch: #1a1a1c;
--glow: rgba(197, 160, 89, 0.15);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
/* Added a slight radial gradient for depth */
background: radial-gradient(circle at center, #161618 0%, #050506 100%);
color: #fff;
font-family: 'Space Grotesk', sans-serif;
min-height: 100vh;
padding: 40px;
overflow-x: hidden;
}
/* Subtle dot overlay remains for texture */
body::before {
content: "";
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background-image: radial-gradient(circle at 2px 2px, rgba(255,255,255,0.03) 1px, transparent 0);
background-size: 40px 40px;
z-index: -1;
pointer-events: none;
}
.container { width: 100%; max-width: 100%; }
header {
border-bottom: 1px solid var(--etch);
padding-bottom: 30px;
margin-bottom: 60px;
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
font-weight: 300;
letter-spacing: 0.5em;
text-transform: uppercase;
color: var(--brass);
font-size: 1.1rem;
text-shadow: 0 0 10px var(--glow);
}
.add-btn {
background: none;
border: 1px solid var(--brass);
color: var(--brass);
padding: 12px 30px;
cursor: pointer;
font-family: inherit;
font-size: 0.75rem;
transition: 0.3s;
letter-spacing: 3px;
text-transform: uppercase;
}
.add-btn:hover {
background: var(--brass);
color: #000;
box-shadow: 0 0 20px var(--glow);
}
/* Grid with high spacing */
.exams-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
gap: 80px;
width: 100%;
perspective: 1000px;
}
/* 4x3 Rectangular Card */
.brushed-plate {
position: relative;
background: rgba(13, 13, 15, 0.8);
border: 1px solid var(--etch);
padding: 45px;
/* Smoother transition for a "lil bit" of movement */
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94), border-color 0.3s, box-shadow 0.3s;
cursor: pointer;
aspect-ratio: 4 / 3;
display: flex;
flex-direction: column;
justify-content: space-between;
transform-style: preserve-3d;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
.brushed-plate:hover {
border-color: var(--brass);
box-shadow: 0 15px 40px rgba(0,0,0,0.7), 0 0 10px var(--glow);
}
/* Flat content (No float) */
.card-content {
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
pointer-events: none;
}
.card-header {
font-weight: 700;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--brass);
font-size: 0.9rem;
border-left: 2px solid var(--brass);
padding-left: 15px;
}
.digit-flipper {
font-size: clamp(6rem, 14vw, 10rem);
font-weight: 700;
line-height: 0.8;
color: #fff;
margin: 20px 0;
}
.label-text {
font-size: 0.8rem;
letter-spacing: 0.6em;
opacity: 0.4;
text-transform: uppercase;
}
footer {
font-family: monospace;
opacity: 0.8;
letter-spacing: 3px;
font-size: 1.2rem;
text-transform: uppercase;
color: var(--brass);
}
.del {
position: absolute; top: 20px; right: 20px;
color: #333; background: none; border: none;
cursor: pointer; font-size: 1.5rem; transition: 0.3s;
z-index: 10;
}
.del:hover { color: #ff4b2b; }
/* Modals */
.modal {
display: none; position: fixed; inset: 0;
background: rgba(0,0,0,0.95); z-index: 100;
align-items: center; justify-content: center; padding: 20px;
backdrop-filter: blur(10px);
}
.modal.active { display: flex; }
.modal-box {
background: #0d0d0f; border: 1px solid var(--brass);
padding: 60px; width: 100%; max-width: 600px;
}
input, textarea {
width: 100%; background: #000; border: 1px solid var(--etch);
padding: 20px; color: #fff; font-family: inherit;
margin: 20px 0; outline: none; font-size: 1rem;
}
input:focus { border-color: var(--brass); }
.action-btn {
background: var(--brass); color: #000; border: none;
padding: 20px; width: 100%; font-weight: 700;
cursor: pointer; text-transform: uppercase; letter-spacing: 4px;
}
@media (max-width: 700px) {
.exams-grid { grid-template-columns: 1fr; gap: 40px; }
.brushed-plate { aspect-ratio: auto; min-height: 400px; }
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>NEURAL_EXAM_LINK_v4.2</h1>
<button class="add-btn" id="openBtn">INITIALIZE_ENTRY</button>
</header>
<div id="examsGrid" class="exams-grid"></div>
</div>
<!-- Modals -->
<div class="modal" id="addModal">
<div class="modal-box">
<h2 style="color: var(--brass); letter-spacing: 5px; font-weight: 300; margin-bottom: 20px;">SYNC_NEW_NODE</h2>
<form id="addForm">
<input type="text" id="nameIn" placeholder="NODE_IDENTIFIER" required>
<input type="date" id="dateIn" required>
<button type="submit" class="action-btn">ESTABLISH_DATA_LINK</button>
</form>
</div>
</div>
<div class="modal" id="syllModal">
<div class="modal-box" style="max-width: 900px;">
<h2 id="syllTitle" style="color: var(--brass); font-weight: 300; letter-spacing: 2px;"></h2>
<form id="syllForm">
<textarea id="syllArea" rows="15" placeholder="ACCESSING_SYLLABUS_STREAM..."></textarea>
<input type="hidden" id="syllId">
<button type="submit" class="action-btn">COMMIT_MEMORY_UPDATE</button>
</form>
</div>
</div>
<script type="module">
import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js";
import { getDatabase, ref, onValue, push, remove, update } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-database.js";
const firebaseConfig = {
apiKey: "AIzaSyCBGYdGdPjYJiKsTMjVYZ9mf9F82ns7g4Q",
authDomain: "pikachu-rxppbp.firebaseapp.com",
databaseURL: "https://pikachu-rxppbp.firebaseio.com",
projectId: "pikachu-rxppbp",
storageBucket: "pikachu-rxppbp.appspot.com",
messagingSenderId: "241970333280",
appId: "1:241970333280:web:704e8930bd591c138d6505"
};
const app = initializeApp(firebaseConfig);
const database = getDatabase(app);
const examsRef = ref(database, 'exams');
let dataStore = [];
onValue(examsRef, (snapshot) => {
const data = snapshot.val();
dataStore = data ? Object.entries(data).map(([id, v]) => ({ id, ...v })) : [];
render();
});
function render() {
const grid = document.getElementById('examsGrid');
grid.innerHTML = dataStore.sort((a,b)=>a.date-b.date).map(e => {
const now = new Date(); now.setHours(0,0,0,0);
const target = new Date(e.date); target.setHours(0,0,0,0);
const diff = Math.ceil((target - now) / 86400000);
let val = diff < 0 ? 'OK' : diff;
let label = diff < 0 ? 'MISSION_COMPLETE' : (diff === 1 ? 'CYCLE_REMAINING' : 'CYCLES_REMAINING');
return `
<div class="brushed-plate" onmousemove="tilt(event, this)" onmouseleave="resetTilt(this)" onclick="showSyll('${e.id}')">
<button class="del" onclick="event.stopPropagation(); window.delEx('${e.id}')">×</button>
<div class="card-content">
<div class="card-header">${e.name}</div>
<div>
<div class="digit-flipper">${val}</div>
<div class="label-text">${label}</div>
</div>
<footer>${new Date(e.date).toLocaleDateString('en-US', {month: 'long', day: 'numeric', year: 'numeric'})}</footer>
</div>
</div>`;
}).join('');
}
// --- SUBTLE Tilt Logic ---
window.tilt = (e, card) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
// Increased divisor (50 instead of 15) for very minimal movement
const rotateX = (y - centerY) / 50;
const rotateY = (centerX - x) / 50;
card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
};
window.resetTilt = (card) => {
card.style.transform = `rotateX(0deg) rotateY(0deg)`;
};
window.delEx = (id) => { if(confirm("DELETE NODE ENTRY?")) remove(ref(database, `exams/${id}`)); };
window.showSyll = (id) => {
const e = dataStore.find(x => x.id === id);
document.getElementById('syllTitle').innerText = `NODE_STREAM: ${e.name}`;
document.getElementById('syllArea').value = e.syllabus || '';
document.getElementById('syllId').value = id;
document.getElementById('syllModal').classList.add('active');
};
document.getElementById('openBtn').onclick = () => document.getElementById('addModal').classList.add('active');
window.onclick = (e) => { if(e.target.classList.contains('modal')) e.target.classList.remove('active'); };
document.getElementById('addForm').onsubmit = (e) => {
e.preventDefault();
push(examsRef, {
name: document.getElementById('nameIn').value,
date: new Date(document.getElementById('dateIn').value).getTime(),
syllabus: ''
});
document.getElementById('addModal').classList.remove('active');
document.getElementById('addForm').reset();
};
document.getElementById('syllForm').onsubmit = (e) => {
e.preventDefault();
update(ref(database, `exams/${document.getElementById('syllId').value}`), {
syllabus: document.getElementById('syllArea').value
});
document.getElementById('syllModal').classList.remove('active');
};
</script>
</body>
</html>