From 3a28dea0a1443a1abc09543477c1456ccc96c371 Mon Sep 17 00:00:00 2001 From: Aryan Krishnan Date: Sun, 26 Apr 2026 00:06:20 +0530 Subject: [PATCH 1/2] Add projects page This PR adds the projects page to project hearthstone based on HTML, CSS JS. This code was generated by claude for fast completion of the website and based on design inspiration from the hearthstone members. Fixes: #41 #42 #29 --- projecthearthstone.in/Projects/index.html | 52 +++ projecthearthstone.in/Projects/script.js | 293 ++++++++++++ projecthearthstone.in/Projects/style.css | 523 ++++++++++++++++++++++ 3 files changed, 868 insertions(+) create mode 100644 projecthearthstone.in/Projects/index.html create mode 100644 projecthearthstone.in/Projects/script.js create mode 100644 projecthearthstone.in/Projects/style.css diff --git a/projecthearthstone.in/Projects/index.html b/projecthearthstone.in/Projects/index.html new file mode 100644 index 0000000..ca09a91 --- /dev/null +++ b/projecthearthstone.in/Projects/index.html @@ -0,0 +1,52 @@ + + + + + + Project Hearthstone - Projects + + + + + + + + + + + + +
+ + +
+
+

+ PROJECT HEARTHSTONE +

+

Bridging Worlds Through AI

+
+
+
+ + +
+ +
+
+ Project Preview +
+
+
+ + +
+
+ +
+
+
+ + + + diff --git a/projecthearthstone.in/Projects/script.js b/projecthearthstone.in/Projects/script.js new file mode 100644 index 0000000..acea5d9 --- /dev/null +++ b/projecthearthstone.in/Projects/script.js @@ -0,0 +1,293 @@ +// Hearthstone Projects - Interactive Logic + +// Project Data +const projects = [ + { + id: 2, + title: "Hearthstone AI", + shortDesc: "Our flagship AI model with 99.7% accuracy, supporting fingerspelling and speech-to-text across Python and JavaScript frameworks, optimized for Raspberry Pi.", + image: "../src/hearthstone-ai.jpg", + specs: [ + "Accuracy: 99.7%", + "Frameworks: Python, JavaScript", + "Platform: Raspberry Pi Optimized", + "Features: Fingerspelling + Speech-to-Text" + ], + techStack: ["TensorFlow", "MediaPipe", "Python", "JavaScript", "Raspberry Pi"], + description: "Hearthstone AI is the heart of our project — a high-accuracy model that understands both hand signs and spoken words. Built on TensorFlow and MediaPipe, it runs smoothly on a Raspberry Pi and works seamlessly in both Python backends and JavaScript frontends. We're actively developing word gesture recognition to move beyond isolated signs." + }, + { + id: 1, + title: "Hearthstone Glove", + shortDesc: "Early prototype using Arduino and flex sensors to translate ASL fingerspelling.", + image: "../src/hearthstone-glove.jpg", + specs: [ + "Hardware: Arduino Uno", + "Sensors: 5 Flex Sensors", + "Detection: Fingerspelling Only", + "Status: Legacy Prototype" + ], + techStack: ["Arduino C", "Flex Sensors", "Analog Circuits"], + description: "Our first iteration — a hardware-based glove that captured finger bends to recognize fingerspelling letters. Though it laid the foundation, we moved on to a more powerful AI-driven approach." + }, + { + id: 3, + title: "Hearthstone ASL Course", + shortDesc: "Gamified learning platform that teaches ASL through interactive lessons, progression tracking, and a complete course system built with HTML, CSS, and JavaScript.", + image: "../src/hearthstone-asl-course.jpg", + specs: [ + "Tech Stack: HTML/CSS/JS", + "Gamification: XP, Achievements, Levels", + "Course Structure: Modular", + "Progress Tracking: Yes" + ], + techStack: ["HTML5", "CSS3", "JavaScript", "Gamification"], + description: "Learning ASL should be fun. Our ASL Course platform turns education into a game — complete lessons, earn XP, unlock achievements, and track your progression as you build real signing skills. Built entirely with web technologies for maximum accessibility." + } +]; + +// Initialize on DOM Ready +document.addEventListener('DOMContentLoaded', () => { + initializeProgressBar(); + initializeProjects(); + initParticles(); // Add particle system +}); + +// ========== PROGRESS BAR ========== +function initializeProgressBar() { + const progressBar = document.getElementById('progressBar'); + if (!progressBar) return; + + window.addEventListener('scroll', () => { + const scrollTop = window.scrollY; + const docHeight = document.documentElement.scrollHeight - window.innerHeight; + const progress = (scrollTop / docHeight) * 100; + progressBar.style.width = `${progress}%`; + }); +} + +// ========== PROJECTS INITIALIZATION ========== +function initializeProjects() { + const track = document.getElementById('projectsTrack'); + if (!track) return; + + // Generate project panels + projects.forEach((project, index) => { + const panel = createProjectPanel(project, index); + track.appendChild(panel); + }); + + // Initialize Intersection Observer after a slight delay to ensure DOM is ready + setTimeout(() => { + initializeIntersectionObserver(); + }, 100); +} + +// Create individual project panel DOM +function createProjectPanel(project, index) { + const panel = document.createElement('div'); + panel.className = 'project-panel'; + panel.dataset.projectId = project.id; + + // Spec sheet items + const specsHtml = project.specs.map(spec => `
${spec}
`).join(''); + + // Tech pills + const techPillsHtml = project.techStack.map(tech => `${tech}`).join(''); + + panel.innerHTML = ` +

${project.title}

+

${project.description}

