-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
587 lines (531 loc) · 27.7 KB
/
Copy pathscript.js
File metadata and controls
587 lines (531 loc) · 27.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
// Data for projects and experiences
const projectsData = {
project1: {
title: "Laravel CRUD Application",
overview: "A full-featured data management system for lecturers and courses with analytics dashboard, search, filters, and real-time validation.",
description: [
"A Laravel-based CRUD system to manage lecturer and course data. Features include a Chart.js-powered dashboard for data visualization, advanced search and filtering, real-time form validation, responsive UI with Tailwind CSS, and database seeding for testing.",
"The main goal is to provide an easy-to-use system for faculty admins or staff to manage academic data efficiently, speed up administrative processes, and minimize manual errors."
],
technologies: ["Laravel", "PHP", "MySQL", "Blade", "Tailwind CSS", "Chart.js", "GitHub Pages"],
images: [
"assets/project1-1.png", "assets/project1-2.png", "assets/project1-3.png",
"assets/project1-4.png", "assets/project1-5.png", "assets/project1-6.png",
"assets/project1-7.png", "assets/project1-8.png"
],
github: "https://github.com/panipanpan2006/crud-app"
},
project2: {
title: "Simple Grade Converter (PHP)",
overview: "A web app to manage student grades and automatically convert numerical scores to letter grades (A-E).",
description: [
"This project manages student grade data and automatically converts numerical scores to letter grades (A-E) for efficiency. I developed this Student Grade List app using pure PHP as practice in building simple yet functional web systems.",
"The app allows users to add, view, and manage student data efficiently, complete with automatic score-to-letter grade conversion.",
"The interface is built with external HTML and CSS for a clean and user-friendly design. Through this project, I practiced programming logic, data management, and basic CRUD concepts without frameworks, while strengthening my ability to write structured and scalable code.",
"This project serves as a strong foundation for understanding web application workflows and prepares me to build more complex systems in the future."
],
technologies: ["PHP", "HTML", "CSS", "JavaScript"],
images: ["assets/project2-1.png", "assets/project2-2.png", "assets/project2-3.png", "assets/project2-4.png"],
github: "https://github.com/panipanpan2006/php-daftar-mahasiswa"
},
project3: {
title: "Book Management System",
overview: "A simple Laravel-based book management app with CRUD, search, and year-based filtering.",
description: [
"A simple book management app built with Laravel featuring CRUD operations, search, and publication year filtering.",
"This Digital Book Management project is a simple web application built with PHP to help users efficiently manage book collections. Through CRUD operations (Create, Read, Update, Delete), users can add, view, update, and delete book information such as title, author, and publication year.",
"The interface is designed to be simple and user-friendly, while the backend logic processes data dynamically and connects to a database. This project demonstrates how PHP can be used to build practical data-driven applications and serves as a foundation for developing a more comprehensive digital library system in the future."
],
technologies: ["Laravel", "PHP", "MySQL", "Blade", "Bootstrap"],
images: [
"assets/project3-1.png", "assets/project3-2.png", "assets/project3-3.png",
"assets/project3-4.png", "assets/project3-5.png", "assets/project3-6.png"
],
github: "https://github.com/panipanpan2006/manage-buku"
},
project4: {
title: "Eloquent ES6+ JavaScript",
overview: "Modern JavaScript project implementing ES6+ features including arrow functions, destructuring, array, object manipulation, spread operator, utility, map, filter and reduce.",
description: [
"This project is a modern JavaScript-based application (ES6+) that I created to manage student data, displaying names, numerical scores, conversions to letter grades, and student rankings neatly in both the browser and console.",
"In this project, I used new features such as variable declarations with let and const, arrow functions, destructuring, spread operators, modern array methods (filter, map, reduce, sort), and template literals so that I not only built basic functions, but also implemented cleaner and more up-to-date coding practices.",
"With only simple HTML as the display, I connected JavaScript logic that processed the student object array, calculated the average, determined who passed based on the criteria, and sorted the highest rankings among them.",
"This project trains skills in front-end JavaScript and data processing, and experiments with new features in programming languages as a foundation for web application development."
],
technologies: ["JavaScript", "ES6+", "Async/Await", "DOM Manipulation", "HTML"],
images: [
"assets/project4-1.png", "assets/project4-2.png", "assets/project4-3.png", "assets/project4-4.png"
],
github: "https://github.com/panipanpan2006/es6-tugas"
},
project5: {
title: "Blade Layout for Campus Portal",
overview: "A comprehensive Laravel Blade template system for campus portal featuring modular components, layouts, and responsive design for academic management.",
description: [
"The Campus-Portal Project is a Laravel-based web platform designed to facilitate the management of student academic information at the Department of Electrical Engineering, Diponegoro University. This system has a modern and responsive interface, featuring various key features such as an academic dashboard, campus news, organizations, library, laboratory, and counseling services.",
"Each feature has a specific function. For example, the academic page displays class schedules, grades, and academic calendars, while the dashboard provides a summary of GPA, total credits, number of active courses, and student attendance rates in the form of interactive graphs and diagrams.",
"In the About section, the system displays the profiles of the lecturers teaching the courses, complete with their areas of expertise and academic roles, as well as department contact information. The entire project demonstrates the implementation of the MVC (Model-View-Controller) architecture typical of the Laravel framework, with the use of the Blade Template Engine for dynamic displays.",
"The UI components utilize Bootstrap to create a clean and responsive display, while the graphics on the dashboard likely use Chart.js for visualizing GPA data and grade distribution. Overall, Campus-Portal is an example of an academic web application that combines the efficiency of the Laravel backend with the convenience of a modern, interactive frontend display."
],
technologies: ["Laravel", "Blade", "Bootstrap", "HTML", "CSS", "JavaScript", "PHP-Framework", "Chart.js"],
images: [
"assets/project5-1.png", "assets/project5-2.png", "assets/project5-3.png", "assets/project5-4.png"
],
github: "https://github.com/panipanpan2006/campus-portal"
},
project6: {
title: "Personal Portfolio Website",
overview: "This project is a personal portfolio website built using HTML, CSS, and JavaScript to showcase my identity, skills, and projects as an Information Technology student.",
description: [
"This project is a personal portfolio website that I created to showcase my identity, skills, experience, and collection of projects as an Electrical Engineering student at Diponegoro University with a focus on information technology.",
"This website is built as a single page/multi-page framework using HTML for content structure, CSS for responsive styling and layout, and JavaScript for basic interactivity such as navigation, light animation, or display effects.",
"The interface is designed to be clean, professional, and easy to read, featuring sections such as “About Me” “Skills” “Projects” and “Contact”. This project demonstrates that I understand the fundamentals of front-end web development and am capable of presenting myself as an active developer building my own digital portfolio."
],
technologies: ["HTML", "Tailwind CSS", "JavaScript"],
images: [
"assets/project6-1.png", "assets/project6-2.png", "assets/project6-3.png",
"assets/project6-4.png", "assets/project6-5.png", "assets/project6-6.png"
],
github: "https://github.com/panipanpan2006/portfolio-website"
}
};
const experienceData = {
kepanitiaan1: {
title: "Freshman Orientation (PMB)",
role: "Evaluation",
date: "August 2024",
location: "Electrical Engineering Department, Faculty of Engineering, Diponegoro University",
description: "Worked behind the scenes to develop evaluation parameters for each participant and compile scores for PMB 2024. Responsible for assessing new student activities during the orientation period. Evaluated participation and performance in various campus introduction activities. Coordinated with other divisions to ensure smooth event execution.",
images: [
"assets/kepanitiaan1-1.png",
"assets/kepanitiaan1-2.png",
"assets/kepanitiaan1-3.png"
]
},
kepanitiaan2: {
title: "Basic Leadership Training (LKMM-Pra Dasar) Electrical Engineering 2024",
role: "Facilitator",
date: "November 2024",
location: "Electrical Engineering Department, Faculty of Engineering, Diponegoro University",
description: "Facilitated discussions and activities in the Student Leadership and Management Training program. Guided participants in understanding leadership and organizational concepts. Helped participants develop essential soft skills for organizational involvement. Shaped students into responsible, independent leaders with clear vision and effective teamwork skills.",
images: [
"assets/kepanitiaan2-1.png",
"assets/kepanitiaan2-2.png",
"assets/kepanitiaan2-3.png"
]
},
magang: {
title: "Internship at PT. Lentera Bumi Nusantara",
role: "Data Logger Team",
date: "June - July 2024",
location: "Ciheras, Cipatujah District, Tasikmalaya Regency, West Java 46189",
description: "Interned at PT. Lentera Bumi Nusantara as part of the Data Logger team for 1 month. Responsible for collecting and analyzing wind speed data using anemometers at 5m and 10m heights. Activities included: Learning fundamentals of data logging and renewable energy power plants, Creating data analysis reports using CR850 data logger at different heights, Weekly presentation of analysis results, and Collaborating with the data logger team.",
images: [
"assets/magang-1.png",
"assets/magang-2.png",
"assets/magang-3.png",
"assets/magang-4.png",
"assets/magang-5.png",
"assets/magang-6.png",
"assets/magang-7.png",
"assets/magang-8.png",
"assets/magang-9.png"
]
},
prestasi1: {
title: "1st Place - Table Tennis POR FT 2025",
role: "Men's Doubles Athlete",
date: "20-21 September 2025",
location: "Student Center UNDIP, Jl. Prof. Eko Budihardjo, Tembalang, Semarang, Central Java 50275",
description: "Won 1st place in the men's doubles table tennis competition at the Faculty of Engineering Sports Week 2025. Defeated multiple teams from various faculties over a 2-day tournament. Successfully advanced through quarterfinals, semifinals, and finals against strong opponents from different faculties.",
images: [
"assets/prestasi1-1.png",
"assets/prestasi1-2.png",
"assets/prestasi1-3.png"
]
},
prestasi2: {
title: "3rd Place - Band Category, Engineering Festival 2023",
role: "Guitarist",
date: "17 November 2023",
location: "Atap Langit Coffee n Eatery, Jl. Durian Raya, Pedalangan, Banyumanik, Semarang, Central Java 50268",
description: "Won 3rd place in the band competition at Engineering Festival UNDIP 2023. Performed two designated songs and got evaluated by the judge. Competed against 10 bands from various faculties at Diponegoro University. Performance evaluated on musicality, stagecraft, creativity, and originality.",
images: [
"assets/prestasi2-1.png",
"assets/prestasi2-2.png",
"assets/prestasi2-3.png"
]
}
};
// ===== MOBILE MENU TOGGLE - SMOOTH VERSION =====
document.addEventListener('DOMContentLoaded', function() {
const menuToggle = document.getElementById('menuToggle');
const navMenu = document.getElementById('navMenu');
const navLinks = document.querySelectorAll('.nav-link');
let isAnimating = false;
if (!menuToggle || !navMenu) {
console.error('❌ Menu elements not found!');
return;
}
// Force initial state
navMenu.classList.remove('active');
menuToggle.classList.remove('active');
document.body.style.overflow = 'auto';
// Toggle menu function with animation lock
function toggleMenu(e) {
if (e) {
e.preventDefault();
e.stopPropagation();
}
if (isAnimating) return;
isAnimating = true;
const isActive = navMenu.classList.contains('active');
if (isActive) {
// Close menu
menuToggle.classList.remove('active');
navMenu.classList.remove('active');
document.body.style.overflow = 'auto';
} else {
// Open menu
menuToggle.classList.add('active');
navMenu.classList.add('active');
document.body.style.overflow = 'hidden';
}
// Reset animation lock after transition
setTimeout(() => {
isAnimating = false;
}, 300);
}
// Close menu function
function closeMenu() {
if (isAnimating) return;
menuToggle.classList.remove('active');
navMenu.classList.remove('active');
document.body.style.overflow = 'auto';
}
// Hamburger button click
menuToggle.addEventListener('click', toggleMenu);
// Close when clicking nav links
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
closeMenu();
});
});
// Close when clicking outside
document.addEventListener('click', function(e) {
const isClickInsideMenu = navMenu.contains(e.target);
const isClickOnToggle = menuToggle.contains(e.target);
if (!isClickInsideMenu && !isClickOnToggle && navMenu.classList.contains('active')) {
closeMenu();
}
});
// Prevent menu clicks from closing
navMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
});
// Smooth scroll function
function scrollToSection(sectionId) {
const element = document.getElementById(sectionId);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
// Close mobile menu
const navMenu = document.getElementById('navMenu');
const menuToggle = document.getElementById('menuToggle');
if (navMenu && navMenu.classList.contains('active')) {
navMenu.classList.remove('active');
menuToggle.classList.remove('active');
document.body.style.overflow = 'auto';
}
}
// Nav link click handlers
document.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href').substring(1);
scrollToSection(targetId);
});
});
// UPDATE ACTIVE NAV LINK
function updateActiveNavLink() {
const sections = document.querySelectorAll('.section');
const navLinks = document.querySelectorAll('.nav-link');
let current = 'home';
let closestSection = null;
let closestDistance = Infinity;
sections.forEach(section => {
const rect = section.getBoundingClientRect();
const sectionTop = rect.top;
const sectionHeight = rect.height;
const sectionCenter = sectionTop + sectionHeight / 2;
const viewportCenter = window.innerHeight / 2;
const distance = Math.abs(sectionCenter - viewportCenter);
if (sectionTop < window.innerHeight * 0.7 && sectionTop + sectionHeight > 0) {
if (distance < closestDistance) {
closestDistance = distance;
closestSection = section;
}
}
});
if (closestSection) {
current = closestSection.getAttribute('id');
}
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${current}`) {
link.classList.add('active');
}
});
}
window.addEventListener('scroll', updateActiveNavLink);
window.addEventListener('load', updateActiveNavLink);
// Toggle Projects - See More Functionality
let projectsExpanded = false;
function toggleProjects() {
const projectCards = document.querySelectorAll('.project-card');
const seeMoreBtn = document.getElementById('seeMoreBtn');
const btnText = seeMoreBtn.querySelector('span');
projectsExpanded = !projectsExpanded;
if (projectsExpanded) {
projectCards.forEach((card, index) => {
if (card.classList.contains('project-hidden')) {
card.style.display = 'block';
setTimeout(() => {
card.style.opacity = '1';
card.style.transform = 'translateY(0)';
}, (index - 3) * 100);
}
});
btnText.textContent = 'Show Less';
seeMoreBtn.classList.add('show-less');
} else {
projectCards.forEach(card => {
if (card.classList.contains('project-hidden')) {
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
setTimeout(() => {
card.style.display = 'none';
}, 300);
}
});
btnText.textContent = 'See More Projects';
seeMoreBtn.classList.remove('show-less');
setTimeout(() => {
document.getElementById('projects').scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 400);
}
}
// Initialize projects
document.addEventListener('DOMContentLoaded', function() {
const projectCards = document.querySelectorAll('.project-card');
projectCards.forEach(card => {
if (card.classList.contains('project-hidden')) {
card.style.display = 'none';
card.style.opacity = '0';
card.style.transform = 'translateY(20px)';
}
});
});
// Contact Form
const contactForm = document.getElementById('contactForm');
if (contactForm) {
contactForm.addEventListener('submit', function(e) {
const submitBtn = this.querySelector('.btn-send-message');
const btnOriginal = submitBtn.innerHTML;
submitBtn.disabled = true;
submitBtn.innerHTML = '<span>Sending...</span>';
setTimeout(() => {
submitBtn.disabled = false;
submitBtn.innerHTML = btnOriginal;
}, 1000);
});
}
// Open Project Modal
function openProjectModal(projectId) {
const project = projectsData[projectId];
if (!project) {
console.error("ERROR: Project not found!", projectId);
return;
}
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h2>${project.title}</h2>
<button class="modal-close" onclick="closeModal(this)">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div class="modal-body">
<div class="modal-overview">
<h3>Project Overview</h3>
<p>${project.overview}</p>
</div>
<div class="modal-gallery">
<h3>Gallery</h3>
<div class="modal-images">
${project.images.map(img => `
<img src="${img}" alt="${project.title}" class="modal-image" onclick="openLightbox('${img}')">
`).join('')}
</div>
</div>
<div class="modal-technologies">
<h3>Technology Used</h3>
<div class="tech-badges">
${project.technologies.map(tech => `
<span class="tech-badge">${tech}</span>
`).join('')}
</div>
</div>
<div class="modal-description">
<h3>Full Description</h3>
${Array.isArray(project.description)
? project.description.map(p => `<p class="description-paragraph">${p}</p>`).join('')
: `<p class="description-paragraph">${project.description}</p>`
}
</div>
</div>
</div>
`;
document.getElementById('modalContainer').appendChild(modal);
document.body.style.overflow = 'hidden';
}
// Open Experience Modal
function openExperienceModal(experienceId) {
const exp = experienceData[experienceId];
if (!exp) return;
const modal = document.createElement('div');
modal.className = 'modal-overlay';
modal.innerHTML = `
<div class="modal-content">
<div class="modal-header">
<h2>${exp.title}</h2>
<button class="modal-close" onclick="closeModal(this)">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div class="modal-body">
<div class="modal-info">
<div class="modal-info-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
<span><strong>Role:</strong> ${exp.role}</span>
</div>
<div class="modal-info-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
<line x1="16" y1="2" x2="16" y2="6"></line>
<line x1="8" y1="2" x2="8" y2="6"></line>
<line x1="3" y1="10" x2="21" y2="10"></line>
</svg>
<span><strong>Time:</strong> ${exp.date}</span>
</div>
<div class="modal-info-item">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
<circle cx="12" cy="10" r="3"></circle>
</svg>
<span><strong>Location:</strong> ${exp.location}</span>
</div>
</div>
<div class="modal-description">
<h3>Description</h3>
<p>${exp.description}</p>
</div>
<div class="modal-gallery">
<h3>Documentation</h3>
<div class="modal-images">
${exp.images.map(img => `
<img src="${img}" alt="${exp.title}" class="modal-image" onclick="openLightbox('${img}')">
`).join('')}
</div>
</div>
</div>
</div>
`;
document.getElementById('modalContainer').appendChild(modal);
document.body.style.overflow = 'hidden';
}
// Close Modal
function closeModal(button) {
const modal = button.closest('.modal-overlay');
modal.remove();
document.body.style.overflow = 'auto';
}
// Lightbox
function openLightbox(imageSrc) {
const lightbox = document.createElement('div');
lightbox.className = 'lightbox';
lightbox.innerHTML = `<img src="${imageSrc}" alt="Preview">`;
lightbox.onclick = function() { this.remove(); };
document.body.appendChild(lightbox);
}
// Click outside modal
document.addEventListener('click', function(e) {
if (e.target.classList.contains('modal-overlay')) {
e.target.remove();
document.body.style.overflow = 'auto';
}
});
// Intersection Observer for animations
const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -100px 0px' };
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Progress Bar Animation
const progressObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate');
progressObserver.unobserve(entry.target);
}
});
}, { threshold: 0.3 });
// DOM Loaded - Initialize everything
document.addEventListener('DOMContentLoaded', function() {
const elementsToAnimate = document.querySelectorAll('.card, .skill-card, .project-card, .experience-card, .contact-card');
elementsToAnimate.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(30px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
// Progress bar animation
document.querySelectorAll('.skill-card').forEach(card => {
progressObserver.observe(card);
});
// Ripple effect
document.querySelectorAll('.btn-view, .btn-github').forEach(btn => {
btn.addEventListener('click', function(e) {
const ripple = document.createElement('span');
const rect = this.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.width = ripple.style.height = size + 'px';
ripple.style.left = x + 'px';
ripple.style.top = y + 'px';
ripple.classList.add('ripple');
this.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
});
});
console.log('%c👋 Welcome to Ivan\'s Portfolio!', 'color: #06b6d4; font-size: 20px; font-weight: bold;');
console.log('%c💻 Built with HTML, CSS, and JavaScript', 'color: #3b82f6; font-size: 14px;');
console.log('%c✅ Mobile menu initialized successfully!', 'color: #22c55e; font-size: 14px;');
});