| Server IP : 167.235.67.158 / Your IP : 216.73.216.95 Web Server : Apache System : Linux ubuntu-8gb-nbg1-1 6.8.0-111-generic #111-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 11 23:16:02 UTC 2026 x86_64 User : upstairsbar.co.uk ( 982) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/iwillruhl/ |
Upload File : |
// Mobile Navigation Toggle
const hamburger = document.querySelector('.hamburger');
const navMenu = document.querySelector('.nav-menu');
hamburger.addEventListener('click', () => {
hamburger.classList.toggle('active');
navMenu.classList.toggle('active');
});
// Close mobile menu when clicking on a link or logo
function closeMobileMenu() {
hamburger.classList.remove('active');
navMenu.classList.remove('active');
}
document.querySelectorAll('.nav-menu a').forEach(link => {
link.addEventListener('click', () => {
closeMobileMenu();
});
});
// Also close when clicking logo (e.g. #home)
const logo = document.querySelector('.navbar .logo');
if (logo) {
logo.addEventListener('click', closeMobileMenu);
}
// Smooth scroll for navigation links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offsetTop = target.offsetTop - 80;
window.scrollTo({
top: offsetTop,
behavior: 'smooth'
});
}
});
});
// Project Modal
const projectModal = document.getElementById('projectModal');
const modalClose = document.querySelector('.modal-close');
const modalOverlay = document.querySelector('.modal-overlay');
const workItems = document.querySelectorAll('.work-item');
// Sample project data - replace with your actual project data
const projectsData = {
1: {
category: 'Explainer video',
title: 'Cambridge Little Steps',
client: 'Cambridge University Press',
media: '<iframe src="https://www.youtube.com/embed/pMp9ExdeH8w" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe>',
overview: 'An explainer video created for Cambridge Little Steps book series, turning pages from the book into animations to show the wonderful universes you get to explore!',
challenge: 'The main challenge was transforming a set of pre-designed assets into a cohesive and engaging animated piece that felt clear, approachable, and educational. A lot of time was taken turning all the illustrations into animatable characters using Photoshop and Illustrator. The video needed to balance clarity with visual interest, ensuring the messaging remained easy to follow for a broad audience while maintaining a consistent pace and tone throughout.',
approach: 'After reworking all the illustrations for After Effects, I focused on creating clean motion that enhanced the storytelling without overwhelming the content. I carefully timed transitions, character movement, and on-screen text to support the narrative and guide the viewer\'s attention as the target audience would be parents and children.\n\nThe animation was built to feel smooth and friendly, using subtle easing, consistent motion language, and clear visual hierarchy. Throughout the process, I prioritized readability and rhythm, ensuring each section flowed naturally into the next while reinforcing the key messages of the book series.',
result: 'The final video delivers a polished and engaging explainer that clearly communicates the core ideas behind Cambridge Little Steps. The animation brings the provided assets to life, resulting in a professional, approachable piece that effectively supports the program\'s messaging and educational goals.',
services: ['Motion Graphics', 'Animation'],
tools: ['Adobe After Effects', 'Adobe Photoshop', 'Adobe Illustrator'],
year: '2020'
},
2: {
category: 'Motion Graphics, Video Editing',
title: 'Nokia\'s Automation Explained Series',
client: 'Nokia',
media: '<div class="video-carousel"><button class="carousel-arrow carousel-prev" aria-label="Previous video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button><div class="video-carousel-container"><div class="video-carousel-track"><div class="video-carousel-item active"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/zky9FU2TUO8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/-RrdaPNZM_c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/b6s6Q7636_4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/RW_3aL--N6w" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/d1prW7BVTto" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div></div></div><button class="carousel-arrow carousel-next" aria-label="Next video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></div>',
overview: 'A mix of talking heads and animation to convey Nokia\'s ideas and simplifying them for the consumer.',
challenge: 'As I was provided the storyboard to this video series, a lot of the challenge came down to graphical elements that matched Nokia\'s guidelines and making smooth transitions between talking heads to animation. There were also numerous videos for this series so another challenge was time managing and keeping organised to keep up with timelines.\n\nA lot of information is thrown at the viewer so it was my job to make sure the pacing and the information got across efficiently in a short amount of time.',
approach: 'My approach to this series of videos was mostly making sure the information gets across to the viewer without being lost in the visuals. As the Nokia branding was quite simplistic, it was trying to utilise that alongside simple yet effective motion to keep the viewer engaged. As I was working with Workspace1 (a creative agency), the collaboration allowed for multiple opinions and made sure everything was understood.',
result: 'The client was very happy with the overall result. Even with time constraints and a lot of back and forth, the team and I managed to provide a series that I feel is better understood and educational than a wall of text. Nokia and I were very happy especially with the transitions and the overall design of the series.',
services: ['Motion Graphics', 'Video Editing', 'Graphic Design'],
tools: ['Adobe After Effects', 'Adobe Premiere Pro', 'Adobe Illustrator'],
year: '2024'
},
3: {
category: 'Interactive Animation',
title: 'National Space Centre',
client: 'National Space Centre',
media: '<div class="video-carousel"><button class="carousel-arrow carousel-prev" aria-label="Previous video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button><div class="video-carousel-container"><div class="video-carousel-track"><div class="video-carousel-item active"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/8fDsNDXU_wk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/1inS6FbVYBE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div></div></div><button class="carousel-arrow carousel-next" aria-label="Next video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></div>',
overview: 'I was integrated into the Creative team at National Space Centre to transform graphical elements into interactive animations for an educational exhibition. The animations were designed to help visitors learn about pollution and how it affects our Earth through engaging, interactive experiences.',
challenge: 'The main challenge was converting static graphical elements into interactive animations that would work seamlessly within the exhibition environment. The animations needed to be educational, engaging, and accessible to visitors of all ages, while effectively communicating complex information about pollution and environmental impact.',
approach: 'Working closely with the Creative team at National Space Centre, I took the graphical elements I was given and developed them into interactive animations. I focused on creating motion that would draw visitors in and make the educational content about pollution and Earth\'s environment both accessible and memorable.\n\nDue to the nature of how the animations were exported for the exhibition system, I can\'t show the actual animation videos here. However, the first video showcases how the animations look within the exhibit, while the second video is an overview of the whole project that I was happily a part of!',
result: 'The interactive animations successfully enhanced the National Space Centre exhibition, providing visitors with an engaging way to learn about pollution and environmental impact. The project brought the graphical elements to life in a way that supported the educational goals of the exhibition and created memorable experiences for visitors.',
services: ['Interactive Animation', 'Motion Graphics'],
tools: ['Adobe After Effects', 'Cinema 4D', 'Illustrator'],
year: '2022'
},
4: {
category: 'Branding and Motion Graphics',
title: 'Seed Creativity',
client: 'Seed Creativity',
media: '<div class="video-carousel"><button class="carousel-arrow carousel-prev" aria-label="Previous video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button><div class="video-carousel-container"><div class="video-carousel-track"><div class="video-carousel-item active"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/nLmGRKB4dEE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/NELL5bUFF_A" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/em-_jnnRPHo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/hwhD0kjtoxo" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div></div></div><button class="carousel-arrow carousel-next" aria-label="Next video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></div>',
overview: 'Working with the Seed Creativity team to create motion-branding for their new look. This involved creating all the motion graphics for the ident, showreel, and beacons that were showcased in the middle of Leicester Highcross.',
challenge: 'The challenge was developing a cohesive motion-branding system that would work across multiple applications—from idents and showreels to physical installations like the beacons at Leicester Highcross. The Memphis Pop style needed to feel consistent while allowing for creative freedom in shape animations, transitions, and idents.',
approach: 'I worked closely with the Seed Creativity team to develop their new motion-branding identity. The Memphis Pop style provided an excellent foundation that allowed me creative freedom to explore funky shape animations, dynamic transitions, and distinctive idents.\n\nI created motion graphics for various applications including the brand ident, showreel sequences, and the beacons displayed at Leicester Highcross. This style gave me the opportunity to experiment with bold, playful animations that captured the energetic and creative spirit of Seed Creativity.',
result: 'The motion-branding successfully established Seed Creativity\'s new visual identity across all touchpoints. The Memphis Pop style animations brought a fresh, dynamic energy to the brand, from the digital idents and showreel to the physical beacon installations at Leicester Highcross, creating a cohesive and memorable brand experience.',
services: ['Kinetic Typography', 'Branding', 'Motion Graphics', 'Video Editing'],
tools: ['Adobe After Effects', 'Premiere Pro', 'Adobe Illustrator'],
year: '2017'
},
5: {
category: 'Graphic & Motion Design',
title: 'Cambridge Dictionary',
client: 'Cambridge University Press',
media: '<iframe src="https://www.youtube.com/embed/RQWttg5qT4c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe>',
overview: 'A motion graphic design project for Cambridge Dictionary, telling the story of the evolution of the English Dictionary through animation.',
challenge: 'The challenge was taking only a script and voice overs and creating a complete visual narrative from scratch. I needed to develop a storyboard, design all graphics, and create animations that would effectively communicate the evolution of the English Dictionary in an engaging and educational way.',
approach: 'With complete creative control over the entire visual process. Starting from the storyboard, I did research of past animations that Cambridge have done and found that their approach was creating very simplistic characters to make the messaging very clear. Knowing this, I made each character into bean-like beings to follow that aesthetic and kept it consistent throughout.',
result: 'The client was very happy with the overall result and praised my communication skills as I went over all my decisions throughout the creative process.',
services: ['Graphic Design', 'Motion Graphics', 'Storyboarding'],
tools: ['Adobe After Effects', 'Adobe Illustrator'],
year: '2017'
},
6: {
category: 'Explainer Videos',
title: 'Deloitte Explainer Videos',
client: 'Deloitte',
media: '<div class="video-carousel"><button class="carousel-arrow carousel-prev" aria-label="Previous video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button><div class="video-carousel-container"><div class="video-carousel-track"><div class="video-carousel-item active"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/KyQsN8TiLU4" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div><div class="video-carousel-item"><div style="width: 100%; aspect-ratio: 16 / 9; position: relative; background: #000; border-radius: 12px; overflow: hidden;"><iframe src="https://www.youtube.com/embed/yWxx1hjIDDw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen style="width: 100%; height: 100%; position: absolute; top: 0; left: 0;"></iframe></div></div></div></div><button class="carousel-arrow carousel-next" aria-label="Next video"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 18L15 12L9 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></button></div>',
overview: 'Two explainer videos created for Deloitte, each with a different scope and creative involvement. The first video involved full creative ownership from storyboarding through to final animation, while the second focused specifically on bringing the animation to life.',
challenge: 'Working with the Squeezed Media team on these Deloitte projects required adapting to different creative workflows and project scopes. The first video needed a complete creative vision from the ground up, while the second required seamlessly integrating into an existing design direction and focusing purely on the animation execution.',
approach: 'For the first video, I was brought onto the Squeezed Media team and took on the entire creative process. I developed the storyboard, created all the graphic design elements, and handled the full animation pipeline. This allowed for a cohesive vision from concept to completion.\n\nFor the second video, I joined the project specifically for the animation phase. Working with the existing design assets and creative direction, I focused on bringing the visuals to life through smooth, purposeful motion that enhanced the storytelling and maintained consistency with the established visual language.',
result: 'Both videos successfully delivered clear, engaging explainer content for Deloitte. The first video showcased a complete creative vision from storyboard to screen, while the second demonstrated seamless collaboration and animation expertise within an existing creative framework. Both projects met Deloitte\'s communication goals and maintained a professional, polished aesthetic.',
services: ['Motion Graphics', 'Animation', 'Graphic Design', 'Storyboarding'],
tools: ['Adobe After Effects', 'Adobe Illustrator'],
year: '2021'
}
// Add more projects as needed
};
function openProjectModal(projectId) {
const project = projectsData[projectId];
if (!project) return;
// Update modal content
document.querySelector('.modal-header .project-category').textContent = project.category;
document.querySelector('.modal-header .project-title').textContent = project.title;
document.querySelector('.modal-header .project-client').textContent = `Client: ${project.client}`;
const mediaContainer = document.querySelector('.modal-media .media-container');
mediaContainer.innerHTML = project.media;
// Store original iframe sources for restoration and restore any that were cleared
const iframes = mediaContainer.querySelectorAll('iframe');
iframes.forEach(iframe => {
const originalSrc = iframe.getAttribute('data-original-src') || iframe.src;
iframe.setAttribute('data-original-src', originalSrc);
// Restore src if it was cleared
if (!iframe.src && originalSrc) {
iframe.src = originalSrc;
}
});
// Initialize video carousel if it exists
initVideoCarousel();
const detailsMain = document.querySelector('.modal-details .details-main');
// Handle paragraph breaks in text content
const formatText = (text) => {
return text.split('\n\n').map(para => `<p>${para}</p>`).join('');
};
detailsMain.innerHTML = `
<div class="detail-section">
<h2>Overview</h2>
<p>${project.overview}</p>
</div>
<div class="detail-section">
<h2>The Challenge</h2>
<p>${project.challenge}</p>
</div>
<div class="detail-section">
<h2>My Approach</h2>
${formatText(project.approach)}
</div>
<div class="detail-section">
<h2>The Result</h2>
<p>${project.result}</p>
</div>
`;
const sidebar = document.querySelector('.modal-details .details-sidebar');
sidebar.innerHTML = `
<div class="sidebar-section">
<h3>Services</h3>
<ul class="services-list">
${project.services.map(service => `<li>${service}</li>`).join('')}
</ul>
</div>
<div class="sidebar-section">
<h3>Tools Used</h3>
<ul class="tools-list">
${project.tools.map(tool => `<li>${tool}</li>`).join('')}
</ul>
</div>
<div class="sidebar-section">
<h3>Year</h3>
<p>${project.year}</p>
</div>
`;
projectModal.classList.add('active');
document.body.style.overflow = 'hidden';
// Scroll modal to top when opened
setTimeout(() => {
projectModal.scrollTop = 0;
const modalContent = document.querySelector('.modal-content');
if (modalContent) {
modalContent.scrollTop = 0;
}
}, 10);
}
function closeProjectModal() {
// Stop all videos in the modal
const modalVideos = projectModal.querySelectorAll('video, iframe');
modalVideos.forEach(video => {
if (video.tagName === 'VIDEO') {
video.pause();
video.currentTime = 0;
} else if (video.tagName === 'IFRAME') {
// For iframes (YouTube/Vimeo), remove src to stop playback
// The src will be restored when the modal opens again
if (video.hasAttribute('data-original-src')) {
video.src = '';
}
}
});
projectModal.classList.remove('active');
document.body.style.overflow = '';
}
// Video Carousel functionality
function initVideoCarousel() {
const carousel = document.querySelector('.video-carousel');
if (!carousel) return;
const track = carousel.querySelector('.video-carousel-track');
const items = carousel.querySelectorAll('.video-carousel-item');
const prevBtn = carousel.querySelector('.carousel-prev');
const nextBtn = carousel.querySelector('.carousel-next');
let currentIndex = 0;
const totalItems = items.length;
// Store original iframe sources on initialization
items.forEach(item => {
const iframes = item.querySelectorAll('iframe');
iframes.forEach(iframe => {
if (!iframe.hasAttribute('data-original-src')) {
iframe.setAttribute('data-original-src', iframe.src);
}
});
});
function stopAllVideos(exceptIndex = null) {
// Stop all videos except the one at exceptIndex
items.forEach((item, i) => {
if (exceptIndex !== null && i === exceptIndex) {
return; // Skip the active item
}
const videos = item.querySelectorAll('video, iframe');
videos.forEach(video => {
if (video.tagName === 'VIDEO') {
video.pause();
video.currentTime = 0;
} else if (video.tagName === 'IFRAME') {
// For iframes (YouTube/Vimeo), clear src to stop playback
// Make sure we've stored the original src first
if (!video.hasAttribute('data-original-src') && video.src) {
video.setAttribute('data-original-src', video.src);
}
video.src = '';
}
});
});
}
function showSlide(index) {
// Stop all videos except the one we're about to show
stopAllVideos(index);
// Show the new slide
items.forEach((item, i) => {
item.classList.toggle('active', i === index);
});
// Restore the iframe src for the newly active slide
const activeItem = items[index];
const activeIframes = activeItem.querySelectorAll('iframe');
activeIframes.forEach(iframe => {
const originalSrc = iframe.getAttribute('data-original-src');
if (originalSrc) {
// Use a small delay to ensure the iframe reloads properly
setTimeout(() => {
iframe.src = originalSrc;
}, 50);
}
});
}
function nextSlide() {
currentIndex = (currentIndex + 1) % totalItems;
showSlide(currentIndex);
}
function prevSlide() {
currentIndex = (currentIndex - 1 + totalItems) % totalItems;
showSlide(currentIndex);
}
if (nextBtn) {
nextBtn.addEventListener('click', nextSlide);
}
if (prevBtn) {
prevBtn.addEventListener('click', prevSlide);
}
// Initialize first slide (don't stop videos on initial load)
items.forEach((item, i) => {
item.classList.toggle('active', i === 0);
});
}
// Open modal on work item click
workItems.forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const projectId = item.getAttribute('data-project');
if (projectId) {
openProjectModal(projectId);
}
});
});
// Open modal on category section project links
document.querySelectorAll('.project-link').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const projectId = link.getAttribute('data-project');
if (projectId) {
openProjectModal(projectId);
}
});
});
// Close modal handlers
modalClose.addEventListener('click', closeProjectModal);
modalOverlay.addEventListener('click', closeProjectModal);
// Close modal on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && projectModal.classList.contains('active')) {
closeProjectModal();
}
});
// Intersection Observer for fade-in animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Observe work items
workItems.forEach(item => {
item.style.opacity = '0';
item.style.transform = 'translateY(30px)';
item.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(item);
});
// Observe category sections for Cub-style scroll reveal
const sectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -80px 0px' });
document.querySelectorAll('.category-section').forEach(section => {
sectionObserver.observe(section);
});
// Navbar: transparent over hero, solid background when scrolled
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
// Initialize Lottie animation for showreel text
function initShowreelAnimation() {
const animationContainer = document.getElementById('showreel-animation');
if (!animationContainer) {
console.error('Animation container not found');
return;
}
if (typeof lottie === 'undefined') {
console.log('Waiting for Lottie library to load...');
setTimeout(initShowreelAnimation, 100);
return;
}
try {
console.log('Loading Lottie animation from Comp 1.json');
const anim = lottie.loadAnimation({
container: animationContainer,
renderer: 'svg',
loop: false,
autoplay: true,
path: 'Comp 1.json'
});
let isReversing = false;
let pauseTimeout = null;
anim.addEventListener('complete', function() {
if (!isReversing) {
// Animation finished playing forward, now play in reverse
isReversing = true;
anim.setDirection(-1);
anim.play();
} else {
// Reverse finished, pause for 10 seconds then play forward again
isReversing = false;
anim.pause();
clearTimeout(pauseTimeout);
pauseTimeout = setTimeout(function() {
anim.setDirection(1);
anim.play();
}, 5000); // 5 seconds pause
}
});
} catch (error) {
console.error('Error loading Lottie animation:', error);
}
}
// Initialize when page loads (skip on showreel page - it has its own init)
window.addEventListener('load', function() {
if (!window.location.pathname.includes('showreel')) {
initShowreelAnimation();
}
});
// Pause showreel video when scrolled past on mobile
function initShowreelPauseOnScroll() {
const showreelContainer = document.querySelector('.showreel-container');
const showreelIframe = showreelContainer?.querySelector('iframe');
if (!showreelContainer || !showreelIframe) return;
// Check if mobile device
const isMobile = window.innerWidth <= 768;
if (!isMobile) return;
// Use Vimeo Player API if available
let player = null;
if (typeof Vimeo !== 'undefined' && Vimeo.Player) {
try {
player = new Vimeo.Player(showreelIframe);
} catch (e) {
console.log('Vimeo Player API not available');
}
}
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (!entry.isIntersecting) {
// Video is out of view, pause it
if (player) {
player.pause().catch(() => {});
} else {
// Fallback: try postMessage
try {
showreelIframe.contentWindow.postMessage('{"method":"pause"}', '*');
} catch (e) {
// If all else fails, don't break the video
}
}
} else {
// Video is in view, resume playback
if (player) {
player.play().catch(() => {});
} else {
// Fallback: try postMessage
try {
showreelIframe.contentWindow.postMessage('{"method":"play"}', '*');
} catch (e) {
// If all else fails, don't break the video
}
}
}
});
}, {
threshold: 0.1 // Trigger when 10% of video is out of view
});
observer.observe(showreelContainer);
}
// Initialize showreel pause on scroll
window.addEventListener('load', function() {
initShowreelPauseOnScroll();
});