+ +
+ ${specsHtml} +
+ +
+ ${techPillsHtml} +
+ +
+ View Demo + GitHub +
+ `; + + return panel; +} + +// ========== INTERSECTION OBSERVER (Project Switch) ========== +function initializeIntersectionObserver() { + const panels = document.querySelectorAll('.project-panel'); + const stickyImage = document.getElementById('stickyImage'); + const imageGlow = document.querySelector('.image-glow'); + + if (panels.length === 0 || !stickyImage) return; + + // Set initial image + if (projects.length > 0) { + updateStickyImage(stickyImage, projects[0].image); + } + + const observerOptions = { + root: null, + rootMargin: '-30% 0px -30% 0px', + threshold: 0.1 + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + const projectId = parseInt(entry.target.dataset.projectId); + const project = projects.find(p => p.id === projectId); + + if (project) { + // Update active state + panels.forEach(p => p.classList.remove('active')); + entry.target.classList.add('active'); + + // Trigger glitch-scale transition on image + triggerImageTransition(stickyImage, project.image); + } + } + }); + }, observerOptions); + + panels.forEach(panel => observer.observe(panel)); +} + +// Update sticky image with glitch effect +function triggerImageTransition(imageElement, newSrc) { + imageElement.classList.remove('active'); + imageElement.classList.add('glitch-effect'); // Add glitch animation + + setTimeout(() => { + updateStickyImage(imageElement, newSrc); + imageElement.classList.remove('glitch-effect'); + imageElement.classList.add('active'); + }, 400); // Glitch duration 400ms +} + +// Helper: Update image src +function updateStickyImage(imageElement, src) { + // For now, use a gradient placeholder since images don't exist + // In production, this would set imageElement.src = src; + imageElement.style.background = generatePlaceholderGradient(); + imageElement.src = 'data:image/svg+xml,%3Csvg width="500" height="300" xmlns="http://www.w3.org/2000/svg"%3E%3Crect width="500" height="300" fill="%230a0a0a"/%3E%3Ctext x="50%25" y="50%25" font-family="Arial, sans-serif" font-size="24" fill="%2300D4FF" text-anchor="middle"%3EProject Preview%3C/text%3E%3C/svg%3E'; +} + +// Generate a cool placeholder gradient +function generatePlaceholderGradient() { + const gradients = [ + 'linear-gradient(135deg, #00D4FF 0%, #0066ff 100%)', + 'linear-gradient(135deg, #ff00d4 0%, #00D4FF 100%)', + 'linear-gradient(135deg, #00ffaa 0%, #00D4FF 100%)', + 'linear-gradient(135deg, #ff0066 0%, #ff00d4 100%)', + 'linear-gradient(135deg, #6600ff 0%, #ff00d4 100%)' + ]; + return gradients[Math.floor(Math.random() * gradients.length)]; +} + +// ========== NEURAL MESH PARTICLES ========== +function initParticles() { + const canvas = document.createElement('canvas'); + canvas.id = 'particleCanvas'; + canvas.style.position = 'fixed'; + canvas.style.top = '0'; + canvas.style.left = '0'; + canvas.style.width = '100%'; + canvas.style.height = '100%'; + canvas.style.zIndex = '-1'; + canvas.style.pointerEvents = 'none'; + document.body.appendChild(canvas); + + const ctx = canvas.getContext('2d'); + let width, height; + const particles = []; + const particleCount = 70; // 60-80 nodes + const connectionDistance = 120; + const mouseRepelDist = 150; + const mouse = { x: null, y: null }; + + function resize() { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + } + + window.addEventListener('resize', resize); + + window.addEventListener('mousemove', e => { + mouse.x = e.clientX; + mouse.y = e.clientY; + }); + + window.addEventListener('mouseout', () => { + mouse.x = null; + mouse.y = null; + }); + + // Initialize size BEFORE creating particles + resize(); + + // Create particles with valid positions + for (let i = 0; i < particleCount; i++) { + particles.push({ + x: Math.random() * width, + y: Math.random() * height, + vx: (Math.random() - 0.5) * 0.5, + vy: (Math.random() - 0.5) * 0.5, + size: Math.random() * 2 + 2 // 2-4px + }); + } + + function update() { + ctx.clearRect(0, 0, width, height); + ctx.strokeStyle = 'rgba(0, 212, 255, 0.4)'; // line opacity + ctx.fillStyle = 'rgba(0, 212, 255, 0.9)'; // particle opacity + + particles.forEach(p => { + p.x += p.vx; + p.y += p.vy; + + if (p.x < 0 || p.x > width) p.vx *= -1; + if (p.y < 0 || p.y > height) p.vy *= -1; + + if (mouse.x != null) { + const dx = p.x - mouse.x; + const dy = p.y - mouse.y; + const dist = Math.sqrt(dx*dx + dy*dy); + if (dist < mouseRepelDist) { + const force = (mouseRepelDist - dist) / mouseRepelDist; + p.x += (dx / dist) * force * 5; + p.y += (dy / dist) * force * 5; + } + } + + ctx.beginPath(); + ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + ctx.fill(); + }); + + // Draw connections + for (let i = 0; i < particles.length; i++) { + for (let j = i+1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const dist = Math.sqrt(dx*dx + dy*dy); + if (dist < connectionDistance) { + ctx.lineWidth = 1 - dist/connectionDistance; + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + ctx.stroke(); + } + } + } + + requestAnimationFrame(update); + } + + update(); +} diff --git a/projecthearthstone.in/Projects/style.css b/projecthearthstone.in/Projects/style.css new file mode 100644 index 0000000..5a1c04c --- /dev/null +++ b/projecthearthstone.in/Projects/style.css @@ -0,0 +1,523 @@ +/* Hearthstone Projects - Premium AI Experience */ + +:root { + --bg-primary: #020204; /* Deep Obsidian */ + --accent-blue: #00D4FF; /* Electric Cyan */ + --signal-green: #00FF41; /* Phosphor-Green */ + --text-primary: #ffffff; + --text-secondary: #a0a0a0; + --border-color: #1a1a1a; + --accent-glow: rgba(0, 212, 255, 0.4); + --signal-glow: rgba(0, 255, 65, 0.4); +} + +body { + background-color: var(--bg-primary); + color: var(--text-primary); + overflow-x: hidden; + font-family: 'Montserrat', sans-serif; +} + +/* ========== PROGRESS BAR ========== */ +.progress-bar { + position: fixed; + top: 0; + left: 0; + height: 3px; + width: 0%; + background: linear-gradient(90deg, var(--accent-blue), #00ffaa); + z-index: 2000; + transition: width 0.1s ease-out; + box-shadow: 0 0 15px var(--accent-blue); +} + +/* ========== HERO SECTION ========== */ +.hero { + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; +} + +.hero-content { + text-align: center; + z-index: 1; +} + +.hero-title { + font-size: clamp(2.5rem, 8vw, 7rem); + font-weight: 900; + letter-spacing: 0.1em; + margin-bottom: 1rem; + font-family: 'Orbitron', sans-serif; +} + +.shimmer { + background: linear-gradient( + 90deg, + var(--accent-blue) 0%, + #ffffff 25%, + var(--accent-blue) 50%, + #ffffff 75%, + var(--accent-blue) 100% + ); + background-size: 200% auto; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + animation: shimmerAnim 4s linear infinite; +} + +@keyframes shimmerAnim { + 0% { background-position: 200% center; } + 100% { background-position: -200% center; } +} + +.hero-subtitle { + font-size: clamp(1rem, 2.5vw, 1.5rem); + color: var(--text-secondary); + letter-spacing: 0.3em; + text-transform: uppercase; + margin-bottom: 2rem; +} + +.hero-divider { + width: 200px; + height: 2px; + background: linear-gradient(90deg, transparent, var(--accent-blue), transparent); + margin: 0 auto; +} + +/* ========== PROJECTS SPLIT VIEW ========== */ +.projects-container { + display: grid; + grid-template-columns: 1fr 1fr; + min-height: 100vh; + position: relative; + background: linear-gradient(to bottom, rgba(0, 10, 30, 0) 0%, rgba(0, 10, 30, 0.5) 20%, rgba(0, 10, 30, 0.5) 100%); +} + +.image-column { + position: relative; + height: 100vh; + position: sticky; + top: 0; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, rgba(0, 212, 255, 0.05) 0%, transparent 50%); + border-right: 1px solid var(--border-color); + z-index: 10; +} + +.sticky-image-wrapper { + position: relative; + width: 80%; + max-width: 500px; + aspect-ratio: 16/9; + display: flex; + align-items: center; + justify-content: center; +} + +.sticky-image { + width: 100%; + height: 100%; + object-fit: cover; + border-radius: 12px; + box-shadow: 0 0 40px rgba(0, 212, 255, 0.3); + transition: filter 0.3s ease, transform 0.3s ease; +} + +.sticky-image.active { + box-shadow: 0 0 50px var(--accent-glow); +} + +.image-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 120%; + height: 120%; + background: radial-gradient(ellipse, var(--accent-glow) 0%, transparent 70%); + opacity: 0.6; + filter: blur(30px); + z-index: -1; + animation: glowPulse 3s ease-in-out infinite; +} + +@keyframes glowPulse { + 0%, 100% { opacity: 0.4; transform: translate(-50%, -50%) scale(1); } + 50% { opacity: 0.7; transform: translate(-50%, -50%) scale(1.1); } +} + +/* Glitch-Scale Transition */ +.glitch-target { + filter: blur(10px); + transform: scale(0.95); + opacity: 0.8; +} + +.glitch-target.active { + filter: blur(0); + transform: scale(1.0); + opacity: 1; + transition: filter 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); +} + +/* Glitch effect for data swap */ +.glitch-effect { + animation: glitch 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) both; +} + +@keyframes glitch { + 0% { + clip-path: inset(5% 0 85% 0); + transform: translateX(-2px); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + } + 20% { + clip-path: inset(25% 0 60% 0); + transform: translateX(2px); + filter: contrast(150%) drop-shadow(2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(-2px 0 rgba(255, 0, 212, 0.8)); + } + 40% { + clip-path: inset(45% 0 40% 0); + transform: translateX(-1px); + filter: contrast(150%) drop-shadow(-1px 0 rgba(0, 212, 255, 0.9)) drop-shadow(1px 0 rgba(255, 0, 212, 0.9)); + } + 60% { + clip-path: inset(55% 0 30% 0); + transform: translateX(1px); + filter: contrast(150%) drop-shadow(1px 0 rgba(0, 212, 255, 0.7)) drop-shadow(-1px 0 rgba(255, 0, 212, 0.7)); + } + 80% { + clip-path: inset(65% 0 20% 0); + transform: translateX(-2px); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + } + 100% { + clip-path: inset(0 0 0 0); + transform: translateX(0); + filter: contrast(100%); + } +} + +/* ========== CONTENT COLUMN ========== */ +.content-column { + padding: 4rem 3rem 6rem; + background: transparent; + backdrop-filter: blur(20px); + display: flex; + flex-direction: column; + justify-content: flex-start; +} + +.projects-track { + display: flex; + flex-direction: column; + gap: 6rem; +} + +.project-panel { + min-height: 80vh; + display: flex; + flex-direction: column; + justify-content: center; + opacity: 0.3; + transition: opacity 0.5s ease, transform 0.5s ease; + transform: translateY(30px); +} + +.project-panel.active { + opacity: 1; + transform: translateY(0); +} + +/* Header */ +.project-panel.active h2 { + opacity: 0; + transform: translateY(20px); + animation: fadeInUp 0.6s ease forwards, breathe 4s ease-in-out infinite 0.6s; + color: var(--accent-blue); + letter-spacing: 0.02em; + margin-bottom: 1rem; +} + +.project-panel.active h2::after { + content: ''; + display: block; + width: 60px; + height: 3px; + background: var(--signal-green); + margin: 0.5rem 0 0; + border-radius: 2px; + box-shadow: 0 0 10px var(--signal-green); +} + +@keyframes fadeInUp { + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes breathe { + 0%, 100% { letter-spacing: 0.02em; } + 50% { letter-spacing: 0.05em; } +} + +/* Description */ +.project-panel .description { + font-size: 1.1rem; + line-height: 1.7; + color: var(--text-secondary); + margin-bottom: 2rem; + max-width: 600px; + opacity: 0; + transform: translateY(10px); + animation: fadeInUp 0.6s ease forwards 0.2s; +} + +/* Spec sheet - glass effect */ +.spec-sheet { + margin: 2rem 0; + padding: 1.5rem; + background: rgba(255, 255, 255, 0.03); + backdrop-filter: blur(12px); + border-radius: 0 8px 8px 0; + border-left: 2px solid var(--signal-green); + position: relative; + overflow: hidden; + box-shadow: inset 0 0 0 1px rgba(0, 212, 255, 0.2), 0 0 15px rgba(0, 212, 255, 0.05); +} + +.spec-item { + font-family: 'Courier New', monospace; + font-size: 0.95rem; + color: var(--text-secondary); + margin: 0.75rem 0; + padding-left: 1.5rem; + position: relative; + opacity: 0; + transform: translateX(-10px); +} + +/* Staggered slide-in from right */ +.project-panel.active .spec-item { + animation: slideInRight 0.4s ease forwards; +} + +.project-panel.active .spec-item:nth-child(1) { animation-delay: 0.3s; } +.project-panel.active .spec-item:nth-child(2) { animation-delay: 0.4s; } +.project-panel.active .spec-item:nth-child(3) { animation-delay: 0.5s; } +.project-panel.active .spec-item:nth-child(4) { animation-delay: 0.6s; } + +@keyframes slideInRight { + to { + opacity: 1; + transform: translateX(0); + } +} + +.spec-item::before { + content: '>'; + position: absolute; + left: 0; + color: var(--signal-green); + font-weight: bold; + animation: caretBlink 1.5s infinite; +} + +@keyframes caretBlink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +/* Tech stack pills */ +.tech-stack { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin: 1.5rem 0; +} + +.tech-pill { + padding: 0.5rem 1rem; + background: transparent; + border: 1px solid var(--accent-blue); + border-radius: 999px; + font-size: 0.85rem; + color: var(--accent-blue); + font-weight: 500; + position: relative; + overflow: hidden; + transition: all 0.3s ease; + cursor: default; +} + +/* Border-trace effect */ +.tech-pill::before { + content: ''; + position: absolute; + inset: 0; + padding: 1px; + border-radius: inherit; + background: conic-gradient( + from 0deg, + transparent 0deg, + var(--signal-green) 20deg, + transparent 40deg, + transparent 320deg, + var(--signal-green) 340deg, + transparent 360deg + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.3s; + animation: rotate 3s linear infinite; + pointer-events: none; +} + +.tech-pill:hover::before { + opacity: 1; +} + +@keyframes rotate { + to { transform: rotate(360deg); } +} + +.tech-pill:hover { + box-shadow: 0 0 20px var(--accent-glow), inset 0 0 10px rgba(0, 212, 255, 0.2); + transform: translateY(-2px); +} + +/* Buttons */ +.button-group { + display: flex; + gap: 1rem; + margin-top: 2rem; +} + +.project-btn { + padding: 0.875rem 2rem; + font-size: 1rem; + font-weight: 600; + border: none; + border-radius: 8px; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); + text-decoration: none; + opacity: 0; + transform: translateY(20px); +} + +.project-panel.active .project-btn { + animation: springIn 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) forwards; +} + +.project-btn:nth-child(2) { + animation-delay: 0.3s !important; +} + +@keyframes springIn { + 0% { opacity: 0; transform: translateY(20px) scale(0.9); } + 60% { opacity: 1; transform: translateY(-5px) scale(1.05); } + 100% { opacity: 1; transform: translateY(0) scale(1); } +} + +.btn-primary { + background: var(--accent-blue); + color: #000; +} + +.btn-primary:hover { + background: #00ffe0; + box-shadow: 0 0 30px var(--accent-glow); + transform: translateY(-3px); +} + +.btn-secondary { + background: transparent; + border: 2px solid var(--accent-blue); + color: var(--accent-blue); +} + +.btn-secondary:hover { + background: rgba(0, 212, 255, 0.1); + box-shadow: 0 0 20px var(--accent-glow); + transform: translateY(-3px); +} + +/* GitHub button border pulse when panel active */ +.project-panel.active .btn-secondary { + animation: borderPulse 2s ease-in-out infinite 0.6s; +} + +@keyframes borderPulse { + 0%, 100% { + box-shadow: 0 0 5px var(--signal-glow); + border-color: var(--signal-green); + } + 50% { + box-shadow: 0 0 20px var(--signal-glow); + border-color: #00ff88; + } +} + +/* Responsive */ +@media (max-width: 1024px) { + .projects-container { + grid-template-columns: 1fr; + } + + .image-column { + height: 50vh; + position: relative; + top: 0; + border-right: none; + border-bottom: 1px solid var(--border-color); + } + + .sticky-image-wrapper { + width: 90%; + } + + .content-column { + padding: 2rem 1.5rem 4rem; + } +} + +@media (max-width: 600px) { + .hero-title { + letter-spacing: 0.05em; + } + + .button-group { + flex-direction: column; + } + + .project-btn { + width: 100%; + text-align: center; + } +} + +/* Particle canvas */ +#particleCanvas { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + opacity: 0.4; + pointer-events: none; +} From 4515c1944c1f5c23466ecd63b2c9e8157a49559b Mon Sep 17 00:00:00 2001 From: Aryan Krishnan Date: Sun, 26 Apr 2026 00:09:12 +0530 Subject: [PATCH 2/2] Prettier --- projecthearthstone.in/Projects/index.html | 70 ++-- projecthearthstone.in/Projects/script.js | 470 +++++++++++----------- projecthearthstone.in/Projects/style.css | 154 +++++-- 3 files changed, 394 insertions(+), 300 deletions(-) diff --git a/projecthearthstone.in/Projects/index.html b/projecthearthstone.in/Projects/index.html index ca09a91..4ddd083 100644 --- a/projecthearthstone.in/Projects/index.html +++ b/projecthearthstone.in/Projects/index.html @@ -1,17 +1,20 @@ - + - - - + + + Project Hearthstone - Projects - - - - - - - - + + + + + + + + @@ -20,33 +23,38 @@
-
-

- PROJECT HEARTHSTONE -

-

Bridging Worlds Through AI

-
-
+
+

+ PROJECT HEARTHSTONE +

+

Bridging Worlds Through AI

+
+
- -
-
- Project Preview -
-
+ +
+
+ Project Preview +
+
- -
-
- -
+ +
+
+
+
- + diff --git a/projecthearthstone.in/Projects/script.js b/projecthearthstone.in/Projects/script.js index acea5d9..45a301b 100644 --- a/projecthearthstone.in/Projects/script.js +++ b/projecthearthstone.in/Projects/script.js @@ -1,101 +1,116 @@ -// Hearthstone Projects - Interactive Logic - // Project Data const projects = [ - { - id: 2, - title: "Hearthstone AI", - shortDesc: "Our flagship AI model with 99.7% accuracy, supporting fingerspelling and speech-to-text across Python and JavaScript frameworks, optimized for Raspberry Pi.", - image: "../src/hearthstone-ai.jpg", - specs: [ - "Accuracy: 99.7%", - "Frameworks: Python, JavaScript", - "Platform: Raspberry Pi Optimized", - "Features: Fingerspelling + Speech-to-Text" - ], - techStack: ["TensorFlow", "MediaPipe", "Python", "JavaScript", "Raspberry Pi"], - description: "Hearthstone AI is the heart of our project — a high-accuracy model that understands both hand signs and spoken words. Built on TensorFlow and MediaPipe, it runs smoothly on a Raspberry Pi and works seamlessly in both Python backends and JavaScript frontends. We're actively developing word gesture recognition to move beyond isolated signs." - }, - { - id: 1, - title: "Hearthstone Glove", - shortDesc: "Early prototype using Arduino and flex sensors to translate ASL fingerspelling.", - image: "../src/hearthstone-glove.jpg", - specs: [ - "Hardware: Arduino Uno", - "Sensors: 5 Flex Sensors", - "Detection: Fingerspelling Only", - "Status: Legacy Prototype" - ], - techStack: ["Arduino C", "Flex Sensors", "Analog Circuits"], - description: "Our first iteration — a hardware-based glove that captured finger bends to recognize fingerspelling letters. Though it laid the foundation, we moved on to a more powerful AI-driven approach." - }, - { - id: 3, - title: "Hearthstone ASL Course", - shortDesc: "Gamified learning platform that teaches ASL through interactive lessons, progression tracking, and a complete course system built with HTML, CSS, and JavaScript.", - image: "../src/hearthstone-asl-course.jpg", - specs: [ - "Tech Stack: HTML/CSS/JS", - "Gamification: XP, Achievements, Levels", - "Course Structure: Modular", - "Progress Tracking: Yes" - ], - techStack: ["HTML5", "CSS3", "JavaScript", "Gamification"], - description: "Learning ASL should be fun. Our ASL Course platform turns education into a game — complete lessons, earn XP, unlock achievements, and track your progression as you build real signing skills. Built entirely with web technologies for maximum accessibility." - } + { + id: 2, + title: "Hearthstone AI", + shortDesc: + "Our flagship AI model with 99.7% accuracy, supporting fingerspelling and speech-to-text across Python and JavaScript frameworks, optimized for Raspberry Pi.", + image: "../src/hearthstone-ai.jpg", + specs: [ + "Accuracy: 99.7%", + "Frameworks: Python, JavaScript", + "Platform: Raspberry Pi Optimized", + "Features: Fingerspelling + Speech-to-Text", + ], + techStack: [ + "TensorFlow", + "MediaPipe", + "Python", + "JavaScript", + "Raspberry Pi", + ], + description: + "Hearthstone AI is the heart of our project — a high-accuracy model that understands both hand signs and spoken words. Built on TensorFlow and MediaPipe, it runs smoothly on a Raspberry Pi and works seamlessly in both Python backends and JavaScript frontends. We're actively developing word gesture recognition to move beyond isolated signs.", + }, + { + id: 1, + title: "Hearthstone Glove", + shortDesc: + "Early prototype using Arduino and flex sensors to translate ASL fingerspelling.", + image: "../src/hearthstone-glove.jpg", + specs: [ + "Hardware: Arduino Uno", + "Sensors: 5 Flex Sensors", + "Detection: Fingerspelling Only", + "Status: Legacy Prototype", + ], + techStack: ["Arduino C", "Flex Sensors", "Analog Circuits"], + description: + "Our first iteration — a hardware-based glove that captured finger bends to recognize fingerspelling letters. Though it laid the foundation, we moved on to a more powerful AI-driven approach.", + }, + { + id: 3, + title: "Hearthstone ASL Course", + shortDesc: + "Gamified learning platform that teaches ASL through interactive lessons, progression tracking, and a complete course system built with HTML, CSS, and JavaScript.", + image: "../src/hearthstone-asl-course.jpg", + specs: [ + "Tech Stack: HTML/CSS/JS", + "Gamification: XP, Achievements, Levels", + "Course Structure: Modular", + "Progress Tracking: Yes", + ], + techStack: ["HTML5", "CSS3", "JavaScript", "Gamification"], + description: + "Learning ASL should be fun. Our ASL Course platform turns education into a game — complete lessons, earn XP, unlock achievements, and track your progression as you build real signing skills. Built entirely with web technologies for maximum accessibility.", + }, ]; // Initialize on DOM Ready -document.addEventListener('DOMContentLoaded', () => { - initializeProgressBar(); - initializeProjects(); - initParticles(); // Add particle system +document.addEventListener("DOMContentLoaded", () => { + initializeProgressBar(); + initializeProjects(); + initParticles(); // Add particle system }); // ========== PROGRESS BAR ========== function initializeProgressBar() { - const progressBar = document.getElementById('progressBar'); - if (!progressBar) return; - - window.addEventListener('scroll', () => { - const scrollTop = window.scrollY; - const docHeight = document.documentElement.scrollHeight - window.innerHeight; - const progress = (scrollTop / docHeight) * 100; - progressBar.style.width = `${progress}%`; - }); + const progressBar = document.getElementById("progressBar"); + if (!progressBar) return; + + window.addEventListener("scroll", () => { + const scrollTop = window.scrollY; + const docHeight = + document.documentElement.scrollHeight - window.innerHeight; + const progress = (scrollTop / docHeight) * 100; + progressBar.style.width = `${progress}%`; + }); } // ========== PROJECTS INITIALIZATION ========== function initializeProjects() { - const track = document.getElementById('projectsTrack'); - if (!track) return; - - // Generate project panels - projects.forEach((project, index) => { - const panel = createProjectPanel(project, index); - track.appendChild(panel); - }); - - // Initialize Intersection Observer after a slight delay to ensure DOM is ready - setTimeout(() => { - initializeIntersectionObserver(); - }, 100); + const track = document.getElementById("projectsTrack"); + if (!track) return; + + // Generate project panels + projects.forEach((project, index) => { + const panel = createProjectPanel(project, index); + track.appendChild(panel); + }); + + // Initialize Intersection Observer after a slight delay to ensure DOM is ready + setTimeout(() => { + initializeIntersectionObserver(); + }, 100); } // Create individual project panel DOM function createProjectPanel(project, index) { - const panel = document.createElement('div'); - panel.className = 'project-panel'; - panel.dataset.projectId = project.id; + const panel = document.createElement("div"); + panel.className = "project-panel"; + panel.dataset.projectId = project.id; - // Spec sheet items - const specsHtml = project.specs.map(spec => `
${spec}
`).join(''); + // Spec sheet items + const specsHtml = project.specs + .map((spec) => `
${spec}
`) + .join(""); - // Tech pills - const techPillsHtml = project.techStack.map(tech => `${tech}`).join(''); + // Tech pills + const techPillsHtml = project.techStack + .map((tech) => `${tech}`) + .join(""); - panel.innerHTML = ` + panel.innerHTML = `

${project.title}

${project.description}

@@ -113,181 +128,182 @@ function createProjectPanel(project, index) {
`; - return panel; + return panel; } // ========== INTERSECTION OBSERVER (Project Switch) ========== function initializeIntersectionObserver() { - const panels = document.querySelectorAll('.project-panel'); - const stickyImage = document.getElementById('stickyImage'); - const imageGlow = document.querySelector('.image-glow'); - - if (panels.length === 0 || !stickyImage) return; - - // Set initial image - if (projects.length > 0) { - updateStickyImage(stickyImage, projects[0].image); - } + const panels = document.querySelectorAll(".project-panel"); + const stickyImage = document.getElementById("stickyImage"); + const imageGlow = document.querySelector(".image-glow"); + + if (panels.length === 0 || !stickyImage) return; + + // Set initial image + if (projects.length > 0) { + updateStickyImage(stickyImage, projects[0].image); + } + + const observerOptions = { + root: null, + rootMargin: "-30% 0px -30% 0px", + threshold: 0.1, + }; + + const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const projectId = parseInt(entry.target.dataset.projectId); + const project = projects.find((p) => p.id === projectId); + + if (project) { + // Update active state + panels.forEach((p) => p.classList.remove("active")); + entry.target.classList.add("active"); + + // Trigger glitch-scale transition on image + triggerImageTransition(stickyImage, project.image); + } + } + }); + }, observerOptions); - const observerOptions = { - root: null, - rootMargin: '-30% 0px -30% 0px', - threshold: 0.1 - }; - - const observer = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - const projectId = parseInt(entry.target.dataset.projectId); - const project = projects.find(p => p.id === projectId); - - if (project) { - // Update active state - panels.forEach(p => p.classList.remove('active')); - entry.target.classList.add('active'); - - // Trigger glitch-scale transition on image - triggerImageTransition(stickyImage, project.image); - } - } - }); - }, observerOptions); - - panels.forEach(panel => observer.observe(panel)); + panels.forEach((panel) => observer.observe(panel)); } // Update sticky image with glitch effect function triggerImageTransition(imageElement, newSrc) { - imageElement.classList.remove('active'); - imageElement.classList.add('glitch-effect'); // Add glitch animation - - setTimeout(() => { - updateStickyImage(imageElement, newSrc); - imageElement.classList.remove('glitch-effect'); - imageElement.classList.add('active'); - }, 400); // Glitch duration 400ms + imageElement.classList.remove("active"); + imageElement.classList.add("glitch-effect"); // Add glitch animation + + setTimeout(() => { + updateStickyImage(imageElement, newSrc); + imageElement.classList.remove("glitch-effect"); + imageElement.classList.add("active"); + }, 400); // Glitch duration 400ms } // Helper: Update image src function updateStickyImage(imageElement, src) { - // For now, use a gradient placeholder since images don't exist - // In production, this would set imageElement.src = src; - imageElement.style.background = generatePlaceholderGradient(); - imageElement.src = 'data:image/svg+xml,%3Csvg width="500" height="300" xmlns="http://www.w3.org/2000/svg"%3E%3Crect width="500" height="300" fill="%230a0a0a"/%3E%3Ctext x="50%25" y="50%25" font-family="Arial, sans-serif" font-size="24" fill="%2300D4FF" text-anchor="middle"%3EProject Preview%3C/text%3E%3C/svg%3E'; + // For now, use a gradient placeholder since images don't exist + // In production, this would set imageElement.src = src; + imageElement.style.background = generatePlaceholderGradient(); + imageElement.src = + 'data:image/svg+xml,%3Csvg width="500" height="300" xmlns="http://www.w3.org/2000/svg"%3E%3Crect width="500" height="300" fill="%230a0a0a"/%3E%3Ctext x="50%25" y="50%25" font-family="Arial, sans-serif" font-size="24" fill="%2300D4FF" text-anchor="middle"%3EProject Preview%3C/text%3E%3C/svg%3E'; } // Generate a cool placeholder gradient function generatePlaceholderGradient() { - const gradients = [ - 'linear-gradient(135deg, #00D4FF 0%, #0066ff 100%)', - 'linear-gradient(135deg, #ff00d4 0%, #00D4FF 100%)', - 'linear-gradient(135deg, #00ffaa 0%, #00D4FF 100%)', - 'linear-gradient(135deg, #ff0066 0%, #ff00d4 100%)', - 'linear-gradient(135deg, #6600ff 0%, #ff00d4 100%)' - ]; - return gradients[Math.floor(Math.random() * gradients.length)]; + const gradients = [ + "linear-gradient(135deg, #00D4FF 0%, #0066ff 100%)", + "linear-gradient(135deg, #ff00d4 0%, #00D4FF 100%)", + "linear-gradient(135deg, #00ffaa 0%, #00D4FF 100%)", + "linear-gradient(135deg, #ff0066 0%, #ff00d4 100%)", + "linear-gradient(135deg, #6600ff 0%, #ff00d4 100%)", + ]; + return gradients[Math.floor(Math.random() * gradients.length)]; } // ========== NEURAL MESH PARTICLES ========== function initParticles() { - const canvas = document.createElement('canvas'); - canvas.id = 'particleCanvas'; - canvas.style.position = 'fixed'; - canvas.style.top = '0'; - canvas.style.left = '0'; - canvas.style.width = '100%'; - canvas.style.height = '100%'; - canvas.style.zIndex = '-1'; - canvas.style.pointerEvents = 'none'; - document.body.appendChild(canvas); - - const ctx = canvas.getContext('2d'); - let width, height; - const particles = []; - const particleCount = 70; // 60-80 nodes - const connectionDistance = 120; - const mouseRepelDist = 150; - const mouse = { x: null, y: null }; - - function resize() { - width = window.innerWidth; - height = window.innerHeight; - canvas.width = width; - canvas.height = height; - } - - window.addEventListener('resize', resize); - - window.addEventListener('mousemove', e => { - mouse.x = e.clientX; - mouse.y = e.clientY; + const canvas = document.createElement("canvas"); + canvas.id = "particleCanvas"; + canvas.style.position = "fixed"; + canvas.style.top = "0"; + canvas.style.left = "0"; + canvas.style.width = "100%"; + canvas.style.height = "100%"; + canvas.style.zIndex = "-1"; + canvas.style.pointerEvents = "none"; + document.body.appendChild(canvas); + + const ctx = canvas.getContext("2d"); + let width, height; + const particles = []; + const particleCount = 70; // 60-80 nodes + const connectionDistance = 120; + const mouseRepelDist = 150; + const mouse = {x: null, y: null}; + + function resize() { + width = window.innerWidth; + height = window.innerHeight; + canvas.width = width; + canvas.height = height; + } + + window.addEventListener("resize", resize); + + window.addEventListener("mousemove", (e) => { + mouse.x = e.clientX; + mouse.y = e.clientY; + }); + + window.addEventListener("mouseout", () => { + mouse.x = null; + mouse.y = null; + }); + + // Initialize size BEFORE creating particles + resize(); + + // Create particles with valid positions + for (let i = 0; i < particleCount; i++) { + particles.push({ + x: Math.random() * width, + y: Math.random() * height, + vx: (Math.random() - 0.5) * 0.5, + vy: (Math.random() - 0.5) * 0.5, + size: Math.random() * 2 + 2, // 2-4px }); + } + + function update() { + ctx.clearRect(0, 0, width, height); + ctx.strokeStyle = "rgba(0, 212, 255, 0.4)"; // line opacity + ctx.fillStyle = "rgba(0, 212, 255, 0.9)"; // particle opacity + + particles.forEach((p) => { + p.x += p.vx; + p.y += p.vy; + + if (p.x < 0 || p.x > width) p.vx *= -1; + if (p.y < 0 || p.y > height) p.vy *= -1; + + if (mouse.x != null) { + const dx = p.x - mouse.x; + const dy = p.y - mouse.y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < mouseRepelDist) { + const force = (mouseRepelDist - dist) / mouseRepelDist; + p.x += (dx / dist) * force * 5; + p.y += (dy / dist) * force * 5; + } + } - window.addEventListener('mouseout', () => { - mouse.x = null; - mouse.y = null; + ctx.beginPath(); + ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); + ctx.fill(); }); - // Initialize size BEFORE creating particles - resize(); - - // Create particles with valid positions - for (let i = 0; i < particleCount; i++) { - particles.push({ - x: Math.random() * width, - y: Math.random() * height, - vx: (Math.random() - 0.5) * 0.5, - vy: (Math.random() - 0.5) * 0.5, - size: Math.random() * 2 + 2 // 2-4px - }); - } - - function update() { - ctx.clearRect(0, 0, width, height); - ctx.strokeStyle = 'rgba(0, 212, 255, 0.4)'; // line opacity - ctx.fillStyle = 'rgba(0, 212, 255, 0.9)'; // particle opacity - - particles.forEach(p => { - p.x += p.vx; - p.y += p.vy; - - if (p.x < 0 || p.x > width) p.vx *= -1; - if (p.y < 0 || p.y > height) p.vy *= -1; - - if (mouse.x != null) { - const dx = p.x - mouse.x; - const dy = p.y - mouse.y; - const dist = Math.sqrt(dx*dx + dy*dy); - if (dist < mouseRepelDist) { - const force = (mouseRepelDist - dist) / mouseRepelDist; - p.x += (dx / dist) * force * 5; - p.y += (dy / dist) * force * 5; - } - } - - ctx.beginPath(); - ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); - ctx.fill(); - }); - - // Draw connections - for (let i = 0; i < particles.length; i++) { - for (let j = i+1; j < particles.length; j++) { - const dx = particles[i].x - particles[j].x; - const dy = particles[i].y - particles[j].y; - const dist = Math.sqrt(dx*dx + dy*dy); - if (dist < connectionDistance) { - ctx.lineWidth = 1 - dist/connectionDistance; - ctx.beginPath(); - ctx.moveTo(particles[i].x, particles[i].y); - ctx.lineTo(particles[j].x, particles[j].y); - ctx.stroke(); - } - } + // Draw connections + for (let i = 0; i < particles.length; i++) { + for (let j = i + 1; j < particles.length; j++) { + const dx = particles[i].x - particles[j].x; + const dy = particles[i].y - particles[j].y; + const dist = Math.sqrt(dx * dx + dy * dy); + if (dist < connectionDistance) { + ctx.lineWidth = 1 - dist / connectionDistance; + ctx.beginPath(); + ctx.moveTo(particles[i].x, particles[i].y); + ctx.lineTo(particles[j].x, particles[j].y); + ctx.stroke(); } - - requestAnimationFrame(update); + } } - update(); + requestAnimationFrame(update); + } + + update(); } diff --git a/projecthearthstone.in/Projects/style.css b/projecthearthstone.in/Projects/style.css index 5a1c04c..9352b51 100644 --- a/projecthearthstone.in/Projects/style.css +++ b/projecthearthstone.in/Projects/style.css @@ -1,9 +1,7 @@ -/* Hearthstone Projects - Premium AI Experience */ - :root { --bg-primary: #020204; /* Deep Obsidian */ - --accent-blue: #00D4FF; /* Electric Cyan */ - --signal-green: #00FF41; /* Phosphor-Green */ + --accent-blue: #00d4ff; /* Electric Cyan */ + --signal-green: #00ff41; /* Phosphor-Green */ --text-primary: #ffffff; --text-secondary: #a0a0a0; --border-color: #1a1a1a; @@ -15,7 +13,7 @@ body { background-color: var(--bg-primary); color: var(--text-primary); overflow-x: hidden; - font-family: 'Montserrat', sans-serif; + font-family: "Montserrat", sans-serif; } /* ========== PROGRESS BAR ========== */ @@ -51,7 +49,7 @@ body { font-weight: 900; letter-spacing: 0.1em; margin-bottom: 1rem; - font-family: 'Orbitron', sans-serif; + font-family: "Orbitron", sans-serif; } .shimmer { @@ -71,8 +69,12 @@ body { } @keyframes shimmerAnim { - 0% { background-position: 200% center; } - 100% { background-position: -200% center; } + 0% { + background-position: 200% center; + } + 100% { + background-position: -200% center; + } } .hero-subtitle { @@ -86,7 +88,12 @@ body { .hero-divider { width: 200px; height: 2px; - background: linear-gradient(90deg, transparent, var(--accent-blue), transparent); + background: linear-gradient( + 90deg, + transparent, + var(--accent-blue), + transparent + ); margin: 0 auto; } @@ -96,7 +103,12 @@ body { grid-template-columns: 1fr 1fr; min-height: 100vh; position: relative; - background: linear-gradient(to bottom, rgba(0, 10, 30, 0) 0%, rgba(0, 10, 30, 0.5) 20%, rgba(0, 10, 30, 0.5) 100%); + background: linear-gradient( + to bottom, + rgba(0, 10, 30, 0) 0%, + rgba(0, 10, 30, 0.5) 20%, + rgba(0, 10, 30, 0.5) 100% + ); } .image-column { @@ -107,7 +119,11 @@ body { display: flex; align-items: center; justify-content: center; - background: linear-gradient(135deg, rgba(0, 212, 255, 0.05) 0%, transparent 50%); + background: linear-gradient( + 135deg, + rgba(0, 212, 255, 0.05) 0%, + transparent 50% + ); border-right: 1px solid var(--border-color); z-index: 10; } @@ -128,7 +144,9 @@ body { object-fit: cover; border-radius: 12px; box-shadow: 0 0 40px rgba(0, 212, 255, 0.3); - transition: filter 0.3s ease, transform 0.3s ease; + transition: + filter 0.3s ease, + transform 0.3s ease; } .sticky-image.active { @@ -150,8 +168,15 @@ body { } @keyframes glowPulse { - 0%, 100% { opacity: 0.4; transform: translate(-50%, -50%) scale(1); } - 50% { opacity: 0.7; transform: translate(-50%, -50%) scale(1.1); } + 0%, + 100% { + opacity: 0.4; + transform: translate(-50%, -50%) scale(1); + } + 50% { + opacity: 0.7; + transform: translate(-50%, -50%) scale(1.1); + } } /* Glitch-Scale Transition */ @@ -163,9 +188,11 @@ body { .glitch-target.active { filter: blur(0); - transform: scale(1.0); + transform: scale(1); opacity: 1; - transition: filter 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); + transition: + filter 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), + transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } /* Glitch effect for data swap */ @@ -177,27 +204,32 @@ body { 0% { clip-path: inset(5% 0 85% 0); transform: translateX(-2px); - filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); } 20% { clip-path: inset(25% 0 60% 0); transform: translateX(2px); - filter: contrast(150%) drop-shadow(2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(-2px 0 rgba(255, 0, 212, 0.8)); + filter: contrast(150%) drop-shadow(2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(-2px 0 rgba(255, 0, 212, 0.8)); } 40% { clip-path: inset(45% 0 40% 0); transform: translateX(-1px); - filter: contrast(150%) drop-shadow(-1px 0 rgba(0, 212, 255, 0.9)) drop-shadow(1px 0 rgba(255, 0, 212, 0.9)); + filter: contrast(150%) drop-shadow(-1px 0 rgba(0, 212, 255, 0.9)) + drop-shadow(1px 0 rgba(255, 0, 212, 0.9)); } 60% { clip-path: inset(55% 0 30% 0); transform: translateX(1px); - filter: contrast(150%) drop-shadow(1px 0 rgba(0, 212, 255, 0.7)) drop-shadow(-1px 0 rgba(255, 0, 212, 0.7)); + filter: contrast(150%) drop-shadow(1px 0 rgba(0, 212, 255, 0.7)) + drop-shadow(-1px 0 rgba(255, 0, 212, 0.7)); } 80% { clip-path: inset(65% 0 20% 0); transform: translateX(-2px); - filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); + filter: contrast(150%) drop-shadow(-2px 0 rgba(0, 212, 255, 0.8)) + drop-shadow(2px 0 rgba(255, 0, 212, 0.8)); } 100% { clip-path: inset(0 0 0 0); @@ -228,7 +260,9 @@ body { flex-direction: column; justify-content: center; opacity: 0.3; - transition: opacity 0.5s ease, transform 0.5s ease; + transition: + opacity 0.5s ease, + transform 0.5s ease; transform: translateY(30px); } @@ -241,14 +275,16 @@ body { .project-panel.active h2 { opacity: 0; transform: translateY(20px); - animation: fadeInUp 0.6s ease forwards, breathe 4s ease-in-out infinite 0.6s; + animation: + fadeInUp 0.6s ease forwards, + breathe 4s ease-in-out infinite 0.6s; color: var(--accent-blue); letter-spacing: 0.02em; margin-bottom: 1rem; } .project-panel.active h2::after { - content: ''; + content: ""; display: block; width: 60px; height: 3px; @@ -266,8 +302,13 @@ body { } @keyframes breathe { - 0%, 100% { letter-spacing: 0.02em; } - 50% { letter-spacing: 0.05em; } + 0%, + 100% { + letter-spacing: 0.02em; + } + 50% { + letter-spacing: 0.05em; + } } /* Description */ @@ -292,11 +333,13 @@ body { border-left: 2px solid var(--signal-green); position: relative; overflow: hidden; - box-shadow: inset 0 0 0 1px rgba(0, 212, 255, 0.2), 0 0 15px rgba(0, 212, 255, 0.05); + box-shadow: + inset 0 0 0 1px rgba(0, 212, 255, 0.2), + 0 0 15px rgba(0, 212, 255, 0.05); } .spec-item { - font-family: 'Courier New', monospace; + font-family: "Courier New", monospace; font-size: 0.95rem; color: var(--text-secondary); margin: 0.75rem 0; @@ -311,10 +354,18 @@ body { animation: slideInRight 0.4s ease forwards; } -.project-panel.active .spec-item:nth-child(1) { animation-delay: 0.3s; } -.project-panel.active .spec-item:nth-child(2) { animation-delay: 0.4s; } -.project-panel.active .spec-item:nth-child(3) { animation-delay: 0.5s; } -.project-panel.active .spec-item:nth-child(4) { animation-delay: 0.6s; } +.project-panel.active .spec-item:nth-child(1) { + animation-delay: 0.3s; +} +.project-panel.active .spec-item:nth-child(2) { + animation-delay: 0.4s; +} +.project-panel.active .spec-item:nth-child(3) { + animation-delay: 0.5s; +} +.project-panel.active .spec-item:nth-child(4) { + animation-delay: 0.6s; +} @keyframes slideInRight { to { @@ -324,7 +375,7 @@ body { } .spec-item::before { - content: '>'; + content: ">"; position: absolute; left: 0; color: var(--signal-green); @@ -333,8 +384,13 @@ body { } @keyframes caretBlink { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.3; } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } } /* Tech stack pills */ @@ -361,7 +417,7 @@ body { /* Border-trace effect */ .tech-pill::before { - content: ''; + content: ""; position: absolute; inset: 0; padding: 1px; @@ -391,11 +447,15 @@ body { } @keyframes rotate { - to { transform: rotate(360deg); } + to { + transform: rotate(360deg); + } } .tech-pill:hover { - box-shadow: 0 0 20px var(--accent-glow), inset 0 0 10px rgba(0, 212, 255, 0.2); + box-shadow: + 0 0 20px var(--accent-glow), + inset 0 0 10px rgba(0, 212, 255, 0.2); transform: translateY(-2px); } @@ -428,9 +488,18 @@ body { } @keyframes springIn { - 0% { opacity: 0; transform: translateY(20px) scale(0.9); } - 60% { opacity: 1; transform: translateY(-5px) scale(1.05); } - 100% { opacity: 1; transform: translateY(0) scale(1); } + 0% { + opacity: 0; + transform: translateY(20px) scale(0.9); + } + 60% { + opacity: 1; + transform: translateY(-5px) scale(1.05); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1); + } } .btn-primary { @@ -462,7 +531,8 @@ body { } @keyframes borderPulse { - 0%, 100% { + 0%, + 100% { box-shadow: 0 0 5px var(--signal-glow); border-color: var(--signal-green); }