21 | Soumik
24 |SEO Specialist & Web Developer
25 |26 | Passionate about ranking websites and crafting beautiful digital experiences. Let's connect and build 27 | something amazing together. 28 |
29 | 35 |├── profiles ├── dsawithaditi │ ├── js │ │ └── main.js │ ├── assets │ │ └── images │ │ │ └── aditi.jpeg │ ├── index.html │ └── css │ │ └── style.css ├── ritwik1709 │ ├── assets │ │ └── 618072.jpg │ ├── index.html │ ├── js │ │ └── main.js │ └── css │ │ └── style.css ├── rumi20 │ ├── assets │ │ └── images │ │ │ └── rumi.jpeg │ ├── js │ │ └── main.js │ ├── index.html │ └── css │ │ └── style.css ├── nusrat80 │ ├── assets │ │ └── images │ │ │ └── nusrat.jpg │ ├── js │ │ └── main.js │ ├── index.html │ └── css │ │ └── style.css ├── withaarzoo │ ├── assets │ │ └── images │ │ │ └── aarzoo.png │ ├── index.html │ ├── js │ │ └── main.js │ └── css │ │ └── style.css ├── rakesh-dev-07 │ ├── assets │ │ └── image │ │ │ └── rakesh.jpeg │ ├── js │ │ └── main.js │ ├── index.html │ └── css │ │ └── style.css └── seobysoumik │ ├── assets │ └── images │ │ └── profile.jpeg │ ├── js │ └── main.js │ ├── index.html │ └── css │ └── style.css ├── contributors.json ├── index.html ├── README.md ├── CONTRIBUTING.md ├── js └── main.js └── css └── style.css /profiles/dsawithaditi/js/main.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /profiles/ritwik1709/assets/618072.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/ritwik1709/assets/618072.jpg -------------------------------------------------------------------------------- /profiles/rumi20/assets/images/rumi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/rumi20/assets/images/rumi.jpeg -------------------------------------------------------------------------------- /profiles/nusrat80/assets/images/nusrat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/nusrat80/assets/images/nusrat.jpg -------------------------------------------------------------------------------- /profiles/dsawithaditi/assets/images/aditi.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/dsawithaditi/assets/images/aditi.jpeg -------------------------------------------------------------------------------- /profiles/withaarzoo/assets/images/aarzoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/withaarzoo/assets/images/aarzoo.png -------------------------------------------------------------------------------- /profiles/rakesh-dev-07/assets/image/rakesh.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/rakesh-dev-07/assets/image/rakesh.jpeg -------------------------------------------------------------------------------- /profiles/seobysoumik/assets/images/profile.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dsawithaditi/Hacktoberfest2025/HEAD/profiles/seobysoumik/assets/images/profile.jpeg -------------------------------------------------------------------------------- /profiles/rakesh-dev-07/js/main.js: -------------------------------------------------------------------------------- 1 | const card = document.querySelector(".card"); 2 | const maxRotation = 10; 3 | 4 | card.addEventListener("mousemove", (e) => { 5 | const cardRect = card.getBoundingClientRect(); 6 | 7 | const x = e.clientX - cardRect.left - cardRect.width / 2; 8 | const y = e.clientY - cardRect.top - cardRect.height / 2; 9 | 10 | const rotateY = (x / (cardRect.width / 2)) * maxRotation; 11 | const rotateX = -1 * (y / (cardRect.height / 2)) * maxRotation; 12 | 13 | card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; 14 | }); 15 | 16 | card.addEventListener("mouseleave", () => { 17 | card.style.transform = "rotateX(0deg) rotateY(0deg)"; 18 | }); 19 | -------------------------------------------------------------------------------- /profiles/nusrat80/js/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const card = document.querySelector(".profile-card"); 3 | 4 | if (card) { 5 | card.addEventListener("mousemove", (e) => { 6 | const cardRect = card.getBoundingClientRect(); 7 | const x = e.clientX - cardRect.left; 8 | const y = e.clientY - cardRect.top; 9 | 10 | const centerX = card.offsetWidth / 2; 11 | const centerY = card.offsetHeight / 2; 12 | 13 | const rotateX = ((y - centerY) / centerY) * -10; 14 | const rotateY = ((x - centerX) / centerX) * 10; 15 | 16 | card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg)`; 17 | }); 18 | 19 | card.addEventListener("mouseleave", () => { 20 | card.style.transform = "rotateX(0deg) rotateY(0deg)"; 21 | }); 22 | } 23 | }); 24 | -------------------------------------------------------------------------------- /profiles/seobysoumik/js/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const card = document.querySelector(".profile-card"); 3 | 4 | const maxTilt = 15; 5 | 6 | card.addEventListener("mousemove", (e) => { 7 | const cardRect = card.getBoundingClientRect(); 8 | 9 | const mouseX = e.clientX - cardRect.left - cardRect.width / 2; 10 | const mouseY = e.clientY - cardRect.top - cardRect.height / 2; 11 | 12 | const rotateY = (mouseX / (cardRect.width / 2)) * maxTilt; 13 | const rotateX = -1 * (mouseY / (cardRect.height / 2)) * maxTilt; 14 | 15 | card.style.transform = `rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.05)`; 16 | card.style.transition = "transform 0.1s linear"; 17 | }); 18 | 19 | card.addEventListener("mouseleave", () => { 20 | card.style.transform = "rotateX(0deg) rotateY(0deg) scale(1)"; 21 | card.style.transition = "transform 0.5s cubic-bezier(0.23, 1, 0.32, 1)"; 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /profiles/rumi20/js/main.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", () => { 2 | const card = document.querySelector(".profile-card"); 3 | 4 | if (card) { 5 | card.addEventListener("mousemove", (e) => { 6 | const { width, height, left, top } = card.getBoundingClientRect(); 7 | const x = e.clientX - left; 8 | const y = e.clientY - top; 9 | const centerX = width / 2; 10 | const centerY = height / 2; 11 | 12 | const rotateX = (y - centerY) / 15; 13 | const rotateY = (centerX - x) / 15; 14 | 15 | card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale(1.05)`; 16 | 17 | card.style.setProperty("--shine-x", `${x}px`); 18 | card.style.setProperty("--shine-y", `${y}px`); 19 | }); 20 | 21 | card.addEventListener("mouseleave", () => { 22 | card.style.transform = 23 | "perspective(1000px) rotateX(0) rotateY(0) scale(1)"; 24 | }); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /profiles/dsawithaditi/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 |
20 | Software Engineer
24 | 30 |
21 | SEO Specialist & Web Developer
25 |26 | Passionate about ranking websites and crafting beautiful digital experiences. Let's connect and build 27 | something amazing together. 28 |
29 | 35 |
25 | Crafting beautiful and intuitive digital experiences with a passion for clean code and 31 | delightful design.
32 |
23 | Full-Stack Web Developer
27 |A showcase of creative developer profile cards from the open-source community. Built 38 | for Hacktoberfest 2025.
39 | Start Contributing Today 41 |
17 |
18 | Full Stack Developer
29 |30 | Passionate developer creating amazing web experiences with modern technologies. 31 | Always learning and building something new! 32 |
33 | 34 |${contributor.bio}
View on GitHub `; 147 | detailModal.classList.add("visible"); 148 | } 149 | function closeDetailModal() { 150 | detailModal.classList.remove("visible"); 151 | detailModalIframeContainer.innerHTML = ""; 152 | detailModalInfo.innerHTML = ""; 153 | } 154 | 155 | // Unchanged Functions 156 | function loadCardIframe(card) { 157 | const iframe = card.querySelector("iframe"); 158 | if (iframe && !iframe.src) { 159 | const contributor = JSON.parse(card.dataset.contributorData); 160 | iframe.src = contributor.project_netlify_link; 161 | iframe.onload = () => { 162 | iframe.classList.add("loaded"); 163 | card.querySelector(".skeleton").style.display = "none"; 164 | }; 165 | } 166 | } 167 | function displayNextBatch() { 168 | if (isLoading || displayedCount >= displayedContributors.length) return; 169 | isLoading = true; 170 | loader.classList.add("visible"); 171 | const batch = displayedContributors.slice( 172 | displayedCount, 173 | displayedCount + batchSize 174 | ); 175 | setTimeout(() => { 176 | batch.forEach((contributor) => { 177 | const card = createProfileCard(contributor); 178 | container.appendChild(card); 179 | cardObserver.observe(card); 180 | }); 181 | displayedCount += batch.length; 182 | isLoading = false; 183 | loader.classList.remove("visible"); 184 | if (displayedCount >= displayedContributors.length) { 185 | sentinel.style.display = "none"; 186 | } 187 | }, 300); 188 | } 189 | function filterContributors() { 190 | const searchTerm = searchInput.value.toLowerCase().trim(); 191 | displayedContributors = allContributors.filter((c) => { 192 | const tags = (c.tags || []).join(",").toLowerCase(); 193 | return ( 194 | c.name.toLowerCase().includes(searchTerm) || 195 | c.bio.toLowerCase().includes(searchTerm) || 196 | tags.includes(searchTerm) 197 | ); 198 | }); 199 | container.innerHTML = ""; 200 | displayedCount = 0; 201 | noResultsMsg.style.display = 202 | displayedContributors.length === 0 ? "block" : "none"; 203 | sentinel.style.display = "block"; 204 | displayNextBatch(); 205 | } 206 | async function initialize() { 207 | try { 208 | const response = await fetch("contributors.json"); 209 | if (!response.ok) throw new Error("contributors.json not found."); 210 | let data = await response.json(); 211 | if (Array.isArray(data)) { 212 | allContributors = data; 213 | } else { 214 | console.error("Data from contributors.json is not a valid array."); 215 | allContributors = []; 216 | } 217 | displayedContributors = [...allContributors]; 218 | const intersectionObserver = new IntersectionObserver((entries) => { 219 | if (entries[0].isIntersecting && !isLoading) { 220 | displayNextBatch(); 221 | } 222 | }); 223 | intersectionObserver.observe(sentinel); 224 | displayNextBatch(); 225 | } catch (error) { 226 | console.error("Failed to load or parse contributors.json:", error); 227 | container.innerHTML = ``; 228 | } 229 | } 230 | const cardObserver = new IntersectionObserver( 231 | (entries, observer) => { 232 | entries.forEach((entry) => { 233 | if (entry.isIntersecting) { 234 | loadCardIframe(entry.target); 235 | observer.unobserve(entry.target); 236 | } 237 | }); 238 | }, 239 | { rootMargin: "200px" } 240 | ); 241 | 242 | initialize(); 243 | }); 244 | -------------------------------------------------------------------------------- /profiles/ritwik1709/js/main.js: -------------------------------------------------------------------------------- 1 | // DOM Elements 2 | const profileCard = document.querySelector('.profile-card'); 3 | const contactBtn = document.querySelector('.contact-btn'); 4 | const socialLinks = document.querySelectorAll('.social-link'); 5 | const skillTags = document.querySelectorAll('.skill-tag'); 6 | const statNumbers = document.querySelectorAll('.stat-number'); 7 | const profileImage = document.querySelector('.profile-image'); 8 | 9 | // Initialize animations when page loads 10 | document.addEventListener('DOMContentLoaded', function() { 11 | initializeAnimations(); 12 | setupEventListeners(); 13 | animateStats(); 14 | setupParallaxEffect(); 15 | }); 16 | 17 | // Initialize all animations 18 | function initializeAnimations() { 19 | // Add staggered animation to skill tags 20 | skillTags.forEach((tag, index) => { 21 | tag.style.animationDelay = `${0.1 * index}s`; 22 | tag.style.animation = 'fadeInUp 0.6s ease-out forwards'; 23 | }); 24 | 25 | // Add hover effect to profile image 26 | profileImage.addEventListener('mouseenter', function() { 27 | this.style.transform = 'scale(1.05) rotate(5deg)'; 28 | }); 29 | 30 | profileImage.addEventListener('mouseleave', function() { 31 | this.style.transform = 'scale(1) rotate(0deg)'; 32 | }); 33 | } 34 | 35 | // Setup event listeners for interactive elements 36 | function setupEventListeners() { 37 | // Contact button interaction 38 | contactBtn.addEventListener('click', function(e) { 39 | e.preventDefault(); 40 | createRippleEffect(e); 41 | showContactModal(); 42 | }); 43 | 44 | // Social links interaction 45 | socialLinks.forEach(link => { 46 | link.addEventListener('click', function(e) { 47 | e.preventDefault(); 48 | createClickAnimation(this); 49 | }); 50 | }); 51 | 52 | // Skill tags hover effect 53 | skillTags.forEach(tag => { 54 | tag.addEventListener('mouseenter', function() { 55 | this.style.transform = 'translateY(-3px) scale(1.05)'; 56 | this.style.boxShadow = '0 8px 25px rgba(102, 126, 234, 0.3)'; 57 | }); 58 | 59 | tag.addEventListener('mouseleave', function() { 60 | this.style.transform = 'translateY(0) scale(1)'; 61 | this.style.boxShadow = 'none'; 62 | }); 63 | }); 64 | 65 | // Card tilt effect 66 | setupCardTiltEffect(); 67 | } 68 | 69 | // Create ripple effect for buttons 70 | function createRippleEffect(event) { 71 | const button = event.currentTarget; 72 | const ripple = document.createElement('span'); 73 | const rect = button.getBoundingClientRect(); 74 | const size = Math.max(rect.width, rect.height); 75 | const x = event.clientX - rect.left - size / 2; 76 | const y = event.clientY - rect.top - size / 2; 77 | 78 | ripple.style.width = ripple.style.height = size + 'px'; 79 | ripple.style.left = x + 'px'; 80 | ripple.style.top = y + 'px'; 81 | ripple.classList.add('ripple'); 82 | 83 | // Add ripple styles 84 | ripple.style.position = 'absolute'; 85 | ripple.style.borderRadius = '50%'; 86 | ripple.style.background = 'rgba(255, 255, 255, 0.6)'; 87 | ripple.style.transform = 'scale(0)'; 88 | ripple.style.animation = 'ripple 0.6s linear'; 89 | ripple.style.pointerEvents = 'none'; 90 | 91 | button.style.position = 'relative'; 92 | button.style.overflow = 'hidden'; 93 | button.appendChild(ripple); 94 | 95 | // Remove ripple after animation 96 | setTimeout(() => { 97 | ripple.remove(); 98 | }, 600); 99 | } 100 | 101 | // Create click animation for social links 102 | function createClickAnimation(element) { 103 | element.style.transform = 'scale(0.9)'; 104 | setTimeout(() => { 105 | element.style.transform = 'scale(1)'; 106 | }, 150); 107 | } 108 | 109 | // Animate statistics numbers 110 | function animateStats() { 111 | const observer = new IntersectionObserver((entries) => { 112 | entries.forEach(entry => { 113 | if (entry.isIntersecting) { 114 | const target = entry.target; 115 | const targetValue = parseInt(target.getAttribute('data-target')); 116 | animateNumber(target, targetValue); 117 | observer.unobserve(target); 118 | } 119 | }); 120 | }); 121 | 122 | statNumbers.forEach(number => { 123 | observer.observe(number); 124 | }); 125 | } 126 | 127 | // Animate number counting 128 | function animateNumber(element, target) { 129 | const duration = 2000; 130 | const start = performance.now(); 131 | const startValue = 0; 132 | 133 | function updateNumber(currentTime) { 134 | const elapsed = currentTime - start; 135 | const progress = Math.min(elapsed / duration, 1); 136 | 137 | // Easing function for smooth animation 138 | const easeOutQuart = 1 - Math.pow(1 - progress, 4); 139 | const currentValue = Math.floor(startValue + (target - startValue) * easeOutQuart); 140 | 141 | element.textContent = currentValue; 142 | 143 | if (progress < 1) { 144 | requestAnimationFrame(updateNumber); 145 | } else { 146 | element.textContent = target; 147 | } 148 | } 149 | 150 | requestAnimationFrame(updateNumber); 151 | } 152 | 153 | // Setup card tilt effect 154 | function setupCardTiltEffect() { 155 | let isTilted = false; 156 | 157 | profileCard.addEventListener('mousemove', function(e) { 158 | if (isTilted) return; 159 | 160 | const rect = this.getBoundingClientRect(); 161 | const centerX = rect.left + rect.width / 2; 162 | const centerY = rect.top + rect.height / 2; 163 | const mouseX = e.clientX - centerX; 164 | const mouseY = e.clientY - centerY; 165 | 166 | const rotateX = (mouseY / rect.height) * -10; 167 | const rotateY = (mouseX / rect.width) * 10; 168 | 169 | this.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) translateZ(10px)`; 170 | }); 171 | 172 | profileCard.addEventListener('mouseleave', function() { 173 | this.style.transform = 'perspective(1000px) rotateX(0deg) rotateY(0deg) translateZ(0px)'; 174 | isTilted = false; 175 | }); 176 | } 177 | 178 | // Setup parallax effect for background elements 179 | function setupParallaxEffect() { 180 | const bgElements = document.querySelectorAll('.bg-circle'); 181 | 182 | window.addEventListener('mousemove', function(e) { 183 | const mouseX = e.clientX / window.innerWidth; 184 | const mouseY = e.clientY / window.innerHeight; 185 | 186 | bgElements.forEach((element, index) => { 187 | const speed = (index + 1) * 0.5; 188 | const x = (mouseX - 0.5) * speed * 20; 189 | const y = (mouseY - 0.5) * speed * 20; 190 | 191 | element.style.transform = `translate(${x}px, ${y}px)`; 192 | }); 193 | }); 194 | } 195 | 196 | // Show contact modal (placeholder function) 197 | function showContactModal() { 198 | // Create a simple alert for now - can be replaced with actual modal 199 | const modal = document.createElement('div'); 200 | modal.style.cssText = ` 201 | position: fixed; 202 | top: 0; 203 | left: 0; 204 | width: 100%; 205 | height: 100%; 206 | background: rgba(0, 0, 0, 0.8); 207 | display: flex; 208 | align-items: center; 209 | justify-content: center; 210 | z-index: 1000; 211 | animation: fadeIn 0.3s ease-out; 212 | `; 213 | 214 | const modalContent = document.createElement('div'); 215 | modalContent.style.cssText = ` 216 | background: white; 217 | padding: 40px; 218 | border-radius: 20px; 219 | text-align: center; 220 | max-width: 400px; 221 | margin: 20px; 222 | animation: slideUp 0.3s ease-out; 223 | `; 224 | 225 | modalContent.innerHTML = ` 226 |Thanks for your interest! You can reach me through the social links above.
228 | 237 | `; 238 | 239 | modal.appendChild(modalContent); 240 | document.body.appendChild(modal); 241 | 242 | // Close modal when clicking outside 243 | modal.addEventListener('click', function(e) { 244 | if (e.target === modal) { 245 | modal.remove(); 246 | } 247 | }); 248 | } 249 | 250 | // Add CSS animations dynamically 251 | const style = document.createElement('style'); 252 | style.textContent = ` 253 | @keyframes ripple { 254 | to { 255 | transform: scale(4); 256 | opacity: 0; 257 | } 258 | } 259 | 260 | @keyframes fadeIn { 261 | from { opacity: 0; } 262 | to { opacity: 1; } 263 | } 264 | 265 | .profile-card { 266 | transition: transform 0.3s ease; 267 | } 268 | 269 | .skill-tag { 270 | transition: all 0.3s ease; 271 | } 272 | `; 273 | document.head.appendChild(style); 274 | 275 | // Add loading animation 276 | window.addEventListener('load', function() { 277 | document.body.style.opacity = '0'; 278 | document.body.style.transition = 'opacity 0.5s ease-in-out'; 279 | 280 | setTimeout(() => { 281 | document.body.style.opacity = '1'; 282 | }, 100); 283 | }); 284 | 285 | // Add scroll animations 286 | const observerOptions = { 287 | threshold: 0.1, 288 | rootMargin: '0px 0px -50px 0px' 289 | }; 290 | 291 | const observer = new IntersectionObserver((entries) => { 292 | entries.forEach(entry => { 293 | if (entry.isIntersecting) { 294 | entry.target.style.opacity = '1'; 295 | entry.target.style.transform = 'translateY(0)'; 296 | } 297 | }); 298 | }, observerOptions); 299 | 300 | // Observe elements for scroll animations 301 | document.addEventListener('DOMContentLoaded', function() { 302 | const animatedElements = document.querySelectorAll('.profile-name, .profile-title, .profile-description, .skills-container, .stats-container, .social-links, .contact-btn'); 303 | 304 | animatedElements.forEach(element => { 305 | element.style.opacity = '0'; 306 | element.style.transform = 'translateY(20px)'; 307 | element.style.transition = 'opacity 0.6s ease-out, transform 0.6s ease-out'; 308 | observer.observe(element); 309 | }); 310 | }); 311 | -------------------------------------------------------------------------------- /profiles/ritwik1709/css/style.css: -------------------------------------------------------------------------------- 1 | /* Reset and base styles */ 2 | * { 3 | margin: 0; 4 | padding: 0; 5 | box-sizing: border-box; 6 | } 7 | 8 | body { 9 | font-family: 'Inter', sans-serif; 10 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 11 | min-height: 100vh; 12 | display: flex; 13 | align-items: center; 14 | justify-content: center; 15 | overflow-x: hidden; 16 | position: relative; 17 | } 18 | 19 | .container { 20 | position: relative; 21 | z-index: 2; 22 | padding: 20px; 23 | width: 100%; 24 | max-width: 400px; 25 | } 26 | 27 | /* Profile Card */ 28 | .profile-card { 29 | background: rgba(255, 255, 255, 0.95); 30 | backdrop-filter: blur(20px); 31 | border-radius: 24px; 32 | box-shadow: 33 | 0 20px 40px rgba(0, 0, 0, 0.1), 34 | 0 0 0 1px rgba(255, 255, 255, 0.2); 35 | overflow: hidden; 36 | position: relative; 37 | animation: slideUp 0.8s ease-out; 38 | transition: transform 0.3s ease, box-shadow 0.3s ease; 39 | } 40 | 41 | .profile-card:hover { 42 | transform: translateY(-10px); 43 | box-shadow: 44 | 0 30px 60px rgba(0, 0, 0, 0.15), 45 | 0 0 0 1px rgba(255, 255, 255, 0.3); 46 | } 47 | 48 | /* Card Header */ 49 | .card-header { 50 | position: relative; 51 | padding: 40px 30px 20px; 52 | text-align: center; 53 | background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); 54 | overflow: hidden; 55 | } 56 | 57 | .profile-image-container { 58 | position: relative; 59 | display: inline-block; 60 | margin-bottom: 20px; 61 | } 62 | 63 | .profile-image { 64 | width: 120px; 65 | height: 120px; 66 | border-radius: 50%; 67 | object-fit: cover; 68 | border: 4px solid rgba(255, 255, 255, 0.9); 69 | box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); 70 | transition: transform 0.3s ease, box-shadow 0.3s ease; 71 | animation: imageFloat 3s ease-in-out infinite; 72 | } 73 | 74 | .profile-image:hover { 75 | transform: scale(1.05); 76 | box-shadow: 0 15px 40px rgba(0, 0, 0, 0.3); 77 | } 78 | 79 | .status-indicator { 80 | position: absolute; 81 | bottom: 10px; 82 | right: 10px; 83 | width: 20px; 84 | height: 20px; 85 | background: #4ade80; 86 | border: 3px solid white; 87 | border-radius: 50%; 88 | animation: pulse 2s infinite; 89 | } 90 | 91 | .floating-elements { 92 | position: absolute; 93 | top: 0; 94 | left: 0; 95 | width: 100%; 96 | height: 100%; 97 | pointer-events: none; 98 | } 99 | 100 | .floating-element { 101 | position: absolute; 102 | background: rgba(255, 255, 255, 0.1); 103 | border-radius: 50%; 104 | animation: float 6s ease-in-out infinite; 105 | } 106 | 107 | .element-1 { 108 | width: 20px; 109 | height: 20px; 110 | top: 20%; 111 | left: 10%; 112 | animation-delay: 0s; 113 | } 114 | 115 | .element-2 { 116 | width: 15px; 117 | height: 15px; 118 | top: 60%; 119 | right: 15%; 120 | animation-delay: 2s; 121 | } 122 | 123 | .element-3 { 124 | width: 25px; 125 | height: 25px; 126 | bottom: 30%; 127 | left: 20%; 128 | animation-delay: 4s; 129 | } 130 | 131 | /* Card Body */ 132 | .card-body { 133 | padding: 30px; 134 | } 135 | 136 | .profile-name { 137 | font-size: 28px; 138 | font-weight: 700; 139 | color: #1f2937; 140 | margin-bottom: 8px; 141 | text-align: center; 142 | animation: fadeInUp 0.8s ease-out 0.2s both; 143 | } 144 | 145 | .profile-title { 146 | font-size: 16px; 147 | color: #6b7280; 148 | text-align: center; 149 | margin-bottom: 16px; 150 | font-weight: 500; 151 | animation: fadeInUp 0.8s ease-out 0.4s both; 152 | } 153 | 154 | .profile-description { 155 | font-size: 14px; 156 | color: #4b5563; 157 | text-align: center; 158 | line-height: 1.6; 159 | margin-bottom: 24px; 160 | animation: fadeInUp 0.8s ease-out 0.6s both; 161 | } 162 | 163 | /* Skills */ 164 | .skills-container { 165 | display: flex; 166 | flex-wrap: wrap; 167 | gap: 8px; 168 | justify-content: center; 169 | margin-bottom: 30px; 170 | animation: fadeInUp 0.8s ease-out 0.8s both; 171 | } 172 | 173 | .skill-tag { 174 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 175 | color: white; 176 | padding: 6px 12px; 177 | border-radius: 20px; 178 | font-size: 12px; 179 | font-weight: 500; 180 | transition: transform 0.2s ease; 181 | } 182 | 183 | .skill-tag:hover { 184 | transform: translateY(-2px); 185 | } 186 | 187 | /* Stats */ 188 | .stats-container { 189 | display: flex; 190 | justify-content: space-around; 191 | margin-bottom: 30px; 192 | animation: fadeInUp 0.8s ease-out 1s both; 193 | } 194 | 195 | .stat-item { 196 | text-align: center; 197 | } 198 | 199 | .stat-number { 200 | font-size: 24px; 201 | font-weight: 700; 202 | color: #1f2937; 203 | display: block; 204 | margin-bottom: 4px; 205 | } 206 | 207 | .stat-label { 208 | font-size: 12px; 209 | color: #6b7280; 210 | font-weight: 500; 211 | } 212 | 213 | /* Card Footer */ 214 | .card-footer { 215 | padding: 20px 30px 30px; 216 | border-top: 1px solid rgba(0, 0, 0, 0.05); 217 | } 218 | 219 | .social-links { 220 | display: flex; 221 | justify-content: center; 222 | gap: 16px; 223 | margin-bottom: 20px; 224 | animation: fadeInUp 0.8s ease-out 1.2s both; 225 | } 226 | 227 | .social-link { 228 | width: 44px; 229 | height: 44px; 230 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 231 | color: white; 232 | border-radius: 12px; 233 | display: flex; 234 | align-items: center; 235 | justify-content: center; 236 | text-decoration: none; 237 | font-size: 18px; 238 | transition: all 0.3s ease; 239 | position: relative; 240 | overflow: hidden; 241 | } 242 | 243 | .social-link::before { 244 | content: ''; 245 | position: absolute; 246 | top: 0; 247 | left: -100%; 248 | width: 100%; 249 | height: 100%; 250 | background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); 251 | transition: left 0.5s; 252 | } 253 | 254 | .social-link:hover::before { 255 | left: 100%; 256 | } 257 | 258 | .social-link:hover { 259 | transform: translateY(-3px); 260 | box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4); 261 | } 262 | 263 | .contact-btn { 264 | width: 100%; 265 | background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); 266 | color: white; 267 | border: none; 268 | padding: 14px 24px; 269 | border-radius: 12px; 270 | font-size: 16px; 271 | font-weight: 600; 272 | cursor: pointer; 273 | transition: all 0.3s ease; 274 | display: flex; 275 | align-items: center; 276 | justify-content: center; 277 | gap: 8px; 278 | animation: fadeInUp 0.8s ease-out 1.4s both; 279 | position: relative; 280 | overflow: hidden; 281 | } 282 | 283 | .contact-btn::before { 284 | content: ''; 285 | position: absolute; 286 | top: 0; 287 | left: -100%; 288 | width: 100%; 289 | height: 100%; 290 | background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); 291 | transition: left 0.5s; 292 | } 293 | 294 | .contact-btn:hover::before { 295 | left: 100%; 296 | } 297 | 298 | .contact-btn:hover { 299 | transform: translateY(-2px); 300 | box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4); 301 | } 302 | 303 | .contact-btn:active { 304 | transform: translateY(0); 305 | } 306 | 307 | /* Background Elements */ 308 | .bg-elements { 309 | position: fixed; 310 | top: 0; 311 | left: 0; 312 | width: 100%; 313 | height: 100%; 314 | pointer-events: none; 315 | z-index: 1; 316 | } 317 | 318 | .bg-circle { 319 | position: absolute; 320 | border-radius: 50%; 321 | background: rgba(255, 255, 255, 0.1); 322 | animation: float 8s ease-in-out infinite; 323 | } 324 | 325 | .circle-1 { 326 | width: 200px; 327 | height: 200px; 328 | top: 10%; 329 | left: -100px; 330 | animation-delay: 0s; 331 | } 332 | 333 | .circle-2 { 334 | width: 150px; 335 | height: 150px; 336 | top: 60%; 337 | right: -75px; 338 | animation-delay: 3s; 339 | } 340 | 341 | .circle-3 { 342 | width: 100px; 343 | height: 100px; 344 | bottom: 20%; 345 | left: 20%; 346 | animation-delay: 6s; 347 | } 348 | 349 | /* Animations */ 350 | @keyframes slideUp { 351 | from { 352 | opacity: 0; 353 | transform: translateY(50px); 354 | } 355 | to { 356 | opacity: 1; 357 | transform: translateY(0); 358 | } 359 | } 360 | 361 | @keyframes fadeInUp { 362 | from { 363 | opacity: 0; 364 | transform: translateY(20px); 365 | } 366 | to { 367 | opacity: 1; 368 | transform: translateY(0); 369 | } 370 | } 371 | 372 | @keyframes imageFloat { 373 | 0%, 100% { 374 | transform: translateY(0px); 375 | } 376 | 50% { 377 | transform: translateY(-10px); 378 | } 379 | } 380 | 381 | @keyframes float { 382 | 0%, 100% { 383 | transform: translateY(0px) rotate(0deg); 384 | } 385 | 50% { 386 | transform: translateY(-20px) rotate(180deg); 387 | } 388 | } 389 | 390 | @keyframes pulse { 391 | 0%, 100% { 392 | transform: scale(1); 393 | opacity: 1; 394 | } 395 | 50% { 396 | transform: scale(1.2); 397 | opacity: 0.8; 398 | } 399 | } 400 | 401 | /* Responsive Design */ 402 | @media (max-width: 480px) { 403 | .container { 404 | padding: 15px; 405 | max-width: 350px; 406 | } 407 | 408 | .card-header { 409 | padding: 30px 20px 15px; 410 | } 411 | 412 | .profile-image { 413 | width: 100px; 414 | height: 100px; 415 | } 416 | 417 | .card-body { 418 | padding: 25px 20px; 419 | } 420 | 421 | .profile-name { 422 | font-size: 24px; 423 | } 424 | 425 | .stats-container { 426 | gap: 10px; 427 | } 428 | 429 | .stat-number { 430 | font-size: 20px; 431 | } 432 | 433 | .card-footer { 434 | padding: 15px 20px 25px; 435 | } 436 | 437 | .social-link { 438 | width: 40px; 439 | height: 40px; 440 | font-size: 16px; 441 | } 442 | } 443 | 444 | @media (max-width: 360px) { 445 | .container { 446 | padding: 10px; 447 | max-width: 320px; 448 | } 449 | 450 | .profile-image { 451 | width: 90px; 452 | height: 90px; 453 | } 454 | 455 | .profile-name { 456 | font-size: 22px; 457 | } 458 | 459 | .skills-container { 460 | gap: 6px; 461 | } 462 | 463 | .skill-tag { 464 | padding: 4px 10px; 465 | font-size: 11px; 466 | } 467 | } 468 | 469 | /* Dark mode support */ 470 | @media (prefers-color-scheme: dark) { 471 | .profile-card { 472 | background: rgba(17, 24, 39, 0.95); 473 | color: #f9fafb; 474 | } 475 | 476 | .profile-name { 477 | color: #f9fafb; 478 | } 479 | 480 | .profile-title { 481 | color: #d1d5db; 482 | } 483 | 484 | .profile-description { 485 | color: #9ca3af; 486 | } 487 | 488 | .stat-number { 489 | color: #f9fafb; 490 | } 491 | 492 | .stat-label { 493 | color: #d1d5db; 494 | } 495 | 496 | .card-footer { 497 | border-top-color: rgba(255, 255, 255, 0.1); 498 | } 499 | } 500 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --dark-bg: #111827; 3 | --card-bg: rgba(42, 53, 71, 0.5); 4 | --border-color: rgba(255, 255, 255, 0.1); 5 | --text-white: #f9fafb; 6 | --text-muted: #9ca3af; 7 | --accent-primary: #8b5cf6; 8 | --accent-secondary: #a78bfa; 9 | --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; 10 | } 11 | 12 | /* --- Base & Aurora BG (Unchanged) --- */ 13 | * { 14 | margin: 0; 15 | padding: 0; 16 | box-sizing: border-box; 17 | text-decoration: none; 18 | } 19 | 20 | body { 21 | font-family: var(--font-family); 22 | background-color: var(--dark-bg); 23 | color: var(--text-white); 24 | overflow-x: hidden; 25 | } 26 | 27 | .background-aurora { 28 | position: fixed; 29 | top: 0; 30 | left: 0; 31 | width: 100%; 32 | height: 100%; 33 | background: 34 | radial-gradient(ellipse at top, #6366f130, transparent), 35 | radial-gradient(ellipse at bottom, #a855f730, transparent); 36 | z-index: -1; 37 | animation: pulse 10s infinite alternate; 38 | } 39 | 40 | @keyframes pulse { 41 | from { 42 | opacity: 0.5; 43 | } 44 | 45 | to { 46 | opacity: 1; 47 | } 48 | } 49 | 50 | /* --- Navbar, Hero, Search (Unchanged) --- */ 51 | .container { 52 | max-width: 1600px; 53 | margin: 0 auto; 54 | padding: 0 24px; 55 | } 56 | 57 | .navbar { 58 | position: sticky; 59 | top: 20px; 60 | z-index: 1000; 61 | display: flex; 62 | justify-content: space-between; 63 | align-items: center; 64 | padding: 15px 30px; 65 | margin-bottom: 20px; 66 | background: rgba(31, 41, 55, 0.5); 67 | backdrop-filter: blur(10px); 68 | -webkit-backdrop-filter: blur(10px); 69 | border: 1px solid var(--border-color); 70 | border-radius: 20px; 71 | box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.2); 72 | } 73 | 74 | .logo { 75 | font-size: 1.5rem; 76 | font-weight: 700; 77 | } 78 | 79 | .nav-links { 80 | display: flex; 81 | align-items: center; 82 | gap: 16px; 83 | } 84 | 85 | .nav-link { 86 | display: flex; 87 | align-items: center; 88 | gap: 8px; 89 | text-decoration: none; 90 | color: var(--text-muted); 91 | transition: color 0.3s; 92 | } 93 | 94 | .nav-link:hover { 95 | color: var(--text-white); 96 | } 97 | 98 | .nav-cta { 99 | background-color: var(--text-white); 100 | color: var(--dark-bg); 101 | padding: 10px 20px; 102 | border-radius: 12px; 103 | text-decoration: none; 104 | font-weight: 600; 105 | display: flex; 106 | align-items: center; 107 | gap: 8px; 108 | transition: 109 | transform 0.3s, 110 | background-color 0.3s; 111 | } 112 | 113 | .nav-cta:hover { 114 | transform: translateY(-2px); 115 | background-color: #e5e7eb; 116 | } 117 | 118 | .hero { 119 | text-align: center; 120 | padding: 60px 0; 121 | } 122 | 123 | .hero h1 { 124 | font-size: 3.5rem; 125 | font-weight: 700; 126 | margin-bottom: 16px; 127 | } 128 | 129 | .hero .subtitle { 130 | font-size: 1.1rem; 131 | color: var(--text-muted); 132 | max-width: 600px; 133 | margin: 0 auto 32px; 134 | } 135 | 136 | .hero-cta { 137 | display: inline-block; 138 | background-color: var(--accent-primary); 139 | color: var(--text-white); 140 | font-size: 1.1rem; 141 | font-weight: 600; 142 | text-decoration: none; 143 | padding: 16px 36px; 144 | border-radius: 50px; 145 | transition: 146 | transform 0.3s, 147 | background-color 0.3s; 148 | } 149 | 150 | .hero-cta:hover { 151 | transform: translateY(-3px); 152 | background-color: var(--accent-secondary); 153 | } 154 | 155 | .search-container { 156 | position: relative; 157 | max-width: 700px; 158 | margin: 0 auto 50px; 159 | } 160 | 161 | .search-container i { 162 | position: absolute; 163 | left: 20px; 164 | top: 50%; 165 | transform: translateY(-50%); 166 | color: var(--text-muted); 167 | } 168 | 169 | #search-input { 170 | width: 100%; 171 | background-color: #1f2937; 172 | color: var(--text-white); 173 | border: 1px solid var(--border-color); 174 | border-radius: 16px; 175 | padding: 16px 20px 16px 50px; 176 | font-size: 1rem; 177 | outline: none; 178 | transition: 179 | border-color 0.3s, 180 | box-shadow 0.3s; 181 | } 182 | 183 | #search-input:focus { 184 | border-color: var(--accent-primary); 185 | box-shadow: 0 0 15px rgba(139, 92, 246, 0.3); 186 | } 187 | 188 | /* --- Masonry Grid & Cards --- */ 189 | #contributors-container { 190 | column-count: 4; 191 | column-gap: 20px; 192 | } 193 | 194 | .contributor-card { 195 | background: var(--card-bg); 196 | backdrop-filter: blur(20px); 197 | -webkit-backdrop-filter: blur(20px); 198 | border: 1px solid var(--border-color); 199 | border-radius: 24px; 200 | margin-bottom: 20px; 201 | overflow: hidden; 202 | break-inside: avoid; 203 | animation: fadeIn 0.5s forwards; 204 | opacity: 0; 205 | transform: translateY(20px); 206 | box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); 207 | transition: 208 | transform 0.3s ease, 209 | box-shadow 0.3s ease; 210 | } 211 | 212 | .contributor-card.hidden { 213 | display: none; 214 | } 215 | 216 | .contributor-card:hover { 217 | transform: scale(1.03) translateY(-5px); 218 | box-shadow: 0 8px 40px rgba(0, 0, 0, 0.3); 219 | } 220 | 221 | .card-iframe-wrapper { 222 | width: 100%; 223 | aspect-ratio: 3 / 4; 224 | position: relative; 225 | cursor: pointer; 226 | border-top-left-radius: 24px; 227 | border-top-right-radius: 24px; 228 | overflow: hidden; 229 | } 230 | 231 | .skeleton { 232 | width: 100%; 233 | height: 100%; 234 | background: linear-gradient(90deg, #1f2937, #374151, #1f2937); 235 | background-size: 200% 100%; 236 | animation: shimmer 1.5s infinite; 237 | position: absolute; 238 | } 239 | 240 | @keyframes shimmer { 241 | 0% { 242 | background-position: 200% 0; 243 | } 244 | 245 | 100% { 246 | background-position: -200% 0; 247 | } 248 | } 249 | 250 | .card-iframe-wrapper iframe { 251 | width: 100%; 252 | height: 100%; 253 | border: none; 254 | opacity: 0; 255 | transition: opacity 0.5s ease; 256 | } 257 | 258 | .card-iframe-wrapper iframe.loaded { 259 | opacity: 1; 260 | } 261 | 262 | .card-footer { 263 | padding: 20px; 264 | display: flex; 265 | flex-direction: column; 266 | gap: 16px; 267 | } 268 | 269 | .card-footer-header { 270 | display: flex; 271 | justify-content: space-between; 272 | align-items: center; 273 | gap: 12px; 274 | } 275 | 276 | .card-footer-header h3 { 277 | font-size: 1.25rem; 278 | font-weight: 600; 279 | white-space: nowrap; 280 | overflow: hidden; 281 | text-overflow: ellipsis; 282 | } 283 | 284 | .card-footer-header a { 285 | color: var(--text-muted); 286 | font-size: 1.5rem; 287 | transition: color 0.3s; 288 | flex-shrink: 0; 289 | } 290 | 291 | .card-footer-header a:hover { 292 | color: var(--text-white); 293 | } 294 | 295 | .card-footer .bio { 296 | font-size: 0.9rem; 297 | color: var(--text-muted); 298 | line-height: 1.6; 299 | overflow: hidden; 300 | display: -webkit-box; 301 | -webkit-line-clamp: 1; 302 | -webkit-box-orient: vertical; 303 | } 304 | 305 | .tags-container { 306 | display: flex; 307 | flex-wrap: wrap; 308 | gap: 8px; 309 | } 310 | 311 | .tag { 312 | background: rgba(255, 255, 255, 0.1); 313 | color: var(--text-white); 314 | font-size: 0.75rem; 315 | font-weight: 500; 316 | padding: 5px 12px; 317 | border-radius: 20px; 318 | } 319 | 320 | /* NEW: "View Project" button on the card */ 321 | .view-project-btn { 322 | width: 100%; 323 | background: rgba(255, 255, 255, 0.1); 324 | color: var(--text-white); 325 | border: none; 326 | border-radius: 12px; 327 | padding: 12px; 328 | font-size: 1rem; 329 | font-weight: 600; 330 | font-family: var(--font-family); 331 | cursor: pointer; 332 | display: flex; 333 | align-items: center; 334 | justify-content: center; 335 | gap: 8px; 336 | transition: background-color 0.3s; 337 | } 338 | 339 | .view-project-btn:hover { 340 | background: rgba(255, 255, 255, 0.2); 341 | } 342 | 343 | /* --- Modals --- */ 344 | .modal-overlay { 345 | position: fixed; 346 | top: 0; 347 | left: 0; 348 | width: 100%; 349 | height: 100%; 350 | background: rgba(17, 24, 39, 0.8); 351 | backdrop-filter: blur(8px); 352 | display: flex; 353 | align-items: center; 354 | justify-content: center; 355 | opacity: 0; 356 | visibility: hidden; 357 | transition: 358 | opacity 0.3s, 359 | visibility 0.3s; 360 | z-index: 2000; 361 | padding: 20px; 362 | } 363 | 364 | .modal-overlay.visible { 365 | opacity: 1; 366 | visibility: visible; 367 | } 368 | 369 | .modal-content { 370 | background: #1f2937; 371 | border-radius: 16px; 372 | width: 90%; 373 | max-width: 800px; 374 | height: 80%; 375 | display: flex; 376 | flex-direction: column; 377 | position: relative; 378 | transform: scale(0.9); 379 | transition: transform 0.3s; 380 | } 381 | 382 | .modal-overlay.visible .modal-content { 383 | transform: scale(1); 384 | } 385 | 386 | .modal-close-button { 387 | position: absolute; 388 | top: -15px; 389 | right: -15px; 390 | background: var(--text-white); 391 | color: var(--dark-bg); 392 | border: none; 393 | border-radius: 50%; 394 | width: 30px; 395 | height: 30px; 396 | font-size: 1.5rem; 397 | cursor: pointer; 398 | z-index: 10; 399 | } 400 | 401 | #modal-iframe-container { 402 | flex-grow: 1; 403 | border-top-left-radius: 16px; 404 | border-top-right-radius: 16px; 405 | overflow: hidden; 406 | } 407 | 408 | #modal-iframe-container iframe { 409 | width: 100%; 410 | height: 100%; 411 | border: none; 412 | } 413 | 414 | #modal-info { 415 | padding: 20px; 416 | text-align: center; 417 | } 418 | 419 | #modal-info h3 { 420 | font-size: 1.5rem; 421 | margin-bottom: 8px; 422 | } 423 | 424 | #modal-info a { 425 | color: var(--accent-secondary); 426 | text-decoration: none; 427 | } 428 | 429 | /* NEW: Device Preview Modal */ 430 | .preview-modal-content { 431 | background: var(--dark-bg); 432 | border: 1px solid var(--border-color); 433 | border-radius: 16px; 434 | width: 100%; 435 | max-width: 1400px; 436 | height: 90vh; 437 | display: flex; 438 | flex-direction: column; 439 | transform: scale(0.9); 440 | transition: transform 0.3s; 441 | box-shadow: 0 10px 50px rgba(0, 0, 0, 0.5); 442 | } 443 | 444 | .modal-overlay.visible .preview-modal-content { 445 | transform: scale(1); 446 | } 447 | 448 | .preview-modal-header { 449 | display: flex; 450 | align-items: center; 451 | padding: 12px 20px; 452 | border-bottom: 1px solid var(--border-color); 453 | flex-shrink: 0; 454 | } 455 | 456 | .preview-modal-header h3 { 457 | font-size: 1rem; 458 | font-weight: 600; 459 | margin-right: auto; 460 | } 461 | 462 | .device-controls { 463 | display: flex; 464 | gap: 8px; 465 | background: #1f2937; 466 | border-radius: 10px; 467 | padding: 4px; 468 | } 469 | 470 | .device-controls button { 471 | background: transparent; 472 | border: none; 473 | color: var(--text-muted); 474 | padding: 8px 12px; 475 | border-radius: 8px; 476 | cursor: pointer; 477 | transition: 478 | background-color 0.3s, 479 | color 0.3s; 480 | } 481 | 482 | .device-controls button.active { 483 | background: var(--accent-primary); 484 | color: var(--text-white); 485 | } 486 | 487 | .preview-modal-header .modal-close-button { 488 | position: static; 489 | transform: none; 490 | width: 24px; 491 | height: 24px; 492 | font-size: 1rem; 493 | margin-left: 16px; 494 | } 495 | 496 | .preview-iframe-container { 497 | flex-grow: 1; 498 | display: flex; 499 | justify-content: center; 500 | align-items: center; 501 | padding: 20px; 502 | overflow: auto; 503 | } 504 | 505 | #preview-iframe { 506 | background-color: #fff; 507 | border-radius: 8px; 508 | box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3); 509 | border: 4px solid #333; 510 | transition: 511 | width 0.4s ease, 512 | height 0.4s ease; 513 | } 514 | 515 | .preview-iframe-container.view-desktop #preview-iframe { 516 | width: 100%; 517 | height: 100%; 518 | } 519 | 520 | .preview-iframe-container.view-tablet #preview-iframe { 521 | width: 768px; 522 | height: 1024px; 523 | max-width: 100%; 524 | max-height: 100%; 525 | } 526 | 527 | .preview-iframe-container.view-mobile #preview-iframe { 528 | width: 375px; 529 | height: 667px; 530 | max-width: 100%; 531 | max-height: 100%; 532 | } 533 | 534 | .preview-modal-footer { 535 | text-align: center; 536 | padding: 16px; 537 | border-top: 1px solid var(--border-color); 538 | flex-shrink: 0; 539 | } 540 | 541 | .preview-modal-footer .hero-cta { 542 | padding: 12px 24px; 543 | font-size: 1rem; 544 | } 545 | 546 | /* --- Final Polish & Responsive --- */ 547 | .no-results-message { 548 | display: none; 549 | text-align: center; 550 | color: var(--text-muted); 551 | padding: 40px; 552 | } 553 | 554 | #loader { 555 | display: none; 556 | width: 40px; 557 | height: 40px; 558 | border: 4px solid var(--border-color); 559 | border-top-color: var(--accent-secondary); 560 | border-radius: 50%; 561 | margin: 40px auto; 562 | animation: spin 1s linear infinite; 563 | } 564 | 565 | #loader.visible { 566 | display: block; 567 | } 568 | 569 | @keyframes spin { 570 | to { 571 | transform: rotate(30deg); 572 | } 573 | } 574 | 575 | #sentinel { 576 | height: 10px; 577 | } 578 | 579 | @media (max-width: 1200px) { 580 | #contributors-container { 581 | column-count: 3; 582 | } 583 | } 584 | 585 | @media (max-width: 900px) { 586 | #contributors-container { 587 | column-count: 2; 588 | } 589 | } 590 | 591 | @media (max-width: 600px) { 592 | #contributors-container { 593 | column-count: 1; 594 | } 595 | 596 | .hero h1 { 597 | font-size: 2.5rem; 598 | } 599 | } 600 | 601 | @keyframes fadeIn { 602 | to { 603 | opacity: 1; 604 | transform: translateY(0); 605 | } 606 | } 607 | --------------------------------------------------------------------------------