| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- (() => {
- const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
- document.getElementById('year').textContent = new Date().getFullYear();
- document.getElementById('ticket-number').textContent = `TKT-${String(Math.floor(1000 + Math.random() * 9000))}`;
- document.getElementById('started-at').value = Math.floor(Date.now() / 1000);
- const reveals = document.querySelectorAll('.reveal');
- if (reducedMotion || !('IntersectionObserver' in window)) {
- reveals.forEach((item) => item.classList.add('visible'));
- } else {
- const observer = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- if (entry.isIntersecting) {
- entry.target.classList.add('visible');
- observer.unobserve(entry.target);
- }
- });
- }, { threshold: 0.12 });
- reveals.forEach((item) => observer.observe(item));
- }
- const form = document.getElementById('contact-form');
- const status = document.getElementById('form-status');
- form.addEventListener('submit', async (event) => {
- event.preventDefault();
- if (!form.reportValidity()) return;
- form.classList.remove('error', 'success');
- form.classList.add('sending');
- status.textContent = 'Routing your ticket…';
- form.querySelector('.button-label').textContent = 'Sending';
- try {
- const response = await fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } });
- const result = await response.json();
- if (!response.ok || !result.ok) throw new Error(result.message || 'Message could not be sent.');
- form.classList.add('success');
- status.textContent = result.message;
- form.reset();
- document.getElementById('started-at').value = Math.floor(Date.now() / 1000);
- form.querySelector('.button-label').textContent = 'Ticket received ✓';
- } catch (error) {
- form.classList.add('error');
- status.textContent = error.message;
- form.querySelector('.button-label').textContent = 'Try again';
- } finally {
- form.classList.remove('sending');
- }
- });
- if (reducedMotion) return;
- const canvas = document.getElementById('particle-field');
- const context = canvas.getContext('2d');
- const pointer = { x: -1000, y: -1000 };
- let particles = [];
- let width = 0;
- let height = 0;
- const resize = () => {
- const ratio = Math.min(devicePixelRatio || 1, 2);
- width = innerWidth;
- height = innerHeight;
- canvas.width = width * ratio;
- canvas.height = height * ratio;
- canvas.style.width = `${width}px`;
- canvas.style.height = `${height}px`;
- context.setTransform(ratio, 0, 0, ratio, 0, 0);
- particles = Array.from({ length: Math.min(55, Math.floor(width / 24)) }, () => ({
- x: Math.random() * width, y: Math.random() * height, vx: (Math.random() - .5) * .18, vy: (Math.random() - .5) * .18, r: Math.random() * 1.4 + .4
- }));
- };
- addEventListener('resize', resize, { passive: true });
- addEventListener('pointermove', (event) => { pointer.x = event.clientX; pointer.y = event.clientY; }, { passive: true });
- addEventListener('pointerleave', () => { pointer.x = pointer.y = -1000; });
- resize();
- const draw = () => {
- context.clearRect(0, 0, width, height);
- particles.forEach((particle, index) => {
- particle.x += particle.vx; particle.y += particle.vy;
- if (particle.x < 0 || particle.x > width) particle.vx *= -1;
- if (particle.y < 0 || particle.y > height) particle.vy *= -1;
- const pdx = pointer.x - particle.x, pdy = pointer.y - particle.y, pointerDistance = Math.hypot(pdx, pdy);
- if (pointerDistance < 130) { particle.x -= pdx * .0008; particle.y -= pdy * .0008; }
- context.beginPath(); context.arc(particle.x, particle.y, particle.r, 0, Math.PI * 2); context.fillStyle = '#6fc9ff55'; context.fill();
- for (let j = index + 1; j < particles.length; j++) {
- const other = particles[j], distance = Math.hypot(particle.x - other.x, particle.y - other.y);
- if (distance < 115) { context.beginPath(); context.moveTo(particle.x, particle.y); context.lineTo(other.x, other.y); context.strokeStyle = `rgba(70,185,255,${(1 - distance / 115) * .09})`; context.stroke(); }
- }
- });
- requestAnimationFrame(draw);
- };
- draw();
- })();
|