Help
RSS
API
Feed
Maltego
Contact
Domain > cameronkfox.com
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2025-04-20
104.21.10.4
(
ClassC
)
2025-10-26
172.67.161.205
(
ClassC
)
Port 443
HTTP/1.1 200 OKDate: Sun, 26 Oct 2025 04:01:27 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: keep-aliveaccept-ranges: bytesaccess-control-allow-headers: Content-Type, Acceptaccess-control-allow-origin: *Speculation-Rules: /cdn-cgi/speculationlast-modified: Thu, 16 Oct 2025 14:29:40 GMTServer: cloudflareNel: {report_to:cf-nel,success_fraction:0.0,max_age:604800}cf-cache-status: DYNAMICReport-To: {group:cf-nel,max_age:604800,endpoints:{url:https://a.nel.cloudflare.com/report/v4?sbdZJ9g%2BNlrIR3JFQgoOpN5f2eWLuN%2Bba5ExuY7fm4QbYV9lX8f4c%2F1hjFUBbbXyqNHdApeRYNmxOUgfkYwrT3U5EtGLaB2sUV6Efc08nmA%3D%3D}}CF-RAY: 99470d71bd6f302c-PDXalt-svc: h3:443; ma86400 !DOCTYPE html>html langen>head> meta charsetUTF-8> meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale5.0, user-scalableyes> title>Cameron Fox | Kelley School of Business Graduate & US Ski Cross Racer/title> meta namedescription contentCameron Fox — Management Information Systems graduate from the Kelley School of Business and competitive ski cross racer. Official website, portfolio, and contact. /> link relcanonical hrefhttps://cameronkfox.com/ /> meta namerobots contentindex,follow,max-snippet:-1,max-image-preview:large,max-video-preview:-1 /> link relpreconnect hrefhttps://fonts.gstatic.com crossorigin /> link reldns-prefetch hrefhttps://fonts.gstatic.com /> meta nameauthor contentCameron Fox /> !-- Open Graph --> meta propertyog:type contentwebsite /> meta propertyog:site_name contentCameron Fox /> meta propertyog:title contentCameron Fox | Kelley School of Business Graduate & US Ski Cross Racer /> meta propertyog:description contentOfficial website of Cameron Fox — MIS (Kelley) and ski cross racer. /> meta propertyog:url contenthttps://cameronkfox.com/ /> meta propertyog:image contenthttps://cameronkfox.com/assets/CameronFox.png /> meta propertyog:image:alt contentHeadshot of Cameron Fox /> !-- Twitter --> meta nametwitter:card contentsummary_large_image /> meta nametwitter:title contentCameron Fox | Kelley School of Business Graduate & US Ski Cross Racer /> meta nametwitter:description contentOfficial website of Cameron Fox — MIS (Kelley) and ski cross racer. /> meta nametwitter:image contenthttps://cameronkfox.com/assets/CameronFox.png /> !-- Identity links (non-visual) --> link relme hrefhttps://www.linkedin.com/in/cameronkfox /> !-- PWA --> link relmanifest href/manifest.json> meta nametheme-color content#ffffff> link hrefhttps://fonts.googleapis.com/css2?familyInter:wght@300;400;500;600;700&familyJetBrains+Mono:wght@300;400;500;600&displayswap relstylesheet> link relstylesheet href/css/header.css> link relstylesheet href/components/SectionNav.css> script src/js/nav-side-preload.js type377e939800cdf2b3e0a8259d-text/javascript>/script> !-- Matomo Placeholder - Scripts moved to body --> !-- Analytics --> script type377e939800cdf2b3e0a8259d-text/javascript> // Initialize visitor data collection let visitorData { sessionId: Date.now().toString(), userAgent: navigator.userAgent, language: navigator.language, platform: navigator.platform, screenResolution: screen.width + x + screen.height, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, referrer: document.referrer }; // Matomo integration window.addEventListener(load, function() { // Set up custom variables after page load setTimeout(function() { if (typeof _paq ! undefined) { _paq.push(setCustomVariable, 1, SessionID, Date.now().toString(), page); _paq.push(setCustomVariable, 2, UserAgent, navigator.userAgent.substring(0, 100), page); _paq.push(setCustomVariable, 3, Language, navigator.language, page); _paq.push(setCustomVariable, 4, Platform, navigator.platform, page); _paq.push(setCustomVariable, 5, ScreenResolution, visitorData.screenResolution, page); _paq.push(setCustomVariable, 7, Timezone, visitorData.timezone, page); if (performance.timing) { const loadTime performance.timing.loadEventEnd - performance.timing.navigationStart; _paq.push(setCustomVariable, 8, PageLoadTime, loadTime.toString(), page); } } }, 2000); // Track user engagement let lastActivity Date.now(); const activityEvents mousedown, mousemove, keypress, scroll, touchstart; activityEvents.forEach(event > { document.addEventListener(event, function() { lastActivity Date.now(); }, { passive: true }); }); // Track time spent on page setInterval(function() { const timeSpent Math.floor((Date.now() - lastActivity) / 1000); if (timeSpent 300 && typeof _paq ! undefined) { _paq.push(trackEvent, Engagement, TimeSpent, timeSpent.toString(), visitorData.sessionId); } }, 30000); // Track page visibility changes document.addEventListener(visibilitychange, function() { if (typeof _paq ! undefined) { const state document.hidden ? Hidden : Visible; _paq.push(trackEvent, PageVisibility, state, window.location.pathname); } }); // Track scroll depth let maxScrollDepth 0; window.addEventListener(scroll, function() { const scrollTop window.pageYOffset || document.documentElement.scrollTop; const windowHeight window.innerHeight; const documentHeight Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight ); const scrollDepth Math.round((scrollTop + windowHeight) / documentHeight * 100); if (scrollDepth > maxScrollDepth && scrollDepth 100) { maxScrollDepth scrollDepth; if (typeof _paq ! undefined && scrollDepth % 25 0) { _paq.push(trackEvent, ScrollDepth, scrollDepth + %, window.location.pathname); } } }, { passive: true }); // Track button clicks document.addEventListener(click, function(e) { const button e.target.closest(button, a, .btn); if (button && typeof _paq ! undefined) { let buttonText button.textContent?.trim().substring(0, 50) || ; if (button.tagName A) { buttonText button.href || buttonText; } _paq.push(trackEvent, Button, Click, buttonText); } }); // Track form interactions document.addEventListener(focusin, function(e) { if ((e.target.tagName INPUT || e.target.tagName TEXTAREA) && typeof _paq ! undefined) { _paq.push(trackEvent, Form, Focus, e.target.name || e.target.id || unnamed); } }); // Track section changes let currentSection ; const sectionObserver new IntersectionObserver((entries) > { entries.forEach(entry > { if (entry.isIntersecting && entry.intersectionRatio > 0.5) { const sectionId entry.target.id; if (sectionId && sectionId ! currentSection && typeof _paq ! undefined) { currentSection sectionId; _paq.push(trackEvent, Navigation, ViewSection, sectionId); } } }); }, { threshold: 0.5 }); document.querySelectorAll(sectionid).forEach(section > { sectionObserver.observe(section); }); }); // Enhanced tracking functions with AI integration window.trackUserInteraction function(action, category UserInteraction, label , value null) { const eventData { event_type: interaction, action: action, category: category, label: label, value: value, url: window.location.href, timestamp: Date.now(), sessionId: visitorData.sessionId, userAgent: visitorData.userAgent }; // Send to Matomo if (typeof _paq ! undefined) { _paq.push(trackEvent, category, action, label, value); } // Send to our analytics API for AI processing sendToAnalyticsAPI(eventData); }; window.trackChatInteraction function(role, message, conversationId null) { const eventData { event_type: chat, role: role, message: message, conversationId: conversationId, url: window.location.href, timestamp: Date.now(), sessionId: visitorData.sessionId, userAgent: visitorData.userAgent }; // Send to Matomo if (typeof _paq ! undefined) { _paq.push(trackEvent, Chat, role, message.substring(0, 100), conversationId); _paq.push(setCustomVariable, 9, LastChatMessage, message.substring(0, 50), page); } // Send to our analytics API for AI processing sendToAnalyticsAPI(eventData); }; window.trackURLCreation function(originalUrl, shortUrl, customSlug ) { const eventData { event_type: url_creation, originalUrl: originalUrl, shortUrl: shortUrl, customSlug: customSlug, url: window.location.href, timestamp: Date.now(), sessionId: visitorData.sessionId, userAgent: visitorData.userAgent }; // Send to Matomo if (typeof _paq ! undefined) { _paq.push(trackEvent, URLCreation, Created, shortUrl); _paq.push(setCustomVariable, 10, LastShortURL, shortUrl, page); if (customSlug) { _paq.push(setCustomVariable, 11, CustomSlug, customSlug, page); } } // Send to our analytics API for AI processing sendToAnalyticsAPI(eventData); }; // Function to send analytics data to our API for AI processing function sendToAnalyticsAPI(eventData) { fetch(/api/analytics.php, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(eventData) }).catch(error > { // Silently handle errors to avoid breaking user experience console.warn(Analytics API error:, error); }); } // Send pageview data on load window.addEventListener(load, function() { const pageviewData { event_type: pageview, url: window.location.href, referrer: document.referrer, timestamp: Date.now(), sessionId: visitorData.sessionId, userAgent: visitorData.userAgent, screenResolution: visitorData.screenResolution, language: visitorData.language }; sendToAnalyticsAPI(pageviewData); }); /script> !-- End Analytics --> !-- JSON-LD Structured Data --> script typeapplication/ld+json> { @context: https://schema.org, @graph: { @type: Person, @id: https://cameronkfox.com/#cameron-fox, name: Cameron Fox, givenName: Cameron, familyName: Fox, url: https://cameronkfox.com/, image: https://cameronkfox.com/assets/CameronFox.png, jobTitle: Management Information Systems Graduate; Ski Cross Athlete, alumniOf: { @type: CollegeOrUniversity, name: Kelley School of Business, Indiana University, sameAs: https://kelley.iu.edu/ }, sport: Ski Cross, description: Cameron Fox is a Kelley School of Business MIS graduate and competitive ski cross racer., sameAs: https://www.linkedin.com/in/cameronkfox, https://www.fis-ski.com/DB/general/athlete-biography.html?competitorid292394§orcodeFS, https://www.fis-ski.com/DB/general/athlete-biography.html?competitorid283049§orcodeAL, https://my.usskiandsnowboard.org/ussa-tools/history/7309233 }, { @type: WebSite, @id: https://cameronkfox.com/#website, url: https://cameronkfox.com/, name: Cameron Fox, publisher: { @id: https://cameronkfox.com/#cameron-fox }, inLanguage: en } } /script> style> :root { --primary-blue: #007AFF; --primary-blue-hover: #0056CC; --background: #000000; --card-bg: rgba(28, 28, 30, 0.85); --border-color: rgba(84, 84, 88, 0.45); --text-primary: #FFFFFF; --text-secondary: rgba(235, 235, 245, 0.8); --text-tertiary: rgba(235, 235, 245, 0.65); } /* Prevent content overflow and ensure proper spacing */ * { margin: 0; padding: 0; box-sizing: border-box; } /* Ensure no element exceeds viewport width */ img, video, iframe, embed, object { max-width: 100%; height: auto; } /* Word splitting prevented for better text wrapping */ /* Prevent horizontal scrolling */ body { overflow-x: hidden; width: 100%; } /* Ensure sections adapt to content */ .section { overflow: visible; width: 100%; position: relative; } /* Flexible containers */ .container, .container-wide { overflow: visible; } html { scroll-behavior: smooth; overflow-y: scroll; height: 100%; /* Enhanced scroll snap as fallback */ scroll-snap-type: y proximity; } body { font-family: Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif; background: var(--background); color: var(--text-primary); line-height: 1.5; -webkit-font-smoothing: antialiased; overflow-x: hidden; } /* Fixed parallax background */ .background { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle at 20% 30%, rgba(0, 122, 255, 0.08) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(175, 82, 222, 0.06) 0%, transparent 50%), radial-gradient(circle at 40% 70%, rgba(52, 199, 89, 0.04) 0%, transparent 50%), radial-gradient(circle at 90% 80%, rgba(255, 149, 0, 0.05) 0%, transparent 50%); z-index: -2; } .floating-elements { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; pointer-events: none; } .floating-element { position: absolute; opacity: 0.04; font-family: JetBrains Mono, monospace; font-size: clamp(18px, 2vw, 24px); animation: float 12s cubic-bezier(0.25, 0.46, 0.45, 0.94) infinite; color: var(--text-primary); transition: all 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); will-change: transform, opacity; } @keyframes float { 0%, 100% { transform: translateY(0px) rotate(0deg) scale(1); opacity: 0.04; } 25% { transform: translateY(-15px) rotate(90deg) scale(1.05); opacity: 0.06; } 50% { transform: translateY(-8px) rotate(180deg) scale(1.1); opacity: 0.08; } 75% { transform: translateY(-22px) rotate(270deg) scale(0.95); opacity: 0.05; } } .floating-1 { top: 15%; left: 10%; animation-delay: 0s; } .floating-2 { top: 40%; right: 15%; animation-delay: 2.5s; } .floating-3 { bottom: 25%; left: 20%; animation-delay: 5s; } .floating-4 { top: 70%; left: 70%; animation-delay: 1.5s; } .floating-5 { top: 25%; left: 60%; animation-delay: 4s; } /* Section styling */ .section { min-height: 100vh; height: auto; /* Allow content to expand naturally */ display: flex; flex-direction: column; justify-content: center; align-items: center; padding: clamp(1rem, 4vw, 4rem) clamp(1rem, 3vw, 3rem); position: relative; z-index: 1; /* CSS scroll snap fallback */ scroll-snap-align: start; scroll-snap-stop: always; } /* Add extra top padding to non-hero sections to prevent header overlap */ .section:not(#hero) { padding-top: clamp(8rem, 12vw, 10rem); /* Extra space for sticky header */ } /* Prevent content overlap when sticky header is visible */ .section:not(#hero) { scroll-margin-top: clamp(100px, 15vw, 140px); /* Increased for better clearance */ } /* Ensure smooth scrolling accounts for sticky header */ .section:target:not(#hero) { padding-top: calc(clamp(1rem, 4vw, 4rem) + clamp(100px, 15vw, 140px)); } /* Mobile adjustments for header spacing */ @media (max-width: 768px) { .section:not(#hero) { scroll-margin-top: 120px; /* Increased mobile spacing */ padding-top: 7rem; /* Fixed mobile padding */ } .section:target:not(#hero) { padding-top: calc(clamp(1rem, 4vw, 4rem) + 120px); } } /* Extra small screens need even more spacing */ @media (max-width: 480px) { .section:not(#hero) { scroll-margin-top: 100px; padding-top: 6rem; /* Fixed small screen padding */ } .section:target:not(#hero) { padding-top: calc(clamp(1rem, 4vw, 4rem) + 100px); } } .container { max-width: min(95vw, 700px); width: 100%; text-align: center; padding: 0 clamp(0.5rem, 2vw, 1.5rem); margin: 0 auto; } .container-wide { max-width: min(98vw, 1400px); width: 100%; text-align: center; padding: 0 clamp(0.5rem, 2vw, 2rem); margin: 0 auto; } /* Typography */ h1 { font-size: clamp(2rem, 6vw, 4.5rem); font-weight: 700; margin-bottom: clamp(0.75rem, 2vw, 1.5rem); letter-spacing: clamp(-0.02em, -0.03vw, -0.025em); line-height: 1.1; } h2 { font-size: clamp(1.5rem, 5vw, 3.5rem); font-weight: 700; margin-bottom: clamp(0.5rem, 1.5vw, 1rem); letter-spacing: clamp(-0.015em, -0.025vw, -0.02em); line-height: 1.1; } h3 { font-size: clamp(1.1rem, 3.5vw, 2.2rem); font-weight: 600; margin-bottom: clamp(0.4rem, 1vw, 0.75rem); letter-spacing: clamp(-0.008em, -0.015vw, -0.01em); line-height: 1.2; } .subtitle { font-size: clamp(0.9rem, 3vw, 1.6rem); color: var(--text-secondary); margin-bottom: clamp(1.5rem, 3vw, 2.5rem); font-weight: 400; letter-spacing: clamp(-0.008em, -0.015vw, -0.01em); line-height: 1.4; } .description { font-size: clamp(0.85rem, 2.5vw, 1.2rem); color: var(--text-tertiary); line-height: 1.65; margin-bottom: clamp(2rem, 4vw, 3rem); font-weight: 400; max-width: min(75ch, 95vw); margin-left: auto; margin-right: auto; } .mono { font-family: JetBrains Mono, monospace; font-size: 0.9rem; letter-spacing: 0.01em; } /* Button containers for better responsive layout */ .button-container { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: 0.75rem; margin-top: 1.5rem; width: 100%; } .button-container .btn { flex: 0 1 auto; min-width: 140px; margin: 0; } /* Adjust button layout on very small screens */ @media (max-width: 480px) { .button-container { flex-direction: column; gap: 0.5rem; } .button-container .btn { width: 100%; max-width: 280px; flex: none; } } /* Profile */ .profile-img { width: 120px; height: 120px; border-radius: 50%; margin-bottom: 1rem; border: 3px solid var(--border-color); object-fit: cover; background: rgba(255, 255, 255, 0.1); } .profile-subtitle { font-family: JetBrains Mono, monospace; font-size: 0.8rem; color: var(--text-tertiary); margin-bottom: 1.5rem; letter-spacing: 0.02em; text-transform: uppercase; } /* Buttons - Apple-style */ .btn { display: inline-flex; align-items: center; justify-content: center; padding: 14px 28px; border: 1px solid var(--border-color); color: var(--text-primary); text-decoration: none; border-radius: 24px; font-size: 0.95rem; font-weight: 500; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); margin: 0.5rem; background: rgba(28, 28, 30, 0.3); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); letter-spacing: -0.01em; position: relative; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); } .btn::before { content: ; position: absolute; top: 50%; left: 50%; width: 0; height: 0; background: rgba(255, 255, 255, 0.1); border-radius: 50%; transform: translate(-50%, -50%); transition: all 0.6s ease; } .btn:hover { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.35); transform: translateY(-3px); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.12), 0 4px 10px rgba(0, 0, 0, 0.08); } .btn:hover::before { width: 300px; height: 300px; } .btn:active { transform: translateY(-1px); transition: all 0.1s ease; } .btn-primary { background: var(--primary-blue); border-color: var(--primary-blue); color: white; box-shadow: 0 4px 15px rgba(0, 122, 255, 0.3), 0 1px 3px rgba(0, 0, 0, 0.1); } .btn-primary::before { background: rgba(255, 255, 255, 0.2); } .btn-primary:hover { background: var(--primary-blue-hover); border-color: var(--primary-blue-hover); transform: translateY(-3px); box-shadow: 0 12px 35px rgba(0, 122, 255, 0.4), 0 6px 15px rgba(0, 0, 0, 0.1); } .btn-primary:active { transform: translateY(-1px); transition: all 0.1s ease; } .btn-arrow::after { content: →; margin-left: 8px; transition: transform 0.3s ease; } .btn:hover .btn-arrow::after { transform: translateX(4px); } /* Cards and Case Studies - Apple-style */ .case-studies-grid { display: grid; grid-template-columns: 1fr; gap: clamp(1rem, 3vw, 2rem); width: 100%; margin-top: clamp(0.75rem, 2vw, 1.5rem); } .case-study { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: clamp(16px, 2.5vw, 22px); padding: clamp(1.5rem, 4vw, 2.5rem); text-align: left; backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); /* Apple-style easing */ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); position: relative; overflow: hidden; display: flex; flex-direction: column; min-height: 280px; /* Ensure consistent card height */ } .case-study::before { content: ; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 50%, rgba(255, 255, 255, 0.01) 100%); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; } .case-study:hover { transform: translateY(-6px) scale(1.02); border-color: rgba(255, 255, 255, 0.25); background: rgba(28, 28, 30, 0.95); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); } .case-study:hover::before { opacity: 1; } .case-study h3 { margin-bottom: clamp(0.5rem, 1.5vw, 0.75rem); font-size: clamp(1.1rem, 2.5vw, 1.4rem); } .case-study p { font-size: clamp(0.9rem, 2vw, 1rem); color: var(--text-tertiary); margin-bottom: clamp(1rem, 2vw, 1.5rem); line-height: 1.5; flex: 1; /* Make description take up available space */ } .case-study .btn { margin-top: auto; /* Push button to bottom */ align-self: flex-start; /* Align button to start */ } /* Chat Interface */ .chat-interface { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: 16px; padding: 1.5rem; margin: 1.5rem 0; backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); text-align: left; } /* Old chat message styles removed - using new styles below */ .chat-input-container { margin-top: 1rem; position: relative; clear: both; } .chat-input { width: 100%; background: rgba(118, 118, 128, 0.12); border: 1px solid var(--border-color); border-radius: 20px; padding: 12px 45px 12px 16px; color: var(--text-primary); font-size: 0.95rem; outline: none; } .chat-send { position: absolute; right: 8px; top: 50%; transform: translateY(-50%); background: var(--primary-blue); border: none; border-radius: 50%; width: 28px; height: 28px; color: white; cursor: pointer; font-size: 0.8rem; } /* Cards Grid - Apple-style */ .cards-grid { display: grid; grid-template-columns: 1fr; gap: clamp(1.25rem, 3vw, 2rem); width: 100%; margin: clamp(1.5rem, 3vw, 2.5rem) 0; } .skill-card { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: clamp(16px, 2.5vw, 20px); padding: clamp(1.5rem, 4vw, 2.5rem); text-align: left; backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); position: relative; overflow: hidden; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); } .skill-card::before { content: ; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(135deg, rgba(0, 122, 255, 0.03) 0%, rgba(255, 255, 255, 0.02) 50%, rgba(52, 199, 89, 0.02) 100%); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; } .skill-card:hover { transform: translateY(-6px) scale(1.01); border-color: rgba(255, 255, 255, 0.25); background: rgba(28, 28, 30, 0.95); box-shadow: 0 12px 30px rgba(0, 0, 0, 0.15), 0 6px 15px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); } .skill-card:hover::before { opacity: 1; } .skill-card h3 { margin-bottom: clamp(0.75rem, 2vw, 1.25rem); font-size: clamp(1rem, 2.5vw, 1.4rem); color: var(--text-primary); letter-spacing: clamp(-0.008em, -0.015vw, -0.01em); } .card-description { font-size: clamp(0.8rem, 2vw, 1.05rem); color: var(--text-tertiary); line-height: 1.6; margin: 0; } /* Demo Section */ .demo-note { font-style: italic; font-size: 0.85rem; color: var(--text-tertiary); margin-bottom: 2rem; } /* Video Production Section */ .video-production-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: clamp(1.25rem, 3vw, 2rem); width: 100%; margin: clamp(1.5rem, 3vw, 2.5rem) 0; } .video-card { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: clamp(16px, 2.5vw, 20px); padding: 0; overflow: hidden; backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06); position: relative; } .video-card::before { content: ; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 50%, rgba(255, 255, 255, 0.01) 100%); opacity: 0; transition: opacity 0.4s ease; pointer-events: none; z-index: 1; } .video-card:hover { transform: translateY(-6px) scale(1.02); border-color: rgba(255, 255, 255, 0.25); background: rgba(28, 28, 30, 0.95); box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); } .video-card:hover::before { opacity: 1; } .video-card video, .video-card iframe { width: 100%; height: auto; aspect-ratio: 16 / 9; display: block; border-radius: clamp(16px, 2.5vw, 20px) clamp(16px, 2.5vw, 20px) 0 0; background: #000; } .video-card-content { padding: clamp(1rem, 3vw, 1.5rem); position: relative; z-index: 2; } .video-card h3 { margin-bottom: 0.5rem; font-size: clamp(0.95rem, 2vw, 1.1rem); color: var(--text-primary); } .video-card p { font-size: clamp(0.8rem, 1.8vw, 0.9rem); color: var(--text-tertiary); line-height: 1.5; margin: 0; } .video-card .video-client { font-size: clamp(0.75rem, 1.6vw, 0.85rem); color: var(--text-secondary); margin-top: 0.5rem; font-style: italic; } .youtube-links { display: flex; flex-wrap: wrap; gap: 0.5rem; justify-content: center; margin-top: 2rem; } .youtube-link { display: inline-flex; align-items: center; padding: 8px 16px; background: rgba(255, 0, 0, 0.1); border: 1px solid rgba(255, 0, 0, 0.3); border-radius: 20px; color: var(--text-primary); text-decoration: none; font-size: 0.85rem; transition: all 0.3s ease; } .youtube-link:hover { background: rgba(255, 0, 0, 0.2); border-color: rgba(255, 0, 0, 0.5); transform: translateY(-2px); } .youtube-link::before { content: ▶; margin-right: 6px; color: #ff0000; } @media (max-width: 768px) { .video-production-grid { grid-template-columns: 1fr; } } /* Tron Grid Background for AI Section */ .tron-grid-container { position: absolute; top: 0; left: 0; right: 0; bottom: 0; overflow: hidden; z-index: 0; pointer-events: none; background: radial-gradient(ellipse at center bottom, rgba(0, 122, 255, 0.08) 0%, transparent 70%), #000; } .tron-grid { position: absolute; width: 200%; height: 200%; left: -50%; top: -50%; background-image: linear-gradient(0deg, transparent 24%, rgba(0, 255, 255, 0.12) 25%, rgba(0, 255, 255, 0.12) 26%, transparent 27%, transparent 74%, rgba(0, 255, 255, 0.12) 75%, rgba(0, 255, 255, 0.12) 76%, transparent 77%, transparent), linear-gradient(90deg, transparent 24%, rgba(0, 255, 255, 0.12) 25%, rgba(0, 255, 255, 0.12) 26%, transparent 27%, transparent 74%, rgba(0, 255, 255, 0.12) 75%, rgba(0, 255, 255, 0.12) 76%, transparent 77%, transparent); background-size: 80px 80px; transform: perspective(500px) rotateX(60deg) translateZ(-100px); transform-origin: center center; animation: tronGridMove 30s linear infinite; opacity: 0.35; filter: drop-shadow(0 0 1px rgba(0, 255, 255, 0.2)); will-change: transform; backface-visibility: hidden; -webkit-backface-visibility: hidden; } @keyframes tronGridMove { 0% { transform: perspective(500px) rotateX(60deg) translateZ(-100px) translateY(0); } 100% { transform: perspective(500px) rotateX(60deg) translateZ(-100px) translateY(80px); } } /* Lightcycle Vehicle */ .lightcycle { position: absolute; width: 40px; height: 20px; z-index: 3; animation: lightcycleRide 20s linear infinite; } /* Lightcycle body */ .lightcycle-body { position: absolute; width: 30px; height: 8px; background: rgba(0, 255, 255, 0.9); box-shadow: 0 0 10px rgba(0, 255, 255, 0.8), 0 0 20px rgba(0, 255, 255, 0.5); border-radius: 2px; top: 6px; left: 5px; } /* Lightcycle front glow */ .lightcycle-front { position: absolute; width: 15px; height: 12px; background: linear-gradient(90deg, rgba(0, 255, 255, 0) 0%, rgba(0, 255, 255, 0.8) 100%); box-shadow: 0 0 15px rgba(0, 255, 255, 0.8); border-radius: 2px; top: 4px; left: 20px; } /* Lightcycle wheels */ .lightcycle-wheel { position: absolute; width: 10px; height: 10px; border: 2px solid rgba(0, 255, 255, 0.9); border-radius: 50%; background: rgba(0, 255, 255, 0.3); box-shadow: 0 0 8px rgba(0, 255, 255, 0.8), inset 0 0 5px rgba(0, 255, 255, 0.6); top: 8px; } .lightcycle-wheel:first-of-type { left: 5px; } .lightcycle-wheel:last-of-type { left: 25px; } @keyframes lightcycleRide { 0% { left: -10%; top: 60%; opacity: 0; } 5% { opacity: 0.9; } 45% { left: 110%; top: 60%; opacity: 0.9; } 50% { opacity: 0; } 100% { left: 110%; top: 60%; opacity: 0; } } /* END OF LINE - Always visible Tron reference */ .end-of-line { position: absolute; bottom: 5%; right: 5%; font-family: JetBrains Mono, monospace; font-weight: 700; font-size: clamp(1.5rem, 3vw, 2.5rem); text-transform: uppercase; letter-spacing: 0.2em; color: rgba(0, 255, 255, 1); text-shadow: 0 0 20px rgba(0, 255, 255, 1), 0 0 40px rgba(0, 255, 255, 0.8), 0 0 60px rgba(0, 255, 255, 0.6); z-index: 4; animation: endOfLinePulse 3s ease-in-out infinite; } @keyframes endOfLinePulse { 0%, 100% { opacity: 0.8; text-shadow: 0 0 20px rgba(0, 255, 255, 1), 0 0 40px rgba(0, 255, 255, 0.8), 0 0 60px rgba(0, 255, 255, 0.6); } 50% { opacity: 1; text-shadow: 0 0 30px rgba(0, 255, 255, 1), 0 0 60px rgba(0, 255, 255, 0.9), 0 0 90px rgba(0, 255, 255, 0.7); } } /* Reduce intensity on mobile */ @media (max-width: 768px) { .tron-grid { opacity: 0.25; } .lightcycle { width: 30px; height: 15px; } .end-of-line { font-size: 1rem; bottom: 3%; right: 3%; } /* Mobile responsive for chat interface */ .chat-interface { padding: 1rem; border-radius: 12px; margin: 1rem 0; } .chat-header { padding: 1rem; } .chat-header h3 { font-size: 1.1rem; } .chat-header p { font-size: 0.8rem !important; } .chat-messages { height: 350px; padding: 0.75rem; } .chat-message { padding: 0.75rem; font-size: 0.9rem; max-width: 85%; } .chat-message.user, .chat-message.assistant { max-width: 85%; } .chat-form { padding: 0.75rem; gap: 0.5rem; } .chat-input { padding: 0.65rem 0.85rem; font-size: 0.9rem; } .chat-form .btn { padding: 0.65rem 1rem; font-size: 0.85rem; } } /* Extra small screens (phones) */ @media (max-width: 480px) { .chat-interface { padding: 0.75rem; border-radius: 10px; } .chat-header { padding: 0.75rem; } .chat-header h3 { font-size: 1rem; } .chat-header p { font-size: 0.75rem !important; } .chat-messages { height: 300px; padding: 0.5rem; } .chat-message { padding: 0.65rem; font-size: 0.85rem; max-width: 90%; border-radius: 10px; } .chat-message.user, .chat-message.assistant { max-width: 90%; } .chat-form { padding: 0.5rem; gap: 0.5rem; flex-wrap: nowrap; } .chat-input { padding: 0.6rem 0.75rem; font-size: 0.85rem; border-radius: 16px; } .chat-form .btn { padding: 0.6rem 0.85rem; font-size: 0.8rem; white-space: nowrap; } .end-of-line { font-size: 0.8rem; bottom: 2%; right: 2%; } /* AI Model section specific mobile spacing */ #ai-model h2 { font-size: clamp(1.75rem, 8vw, 2.5rem); } #ai-model .subtitle { font-size: clamp(0.95rem, 4vw, 1.15rem); } #ai-model .description { font-size: clamp(0.85rem, 3.5vw, 1rem); } #ai-model .description.mono { font-size: 0.7rem !important; } } /* Very small screens */ @media (max-width: 375px) { .chat-interface { padding: 0.6rem; } .chat-header { padding: 0.6rem; } .chat-header h3 { font-size: 0.95rem; } .chat-header p { font-size: 0.7rem !important; } .chat-messages { height: 280px; padding: 0.5rem; } .chat-message { padding: 0.6rem; font-size: 0.8rem; max-width: 92%; } .chat-form { padding: 0.5rem; flex-direction: column; gap: 0.5rem; } .chat-input { width: 100%; padding: 0.6rem; font-size: 0.8rem; } .chat-form .btn { width: 100%; padding: 0.6rem; font-size: 0.8rem; } .end-of-line { font-size: 0.7rem; } #ai-model h2 { font-size: 1.6rem; } #ai-model .subtitle { font-size: 0.9rem; } #ai-model .description { font-size: 0.8rem; } #ai-model .description.mono { font-size: 0.65rem !important; } } /* Ultra small screens */ @media (max-width: 320px) { .chat-interface { padding: 0.5rem; } .chat-header { padding: 0.5rem; } .chat-header h3 { font-size: 0.9rem; } .chat-header p { font-size: 0.65rem !important; line-height: 1.2; } .chat-messages { height: 250px; padding: 0.4rem; } .chat-message { padding: 0.5rem; font-size: 0.75rem; max-width: 95%; } .chat-form { padding: 0.4rem; } .chat-input { padding: 0.5rem; font-size: 0.75rem; } .chat-form .btn { padding: 0.5rem; font-size: 0.75rem; } #ai-model h2 { font-size: 1.4rem; } #ai-model .subtitle { font-size: 0.8rem; } #ai-model .description { font-size: 0.7rem; } #ai-model .description.mono { font-size: 0.6rem !important; } } /* Ensure section content is above the grid */ #ai-model .container { position: relative; z-index: 1; } /* Comprehensive Responsive Design */ /* Extra small screens (up to 375px) */ @media (max-width: 375px) { .section { min-height: auto; height: auto; padding: 2rem 1rem; justify-content: flex-start; } .container, .container-wide { padding: 0 0.5rem; max-width: 100%; } h1 { font-size: clamp(1.4rem, 8vw, 2rem); } h2 { font-size: clamp(1.2rem, 7vw, 1.75rem); } h3 { font-size: clamp(1rem, 6vw, 1.3rem); } .cards-grid, .case-studies-grid { grid-template-columns: 1fr; gap: 1rem; } .case-study, .skill-card { padding: 1.25rem; margin-bottom: 1rem; } .case-study { min-height: 240px; /* Adjusted for smaller screens */ } .btn { width: 100%; max-width: 280px; padding: 12px 20px; font-size: 0.9rem; } } /* Small screens (376px - 480px) */ @media (min-width: 376px) and (max-width: 480px) { .section { min-height: auto; height: auto; padding: 2.5rem 1.25rem; } h1 { font-size: clamp(1.6rem, 7vw, 2.25rem); } h2 { font-size: clamp(1.4rem, 6vw, 2rem); } h3 { font-size: clamp(1.1rem, 5vw, 1.5rem); } .cards-grid, .case-studies-grid { grid-template-columns: 1fr; gap: 1.25rem; } } /* Medium-small screens (481px - 640px) */ @media (min-width: 481px) and (max-width: 640px) { .section { padding: 3rem 1.5rem; } .cards-grid { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); } .case-studies-grid { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); } } /* Medium screens (641px - 768px) */ @media (min-width: 641px) and (max-width: 768px) { .section { padding: 3.5rem 2rem; } .cards-grid { grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); } .case-studies-grid { grid-template-columns: repeat(auto-fit, minmax(min(350px, 100%), 1fr)); } } /* Large screens (769px - 1024px) */ @media (min-width: 769px) and (max-width: 1024px) { .section { padding: 4rem 2.5rem; } .cards-grid { grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); } .case-studies-grid { grid-template-columns: repeat(auto-fit, minmax(min(380px, 100%), 1fr)); } } /* Extra large screens (1025px+) */ @media (min-width: 1025px) { .cards-grid { grid-template-columns: repeat(auto-fit, minmax(min(350px, 100%), 1fr)); max-width: 1200px; margin: 0 auto; } .case-studies-grid { grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr)); max-width: 1400px; margin: 0 auto; } } /* Ultra-small screens (320px and below) */ @media (max-width: 320px) { .section { min-height: auto; height: auto; padding: 2rem 0.75rem; display: flex; flex-direction: column; justify-content: flex-start; align-items: center; text-align: center; } .container, .container-wide { max-width: 100%; width: 100%; padding: 0 0.5rem; margin: 0; box-sizing: border-box; } h1 { font-size: clamp(1.5rem, 8vw, 2.5rem); line-height: 1.1; margin-bottom: 1rem; letter-spacing: -0.01em; } h2 { font-size: clamp(1.25rem, 7vw, 2rem); line-height: 1.2; margin-bottom: 0.75rem; letter-spacing: -0.008em; } h3 { font-size: clamp(1rem, 6vw, 1.4rem); line-height: 1.3; margin-bottom: 0.5rem; letter-spacing: -0.005em; } .subtitle { font-size: clamp(0.9rem, 5vw, 1.2rem); line-height: 1.4; margin-bottom: 1.25rem; letter-spacing: -0.005em; } .description { font-size: clamp(0.85rem, 4vw, 1rem); line-height: 1.5; margin-bottom: 1.5rem; max-width: 100%; } .profile-img { width: 60px; height: 60px; margin-bottom: 0.75rem; } .profile-subtitle { font-size: 0.7rem; margin-bottom: 1rem; } .btn { padding: 12px 20px; font-size: 0.85rem; margin: 0.25rem; display: inline-block; width: auto; min-width: 120px; text-align: center; box-sizing: border-box; } /* Stack buttons vertically on very small screens */ .section > .container > div { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; } .section > .container > div .btn { width: 100%; max-width: 280px; margin: 0; } .case-study, .skill-card { padding: 1rem; margin-bottom: 1rem; border-radius: 12px; } .case-study { min-height: 200px; /* Adjusted for very small screens */ } .case-study h3, .skill-card h3 { font-size: 0.9rem; margin-bottom: 0.5rem; } .case-study p, .card-description { font-size: 0.75rem; line-height: 1.4; } .cards-grid, .case-studies-grid { grid-template-columns: 1fr; gap: 0.75rem; margin: 1rem 0; } .floating-elements { display: none; /* Hide floating elements on very small screens */ } .chat-interface { padding: 1rem; border-radius: 12px; } .chat-input { font-size: 0.8rem; padding: 10px 40px 10px 12px; } .chat-message { font-size: 0.8rem; padding: 8px 12px; margin: 6px 0; width: fit-content; max-width: 75%; } .chat-message.user { margin-left: auto; text-align: right; margin-right: 0; } .chat-message.assistant { margin-right: auto; } /* Ensure no horizontal overflow */ body { overflow-x: hidden; } .section { box-sizing: border-box; word-wrap: break-word; overflow-wrap: break-word; } /* Prevent text from being too small to read */ .container, .container-wide { font-size: max(14px, 0.9rem); } } /* Very narrow screens (200px - 250px width) */ @media (max-width: 250px) { .section { padding: 0.5rem 0.5rem; /* Ensure content doesnt overflow horizontally */ overflow-x: hidden; } /* Word splitting prevented for better readability */ h1 { font-size: 1.25rem; margin-bottom: 0.5rem; line-height: 1.1; } h2 { font-size: 1.1rem; margin-bottom: 0.4rem; line-height: 1.2; } h3 { font-size: 0.9rem; margin-bottom: 0.3rem; line-height: 1.3; } .subtitle { font-size: 0.75rem; margin-bottom: 0.75rem; line-height: 1.4; } .description { font-size: 0.7rem; line-height: 1.4; margin-bottom: 1rem; } .btn { padding: 8px 12px; font-size: 0.8rem; margin: 0.2rem; width: 100%; min-width: unset; /* Ensure buttons are properly sized */ box-sizing: border-box; } .case-study, .skill-card { padding: 0.75rem; margin-bottom: 0.75rem; border-radius: 10px; } .case-study { min-height: 180px; /* Adjusted for extremely small screens */ } .case-study h3, .skill-card h3 { font-size: 0.8rem; margin-bottom: 0.4rem; line-height: 1.2; } .case-study p, .card-description { font-size: 0.7rem; line-height: 1.3; } .chat-interface { padding: 0.75rem; border-radius: 10px; } .chat-input { font-size: 0.75rem; padding: 8px 35px 8px 10px; /* Ensure input fits properly */ box-sizing: border-box; } .chat-message { font-size: 0.75rem; padding: 6px 10px; margin: 4px 0; width: fit-content; max-width: 75%; } .chat-message.user { margin-left: auto; text-align: right; margin-right: 0; } .chat-message.assistant { margin-right: auto; } /* Ensure images and other media scale properly */ img, svg { max-width: 100%; height: auto; } /* Improve touch targets */ .btn, .case-study, .skill-card { min-height: 44px; /* iOS recommended touch target size */ } } /* Extremely small screens (under 200px width) */ @media (max-width: 200px) { .section { padding: 0.25rem 0.25rem; } h1 { font-size: 1rem; margin-bottom: 0.25rem; } h2 { font-size: 0.9rem; margin-bottom: 0.25rem; } h3 { font-size: 0.8rem; margin-bottom: 0.2rem; } .subtitle { font-size: 0.65rem; margin-bottom: 0.5rem; } .description { font-size: 0.6rem; line-height: 1.3; margin-bottom: 0.75rem; } .btn { padding: 6px 10px; font-size: 0.7rem; margin: 0.1rem; } .case-study, .skill-card { padding: 0.5rem; margin-bottom: 0.5rem; border-radius: 8px; } .case-study { min-height: 160px; /* Adjusted for extremely small screens */ } .case-study h3, .skill-card h3 { font-size: 0.7rem; margin-bottom: 0.3rem; } .case-study p, .card-description { font-size: 0.6rem; line-height: 1.2; } .chat-interface { padding: 0.5rem; border-radius: 8px; } .chat-input { font-size: 0.65rem; padding: 6px 30px 6px 8px; } .chat-message { font-size: 0.65rem; padding: 4px 8px; margin: 3px 0; width: fit-content; max-width: 80%; } .chat-message.user { margin-left: auto; text-align: right; margin-right: 0; } .chat-message.assistant { margin-right: auto; } .profile-img { width: 40px; height: 40px; margin-bottom: 0.5rem; } /* Minimum touch targets */ .btn, .case-study, .skill-card { min-height: 36px; /* Minimum viable touch target */ } } /* Landscape orientation handling */ @media (max-height: 500px) and (orientation: landscape) { .section { min-height: auto; height: auto; padding: 1.5rem 1rem; justify-content: flex-start; } .container, .container-wide { max-width: none; padding: 0 1rem; } /* Reduce vertical spacing in landscape */ h1, h2, h3 { margin-bottom: 0.5rem; } .subtitle { margin-bottom: 1rem; } .description { margin-bottom: 1.5rem; line-height: 1.4; } .cards-grid, .case-studies-grid { grid-template-columns: repeat(auto-fit, minmax(min(250px, 100%), 1fr)); gap: 1rem; } .case-study, .skill-card { padding: 1rem; } .case-study { min-height: 200px; /* Adjusted for landscape orientation */ } } /* Ensure proper scroll snap on mobile */ @supports (-webkit-touch-callout: none) { html { scroll-snap-type: y mandatory; } .section { scroll-snap-align: start; scroll-snap-stop: always; } } /* Smooth programmatic scrolling compatibility */ html { scroll-behavior: smooth; } /* Ensure scroll-snap works with programmatic navigation */ .section { scroll-margin-top: 80px; /* Account for fixed header */ } /* Custom scrollbar - Apple-style */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.15); border-radius: 3px; transition: all 0.3s ease; } ::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); transform: scaleY(1.2); } ::-webkit-scrollbar-thumb:active { background: rgba(255, 255, 255, 0.35); } /* Smooth scrolling for all elements */ * { scroll-behavior: smooth; } /* Apple-style selection */ ::selection { background: rgba(0, 122, 255, 0.2); color: var(--text-primary); } ::-moz-selection { background: rgba(0, 122, 255, 0.2); color: var(--text-primary); } /* Apple-style focus states */ .btn:focus-visible, .case-study:focus-visible, .skill-card:focus-visible { outline: 2px solid var(--primary-blue); outline-offset: 2px; box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.1); } /* Chat interface styles */ .chat-interface { background: var(--card-bg); border: 1px solid var(--border-color); border-radius: 16px; padding: 1.5rem; margin: 1.5rem 0; backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); text-align: left; } .chat-header { padding: 1.5rem; border-bottom: 1px solid var(--line); text-align: center; } .chat-messages { height: 400px; overflow-y: auto; padding: 1rem; } .chat-message { margin-bottom: 1rem; padding: 1rem; border-radius: 12px; width: fit-content; max-width: 70%; } .chat-message.user { background: var(--primary-blue); color: white; margin-left: auto; text-align: right; width: fit-content; max-width: calc(100% - 1rem); margin-right: 0; } .chat-message.assistant { background: rgba(118, 118, 128, 0.24); border: 1px solid var(--border-color); width: fit-content; max-width: calc(100% - 1rem); margin-right: auto; } /* Streaming indicator */ .chat-message.streaming::after { content: ; display: inline-block; width: 2px; height: 1em; background: var(--primary-blue); margin-left: 3px; vertical-align: text-bottom; animation: cursor-blink 1s infinite; } @keyframes cursor-blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } } @media (prefers-reduced-motion: reduce) { .chat-message.streaming::after { animation: none; opacity: 0.6; } } .chat-form { padding: 1rem; border-top: 1px solid var(--border-color); display: flex; gap: 0.75rem; } .chat-input { flex: 1; padding: 0.75rem 1rem; background: rgba(118, 118, 128, 0.12); border: 1px solid var(--border-color); border-radius: 20px; color: var(--text-primary); font-size: 1rem; } .chat-input:focus { outline: 2px solid var(--primary-blue); outline-offset: 2px; border-color: var(--primary-blue); } /* Loading animation */ .loading-dots { display: flex; gap: 4px; justify-content: center; margin: 1rem 0; } .loading-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--primary-blue); animation: loading-bounce 1.4s infinite ease-in-out both; } .loading-dot:nth-child(1) { animation-delay: -0.32s; } .loading-dot:nth-child(2) { animation-delay: -0.16s; } @keyframes loading-bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } } /* Header CSS now handled by /css/header.css */ .sticky-header .container { display: flex; justify-content: space-between; align-items: center; max-width: min(95vw, 1200px); margin: 0 auto; padding: 0 clamp(1rem, 3vw, 2rem); transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); } /* Flipped header layout when dropdown is on left */ .sticky-header.flipped .container { flex-direction: row-reverse; } .sticky-header.flipped .logo { order: 2; } .sticky-header.flipped .mobile-nav-toggle { order: 1; } .sticky-header .logo { display: flex; align-items: center; text-decoration: none; color: var(--text-primary); font-weight: 600; font-size: clamp(1rem, 2.5vw, 1.2rem); transition: color 0.3s ease; } .sticky-header .logo:hover { color: var(--primary-blue); } .sticky-header .logo img { width: clamp(28px, 4vw, 36px); height: clamp(28px, 4vw, 36px); border-radius: 50%; margin-right: clamp(0.5rem, 1.5vw, 0.75rem); border: 2px solid var(--border-color); object-fit: cover; } .sticky-nav { display: flex; align-items: center; gap: clamp(0.5rem, 2vw, 1rem); } .sticky-nav .nav-btn { display: inline-flex; align-items: center; justify-content: center; padding: clamp(8px, 1.5vw, 12px) clamp(16px, 3vw, 20px); border: 1px solid var(--border-color); color: var(--text-primary); text-decoration: none; border-radius: clamp(16px, 2vw, 20px); font-size: clamp(0.8rem, 2vw, 0.9rem); font-weight: 500; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); background: rgba(28, 28, 30, 0.3); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); letter-spacing: -0.01em; white-space: nowrap; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); } .sticky-nav .nav-btn:hover { background: rgba(255, 255, 255, 0.12); border-color: rgba(255, 255, 255, 0.35); transform: translateY(-2px); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); } .sticky-nav .nav-btn.primary { background: var(--primary-blue); border-color: var(--primary-blue); color: white; box-shadow: 0 2px 10px rgba(0, 122, 255, 0.2); } .sticky-nav .nav-btn.primary:hover { background: var(--primary-blue-hover); border-color: var(--primary-blue-hover); box-shadow: 0 4px 20px rgba(0, 122, 255, 0.3); } /* Mobile Navigation */ .mobile-nav-toggle { display: none; background: none; border: 1px solid var(--border-color); color: var(--text-primary); padding: clamp(8px, 1.5vw, 10px); border-radius: clamp(12px, 2vw, 16px); cursor: pointer; transition: all 0.3s ease; backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); } .mobile-nav-toggle:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.35); } .mobile-nav-toggle span { display: block; width: 20px; height: 2px; background: var(--text-primary); margin: 4px 0; transition: 0.3s; border-radius: 2px; } .mobile-nav-menu { position: fixed; bottom: 0; left: 0; right: 0; background: transparent; padding: 0; text-align: right; z-index: 1001; transform: translateY(100%); transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); touch-action: pan-x; } /* Enhanced opaque background panel with directional margins */ .mobile-nav-menu::after { content: ; position: absolute; bottom: clamp(2rem, 5vw, 3rem); right: clamp(1rem, 3vw, 2rem); width: auto; min-width: 200px; height: auto; background: /* Enhanced gradient with directional blur from center to edges */ radial-gradient( ellipse at var(--panel-gradient-center, 80%) 60%, rgba(0, 0, 0, 0.98) 0%, rgba(0, 0, 0, 0.95) 25%, rgba(0, 0, 0, 0.92) 45%, rgba(0, 0, 0, 0.88) 65%, rgba(0, 0, 0, 0.82) 80%, rgba(0, 0, 0, 0.75) 90%, rgba(0, 0, 0, 0.65) 100% ), linear-gradient(145deg, rgba(0, 0, 0, 0.95) 0%, rgba(0, 0, 0, 0.9) 50%, rgba(0, 0, 0, 0.92) 100% ); backdrop-filter: blur(35px) saturate(115%); -webkit-backdrop-filter: blur(35px) saturate(115%); border-radius: clamp(24px, 6vw, 36px); border: 1px solid rgba(255, 255, 255, 0.08); z-index: -1; transition: all 0.6s cubic-bezier(0.25, 0.46, 0.45, 0.94); opacity: 0; transform: scale(0.88) translateY(15px); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.25), 0 2px 8px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.12), inset 0 -1px 0 rgba(255, 255, 255, 0.04); /* Calculate size based on content with left margin */ top: 0; left: auto; /* Add margin on the left side when on right */ margin-left: clamp(3rem, 8vw, 5rem); } /* Show background panel when menu is active */ .mobile-nav-menu.active::after { opacity: 1; transform: scale(1) translateY(0); } /* Adjust panel position and gradient for left alignment */ .mobile-nav-menu.left-aligned::after { right: auto; left: clamp(1rem, 3vw, 2rem); /* Flip gradient center for left alignment */ --panel-gradient-center: 20%; /* Add margin to right side when on left */ margin-left: 0; margin-right: clamp(3rem, 8vw, 5rem); } /* Navigation options container */ .mobile-nav-menu .nav-options { position: relative; z-index: 1; padding: clamp(2rem, 5vw, 3rem) clamp(1.5rem, 4vw, 2.5rem) clamp(2rem, 5vw, 3rem); display: inline-block; margin-left: auto; margin-right: clamp(1rem, 3vw, 2rem); } /* Left-aligned navigation options */ .mobile-nav-menu.left-aligned .nav-options { margin-left: clamp(1rem, 3vw, 2rem); margin-right: auto; } /* Enhanced seamless background gradient that blends into content */ .mobile-nav-menu::before { content: ; position: absolute; top: -120vh; left: 0; right: 0; height: calc(100% + 120vh); background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; pointer-events: none; z-index: -2; transition: all 1s cubic-bezier(0.25, 0.46, 0.45, 0.94); } /* Apply enhanced gradient and blur only when menu is active */ .mobile-nav-menu.active::before { background: /* Ultra-smooth gradient with extended blending */ linear-gradient( to top, /* Bottom section: Solid behind opaque panel */ rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.12) 15%, rgba(0, 0, 0, 0.08) 30%, /* Extended middle transition: Ultra-smooth blend */ rgba(0, 0, 0, 0.06) 45%, rgba(0, 0, 0, 0.04) 60%, rgba(0, 0, 0, 0.025) 70%, rgba(0, 0, 0, 0.015) 78%, rgba(0, 0, 0, 0.008) 85%, rgba(0, 0, 0, 0.004) 90%, rgba(0, 0, 0, 0.002) 94%, rgba(0, 0, 0, 0.001) 97%, /* Complete fade to transparent */ transparent 100% ), /* Enhanced side gradients for seamless horizontal blending */ radial-gradient( ellipse at var(--gradient-center, 85%) 40%, transparent 0%, rgba(0, 0, 0, 0.02) 30%, rgba(0, 0, 0, 0.05) 50%, rgba(0, 0, 0, 0.08) 65%, rgba(0, 0, 0, 0.12) 78%, rgba(0, 0, 0, 0.15) 88%, rgba(0, 0, 0, 0.18) 95%, rgba(0, 0, 0, 0.2) 100% ); backdrop-filter: blur(40px) saturate(120%) brightness(1.05); -webkit-backdrop-filter: blur(40px) saturate(120%) brightness(1.05); } /* Left-aligned state */ .mobile-nav-menu.left-aligned { text-align: left; } .mobile-nav-menu.left-aligned::before { --gradient-direction: right; --gradient-center: 15%; } .mobile-nav-menu.active { transform: translateY(0); animation: slideUpWithBounce 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; } /* Advanced dropdown entrance animation */ @keyframes slideUpWithBounce { 0% { opacity: 0; transform: translateY(100%) scale(0.95); } 60% { opacity: 0.8; transform: translateY(-5%) scale(1.02); } 80% { opacity: 0.95; transform: translateY(2%) scale(1.01); } 100% { opacity: 1; transform: translateY(0%) scale(1); } } /* Staggered animation for nav buttons */ .mobile-nav-menu.active .nav-btn { animation: fadeInUp 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275) forwards; opacity: 0; transform: translateY(20px); } .mobile-nav-menu.active .nav-btn:nth-child(1) { animation-delay: 0.1s; } .mobile-nav-menu.active .nav-btn:nth-child(2) { animation-delay: 0.15s; } .mobile-nav-menu.active .nav-btn:nth-child(3) { animation-delay: 0.2s; } .mobile-nav-menu.active .nav-btn:nth-child(4) { animation-delay: 0.25s; } @keyframes fadeInUp { to { opacity: 1; transform: translateY(0); } } .mobile-nav-menu .nav-btn { display: block; width: auto; max-width: none; margin-left: auto; margin-right: 0; text-align: right; margin-bottom: clamp(1.5rem, 4vw, 2rem); padding: 0; font-size: clamp(1.8rem, 6vw, 2.5rem); font-weight: 700; color: var(--text-primary); background: transparent; border: none; border-radius: 0; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); text-decoration: none; min-height: auto; display: flex; align-items: center; justify-content: flex-end; white-space: nowrap; transform: translateX(0); box-shadow: none; letter-spacing: -0.02em; } /* Left-aligned button state */ .mobile-nav-menu.left-aligned .nav-btn { margin-left: 0; margin-right: auto; text-align: left; justify-content: flex-start; } .mobile-nav-menu .nav-btn:hover { color: var(--primary-blue); transform: translateX(-8px); transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .mobile-nav-menu.left-aligned .nav-btn:hover { transform: translateX(8px); } .mobile-nav-menu .nav-btn.primary { background: transparent; border: none; color: var(--primary-blue); } .mobile-nav-menu .nav-btn.primary:hover { background: transparent; color: var(--primary-blue-hover); } .mobile-nav-menu .nav-btn:last-child { margin-bottom: 0; } /* Performance optimizations for reduced motion */ :rootstyle*--reduce-motion: 1 .mobile-nav-menu { transition-duration: 0.2s !important; } :rootstyle*--reduce-motion: 1 .mobile-nav-menu.active::before { transition-duration: 0.4s !important; backdrop-filter: blur(20px) saturate(110%) !important; } :rootstyle*--reduce-motion: 1 .mobile-nav-menu .nav-btn { animation-duration: 0.2s !important; transition-duration: 0.2s !important; } /* Respect users motion preferences */ @media (prefers-reduced-motion: reduce) { .mobile-nav-menu { transition-duration: 0.2s; } .mobile-nav-menu.active::before { transition-duration: 0.4s; backdrop-filter: blur(20px) saturate(110%); } .mobile-nav-menu .nav-btn { animation: none; transition-duration: 0.2s; } .mobile-nav-menu.active .nav-btn { animation: none; opacity: 1; transform: translateY(0); } } /* Responsive Navigation */ @media (max-width: 768px) { .sticky-nav { display: none; } .mobile-nav-toggle { display: block; } } @media (max-width: 480px) { .sticky-header { padding: clamp(1rem, 3vw, 1.25rem) 0; } .sticky-header .container { padding: 0 1rem; } .sticky-header .logo { font-size: 1rem; } .sticky-header .logo img { width: 28px; height: 28px; margin-right: 0.5rem; } } /* Scroll to Top Button */ .scroll-to-top { position: fixed; bottom: clamp(1.5rem, 4vw, 2rem); right: clamp(1.5rem, 4vw, 2rem); width: clamp(48px, 8vw, 56px); height: clamp(48px, 8vw, 56px); background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); opacity: 0; transform: translateY(20px) scale(0.8); z-index: 100; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.1); } .scroll-to-top.visible { opacity: 1; transform: translateY(0) scale(1); } .scroll-to-top:hover { background: rgba(255, 255, 255, 0.15); border-color: rgba(255, 255, 255, 0.4); transform: translateY(-3px) scale(1.05); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15), 0 4px 10px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.2); } .scroll-to-top:active { transform: translateY(-1px) scale(1.02); transition: all 0.1s ease; } .scroll-to-top svg { width: clamp(24px, 5vw, 28px); height: clamp(24px, 5vw, 28px); fill: var(--text-primary); transition: all 0.3s ease; } .scroll-to-top:hover svg { fill: var(--primary-blue); transform: translateY(-1px); } /* Reduce motion for accessibility */ @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; } .scroll-to-top { transition: opacity 0.2s ease !important; transform: none !important; } .scroll-to-top:hover { transform: none !important; } } /* Apple-style loading states */ .btn:disabled, .btnaria-disabledtrue { opacity: 0.5; cursor: not-allowed; transform: none; } /* Cookie Consent Banner */ .cookie-consent { position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000; background: rgba(28, 28, 30, 0.95); backdrop-filter: blur(40px); -webkit-backdrop-filter: blur(40px); border-top: 1px solid var(--border-color); padding: clamp(1rem, 3vw, 1.5rem) clamp(1rem, 3vw, 2rem); transform: translateY(100%); transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15); } .cookie-consent.show { transform: translateY(0); } .cookie-consent-content { max-width: min(95vw, 1200px); margin: 0 auto; display: flex; align-items: center; justify-content: space-between; gap: clamp(1rem, 3vw, 2rem); } .cookie-consent-text { flex: 1; color: var(--text-primary); font-size: clamp(0.85rem, 2.5vw, 1rem); line-height: 1.5; } .cookie-consent-text p { margin: 0 0 clamp(0.5rem, 1vw, 0.75rem) 0; } .cookie-consent-text a { color: var(--primary-blue); text-decoration: none; transition: color 0.3s ease; } .cookie-consent-text a:hover { color: var(--primary-blue-hover); text-decoration: underline; } .cookie-consent-actions { display: flex; gap: clamp(0.5rem, 1.5vw, 0.75rem); flex-shrink: 0; } .cookie-btn { padding: clamp(0.75rem, 2vw, 1rem) clamp(1.25rem, 3vw, 1.75rem); border: 1px solid var(--border-color); border-radius: clamp(12px, 2vw, 16px); font-size: clamp(0.8rem, 2vw, 0.9rem); font-weight: 500; cursor: pointer; transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94); text-decoration: none; display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; background: rgba(28, 28, 30, 0.5); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); color: var(--text-primary); } .cookie-btn:hover { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.25); transform: translateY(-2px); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); } .cookie-btn.primary { background: var(--primary-blue); border-color: var(--primary-blue); color: white; box-shadow: 0 2px 10px rgba(0, 122, 255, 0.2); } .cookie-btn.primary:hover { background: var(--primary-blue-hover); border-color: var(--primary-blue-hover); box-shadow: 0 4px 20px rgba(0, 122, 255, 0.3); } /* Responsive cookie consent */ @media (max-width: 768px) { .cookie-consent-content { flex-direction: column; text-align: center; gap: 1rem; } .cookie-consent-actions { width: 100%; justify-content: center; flex-wrap: wrap; } .cookie-btn { flex: 1; min-width: 120px; } } @media (max-width: 480px) { .cookie-consent { padding: 1rem; } .cookie-consent-actions { flex-direction: column; width: 100%; } .cookie-btn { width: 100%; flex: none; } } /style>/head>body> !-- Unified Site Header - Injected by header.js --> div idsite-header>/div> div classbackground>/div> div classfloating-elements> div classfloating-element floating-1>◆/div> div classfloating-element floating-2>◇/div> div classfloating-element floating-3>◆/div> div classfloating-element floating-4>◇/div> div classfloating-element floating-5>◆/div> /div> !-- Scroll to Top Button --> button classscroll-to-top idscroll-to-top aria-labelScroll to top> svg viewBox0 0 24 24 fillnone xmlnshttp://www.w3.org/2000/svg> path dM7 14L12 9L17 14 strokecurrentColor stroke-width2 stroke-linecapround stroke-linejoinround/> /svg> /button> !-- Cookie Consent Banner --> div idcookieConsent classcookie-consent> div classcookie-consent-content> div classcookie-consent-text> p>This site uses cookies to enhance your experience and provide analytics./p> /div> div classcookie-consent-actions> button classcookie-btn idacceptEssential>Only Essential/button> button classcookie-btn primary idacceptAll>Accept All/button> /div> /div> /div> !-- Hero Section --> section classsection idhero data-titleHome> div classcontainer> img srchttps://cameronkfox.com/assets/CameronFox.png altHeadshot of Cameron Fox classprofile-img> h2>Cameron Fox/h2> p classprofile-subtitle>Portfolio powered by my own cluster/p> h1>Not just a portfolio.br>A live system I built and run./h1> p classdescription>While classmates studied theory, I engineered a three-node hypervisor, automated workflows, and proved whats possible. This isnt theory — its reality./p> div classbutton-container> a hrefhttps://cameronkfox.com/Cameron_Fox_Resume.pdf target_blank relnoopener noreferrer classbtn titleDownload Cameron Foxs Resume>Get Resume/a> a href#case-studies classbtn titleView Cameron Foxs Case Studies>Case Studies/a> a href/blog/ classbtn titleRead Cameron Foxs Blog>Blog/a> a href/cdn-cgi/l/email-protection#7e3d1f131b0c11103e3d1f131b0c111035381106501d1113 classbtn aria-labelContact Cameron Fox titleContact Cameron Fox>Get in Touch/a> /div> /div> /section> !-- What I Bring Section --> section classsection idwhat-i-bring data-titleWhat I Bring> div classcontainer> h2>What I Bring/h2> p classsubtitle>Engineering.br>Designed with Intention./p> p classdescription>I dont just build systems — I shape them to be reliable, secure, and effortless. From hypervisor clusters to automated workflows, I engineer infrastructure that removes friction and lets clarity rise to the surface./p> div classcards-grid> div classskill-card> h3>Systems & Automation/h3> p classcard-description>I build resilient, adaptive systems. From Proxmox clusters to automated provisioning, I design workflows that recover gracefully, roll back safely, and eliminate manual work. My environments dont just run—they just work./p> /div> div classskill-card> h3>Data & Clarity/h3> p classcard-description>Every system should explain itself. I implement self-hosted analytics and real-time dashboards to turn raw data into clarity. With clear insights, every decision is made with confidence./p> /div> div classskill-card> h3>Design Mindset/h3> p classcard-description>Complexity is inevitable; confusion isnt. I design technical solutions—from cloud platforms to lesson workflows—that feel simple, elegant, and reliable. I dont just solve problems, I make them intuitive./p> /div> div classskill-card> h3>Security Built-In/h3> p classcard-description>Strong systems are secure by default. I build with least privilege, manage secrets responsibly, and design backups that actually restore. Resilience and trust are baked in from the start./p> /div> /div> /div> /section> !-- Video Production Section --> section classsection idvideo-production data-titleVideo Production> div classcontainer-wide> h2>Beyond the Code./h2> p classsubtitle>Directing. Editing. Producing./p> p classdescription>From concept to final cut, Ive produced video content that tells compelling stories. Working with clients like a hrefhttps://holdenhwm.com target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>Holden Wealth Management/a> and a hrefhttps://bceaglemfg.com target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>BC Eagle Manufacturing/a>, Ive directed, shot, and edited professional video content that brings vision to life./p> div classvideo-production-grid> div classvideo-card> video controls preloadmetadata poster/content/hwm/Hero.jpg> source src/content/hwm/Hero.m4v typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Hero Video - Holden Wealth Management/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Jon.jpg> source src/content/hwm/Jon.m4v typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Jons Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Whitney.jpg> source src/content/hwm/Whitney.m4v typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Whitneys Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Jack.jpg> source src/content/hwm/Jack.mp4 typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Jacks Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Bella.jpg> source src/content/hwm/Bella.mp4 typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Bellas Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Joshua.jpg> source src/content/hwm/Joshua.m4v typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Joshuas Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Lydia.jpg> source src/content/hwm/Lydia.m4v typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Lydias Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Max.jpg> source src/content/hwm/Max.mp4 typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Maxs Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> div classvideo-card> video controls preloadmetadata poster/content/hwm/Suoming.jpg> source src/content/hwm/Suoming.mp4 typevideo/mp4> Your browser does not support the video tag. /video> div classvideo-card-content> h3>Suomings Story/h3> p classvideo-client>Client: Holden Wealth Management/p> /div> /div> /div> div stylemargin-top: 3rem;> h3 stylefont-size: clamp(1.2rem, 3vw, 1.8rem); margin-bottom: 1rem;>Editors Notes/h3> div stylebackground: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 16px; padding: clamp(1.5rem, 3vw, 2rem); margin-bottom: 2.5rem; text-align: left;> p classdescription stylemargin-bottom: 1.5rem; font-size: clamp(0.85rem, 2.2vw, 1rem); line-height: 1.7;> These videos prove that creative vision doesnt require multi-million dollar equipment — it only requires maximizing the potential of what you already have. Professional results come from understanding your tools and having a clear vision, not from the price tag. /p> div stylemargin-bottom: 1.5rem;> h4 stylefont-size: clamp(0.95rem, 2.3vw, 1.15rem); color: var(--text-primary); margin-bottom: 0.75rem; font-weight: 600;>Holden Wealth Management Series/h4> p classdescription stylemargin: 0 0 0.5rem 0; font-size: clamp(0.8rem, 2vw, 0.95rem); line-height: 1.6;> strong>Camera:/strong> iPhone 16 Probr> strong>Editing:/strong> Final Cut Pro, DaVinci Resolve, Autodesk Flamebr> strong>Hardware:/strong> Soft boxes, DJI Osmo Mobile 3, DJI Mic 2br> strong>Audio:/strong> Recorded with DJI Mic 2, edited and mastered in Adobe Auditionbr> strong>Stock Footage:/strong> Drone shots sourced from Adobe Stock /p> /div> div stylemargin-bottom: 1.5rem;> h4 stylefont-size: clamp(0.95rem, 2.3vw, 1.15rem); color: var(--text-primary); margin-bottom: 0.75rem; font-weight: 600;>BC Eagle Manufacturing - In the Making Series/h4> p classdescription stylemargin: 0 0 0.5rem 0; font-size: clamp(0.8rem, 2vw, 0.95rem); line-height: 1.6;> strong>Camera:/strong> Sony A7R 3br> strong>Editing:/strong> Final Cut Probr> strong>Inspiration:/strong> Heavily inspired by a hrefhttps://www.youtube.com/@danielschiffer target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>Daniel Schiffers/a> dynamic product cinematography style /p> /div> div stylemargin-bottom: 1.5rem;> h4 stylefont-size: clamp(0.95rem, 2.3vw, 1.15rem); color: var(--text-primary); margin-bottom: 0.75rem; font-weight: 600;>Brown County Barn Quilt Painting Series/h4> p classdescription stylemargin: 0 0 0.5rem 0; font-size: clamp(0.8rem, 2vw, 0.95rem); line-height: 1.6;> strong>Camera:/strong> iPhone 13 Probr> strong>Editing:/strong> Final Cut Probr> strong>Hardware:/strong> DJI Osmo Mobile 3, lapel micbr> strong>Audio:/strong> Recorded to subjects iPhone via Voice Memos app through lapel mic /p> /div> p classdescription stylemargin: 1.5rem 0 0 0; font-size: clamp(0.8rem, 2vw, 0.95rem); line-height: 1.6; font-style: italic; color: var(--text-secondary);> All Final Cut Pro plugins sourced from a hrefhttps://store.pixelfilmstudios.com target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>Pixel Film Studios/a> /p> /div> h3 stylefont-size: clamp(1.2rem, 3vw, 1.8rem); margin-bottom: 1rem;>Additional Work/h3> p classdescription stylemargin-bottom: 1.5rem;>View more of my video production work on YouTube, including projects for BC Eagle Manufacturing and Brown County Barn Quilt Painting./p> div stylemargin-bottom: 2rem;> h4 stylefont-size: clamp(1rem, 2.5vw, 1.3rem); color: var(--text-secondary); margin-bottom: 1rem; font-weight: 600;>In the Making Series & Summer Internship/h4> p classdescription stylemargin-bottom: 1rem; font-size: clamp(0.8rem, 2vw, 0.95rem);>View on a hrefhttps://www.youtube.com/@bceaglemanufacturing9494 target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>BC Eagle Manufacturings YouTube Channel/a>/p> div classyoutube-links> a hrefhttps://www.youtube.com/watch?vay6khcHosPM target_blank relnoopener noreferrer classyoutube-link>In the Making - CNC Mill/a> a hrefhttps://www.youtube.com/watch?vXi5-DDKW7gU target_blank relnoopener noreferrer classyoutube-link>In the Making - Shirt/a> a hrefhttps://www.youtube.com/watch?vR7NRcD6KNiY target_blank relnoopener noreferrer classyoutube-link>In the Making - ProShop Bottle Openers/a> a hrefhttps://www.youtube.com/watch?vfYzbJsA_JMA target_blank relnoopener noreferrer classyoutube-link>Summer Internship - Sponsored by Conexus/a> /div> /div> div> h4 stylefont-size: clamp(1rem, 2.5vw, 1.3rem); color: var(--text-secondary); margin-bottom: 1rem; font-weight: 600;>Barn Quilt Painting Series/h4> p classdescription stylemargin-bottom: 1rem; font-size: clamp(0.8rem, 2vw, 0.95rem);>View on a hrefhttps://www.youtube.com/@browncountybarnquiltpainti8021 target_blank relnoopener noreferrer stylecolor: var(--primary-blue); text-decoration: none;>Brown County Barn Quilt Paintings YouTube Channel/a>/p> div classyoutube-links> a hrefhttps://www.youtube.com/watch?vHfj-uxBFESg target_blank relnoopener noreferrer classyoutube-link>Episode 1: Intro to Barn Quilts/a> a hrefhttps://www.youtube.com/watch?vSyO1T_H6o2I target_blank relnoopener noreferrer classyoutube-link>Episode 2: Patriotic Pattern/a> a hrefhttps://www.youtube.com/watch?vyO-6xl1qzcg target_blank relnoopener noreferrer classyoutube-link>Episode 3: Ohio Star/a> a hrefhttps://www.youtube.com/watch?vlcPpW6qKjpI target_blank relnoopener noreferrer classyoutube-link>Episode 4: Black Eyed Susan/a> a hrefhttps://www.youtube.com/watch?vsL9uBn6DGUY target_blank relnoopener noreferrer classyoutube-link>Episode 5: Lemoyne Star/a> a hrefhttps://www.youtube.com/watch?vFYfsj7lLFw4 target_blank relnoopener noreferrer classyoutube-link>Episode 6: Mariners Compass/a> /div> /div> /div> /div> /section> !-- Interactive Demo Section --> section classsection iddemo data-titleInteractive Demo> div classcontainer> h2>Interactive Demo/h2> p classsubtitle>See it run./p> p classdescription>Launch a live Linux environment hosted on my cluster. In seconds, a VM spins up — yours to explore, control, and test — all from your browser. Its not a simulation. Its real infrastructure, provisioned on demand./p> p classdemo-note>Best experienced on desktop or tablet./p> div stylebackground: rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.2); border-radius: 12px; padding: 1.5rem; margin: 2rem 0; text-align: center;> h3 stylecolor: var(--text-secondary); font-size: 1.1rem; margin-bottom: 0.5rem;>Coming Soon/h3> p stylecolor: var(--text-tertiary); font-size: 0.9rem; margin: 0;>The interactive demo is currently being prepared and will be available shortly./p> /div> div classbutton-container> a href/api/demo/start classbtn btn-primary idlaunch-demo>Launch Demo/a> /div> /div> /section> !-- Short URL Section --> section classsection idshort-url data-titleShort URL> div classcontainer> h2>Performance in every redirect./h2> p classsubtitle>Proof that even the smallest link can hold everything together./p> p classdescription>I built this short-URL service not because the world needed another one, but because I wanted to prove a point. That I can design, deploy, and run a complete mini-service end to end. Every redirect flows through infrastructure I own, tuned for performance and reliability. Its more than a tool — its a statement of technical and networking expertise, distilled into something anyone can try./p> div classbutton-container> a hrefhttps://cameronkfox.com/shorturl.html target_blank relnoopener noreferrer classbtn btn-primary>Try It Out/a> /div> /div> /section> !-- Meet the Cameron Model Section --> section classsection idai-model data-titleAI Model> div classtron-grid-container> div classtron-grid>/div> div classlightcycle> div classlightcycle-body>/div> div classlightcycle-front>/div> div classlightcycle-wheel>/div> div classlightcycle-wheel>/div> /div> div classend-of-line>END OF LINE/div> /div> div classcontainer> h2>Meet the Cameron Model./h2> p classsubtitle>An AI chatbot, trained on my experience, ready to answer for me./p> p classdescription>Its more than a chatbot. Its a demonstration of the systems, training, and infrastructure I built to make it real./p> p classdescription mono stylefont-size: 0.8rem; margin-top: -1.5rem; margin-bottom: 2rem;>Powered locally by NVIDIA Tesla GPUs/p> div classchat-interface> div classchat-header> h3>Camerons AI Assistant/h3> p stylecolor: var(--text-tertiary); font-size: 0.9rem; margin: 0;>Self-hosted AI • Built on my infrastructure/p> /div> div classchat-messages idchat-messages> div classchat-message assistant> p>Hello! Im Camerons AI assistant. Ask me about his projects, skills, or experience, and Ill answer as if I were Cameron himself./p> /div> /div> form classchat-form idchat-form> input typetext classchat-input idchat-input placeholderAsk about my projects, skills, or experience… autocompleteoff> button typesubmit classbtn btn-primary>Send/button> /form> /div> !-- AI Disclaimer --> p stylemargin-top: 1.5rem; font-size: 0.85rem; color: var(--text-tertiary); font-style: italic; text-align: center; line-height: 1.4;> em>AI systems can occasionally provide incomplete or inaccurate responses. For the most current and accurate information, please contact me directly./em> /p> /div> /section> !-- Case Studies Section --> section classsection case-studies-section idcase-studies data-titleCase Studies> div classcontainer-wide> h1>Case Closed./h1> p classsubtitle>Proof delivered./p> p classdescription>Each case study is a system built, deployed, and proven — designed to solve real problems and show what I can engineer end-to-end./p> div classcase-studies-grid> div classcase-study> h3>Proxmox Cluster/h3> p>A three-node hypervisor, built from surplus hardware and scaled into a private cloud powering AI, media delivery, and live demos./p> a href/assets/Proxmox-Interactive.pdf target_blank relnoopener noreferrer classbtn btn-arrow>View Case Study/a> /div> div classcase-study> h3>Swim Lesson Tracking/h3> p>A manual, error-prone workflow turned into an automated system on Microsoft Power Platform — saving hours, eliminating mistakes, and improving client experience./p> a href/assets/SwimLesson-Interactive.pdf target_blank relnoopener noreferrer classbtn btn-arrow>View Case Study/a> /div> div classcase-study> h3>DNS Hijack Demo/h3> p>A live classroom demonstration of DNS hijacking that turned abstract risks into a hands-on, unforgettable security lesson./p> a href/assets/DNS-Interactive.pdf target_blank relnoopener noreferrer classbtn btn-arrow>View Case Study/a> /div> div classcase-study> h3>Portfolio Analytics/h3> p>Replacing Google Analytics with a self-hosted system that tracks every chatbot session — creating a real-time feedback loop that makes the model smarter with each interaction./p> a href/assets/Portfolio-Interactive.pdf target_blank relnoopener noreferrer classbtn btn-arrow>View Case Study/a> /div> div classcase-study> h3>Nimbus/h3> p>A cross-platform automation layer that unifies Azure, AWS, Okta, Intune, and Cloudflare into a single flow — IT operations orchestrated in seconds./p> a href/assets/Nimbus-Interactive.pdf target_blank relnoopener noreferrer classbtn btn-arrow>View Case Study/a> /div> /div> /div> /section> !-- More Than a Portfolio Section --> section classsection idmore-than-portfolio data-titleMore Than Portfolio> div classcontainer> h2>More Than a Portfolio./h2> p classsubtitle>A platform that proves whats possible./p> p classdescription>This portfolio doesnt just showcase work — it runs on the infrastructure I built. Proof that my systems arent slides or simulations, but real engineering, end to end./p> div classbutton-container> a hrefhttps://cameronkfox.com/Cameron_Fox_Resume.pdf target_blank relnoopener noreferrer classbtn titleDownload Cameron Foxs Resume>Get Resume/a> a href/cdn-cgi/l/email-protection#d192b0bcb4a3bebf9192b0bcb4a3bebf9a97bea9ffb2bebc classbtn aria-labelContact Cameron Fox titleContact Cameron Fox>Get in Touch/a> /div> /div> /section> script data-cfasyncfalse src/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js>/script>script type377e939800cdf2b3e0a8259d-text/javascript> // Global function for Matomo chat logging (fallback if not available) window.matomoLogChatTurn function(data) { fetch(/api/matomo/chatlog.php, { method:POST, headers:{Content-Type:application/json}, body: JSON.stringify(data) }).catch(()>{}); }; document.addEventListener(DOMContentLoaded, () > { const sections Array.from(document.querySelectorAll(.section)); // Cookie Consent Management function initCookieConsent() { const cookieConsent document.getElementById(cookieConsent); const acceptAllBtn document.getElementById(acceptAll); const acceptEssentialBtn document.getElementById(acceptEssential); const COOKIE_NAME cameron-cookie-consent; const COOKIE_EXPIRY_DAYS 365; // Check if consent already given function hasConsent() { return localStorage.getItem(COOKIE_NAME) accepted || getCookie(COOKIE_NAME) accepted; } // Set cookie with expiry function setCookie(name, value, days) { const expires new Date(); expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000)); document.cookie name + + value + ;expires + expires.toUTCString() + ;path/;SameSiteLax; } // Get cookie value function getCookie(name) { const nameEQ name + ; const ca document.cookie.split(;); for(let i 0; i ca.length; i++) { let c cai; while (c.charAt(0) ) c c.substring(1, c.length); if (c.indexOf(nameEQ) 0) return c.substring(nameEQ.length, c.length); } return null; } // Show consent banner function showConsent() { if (cookieConsent) { setTimeout(() > { cookieConsent.classList.add(show); }, 1000); // Show after 1 second for better UX } } // Hide consent banner function hideConsent() { if (cookieConsent) { cookieConsent.classList.remove(show); setTimeout(() > { cookieConsent.style.display none; }, 400); // Wait for animation to complete } } // Accept consent (both buttons do the same thing) function acceptConsent() { // Store consent in both localStorage and cookie for redundancy localStorage.setItem(COOKIE_NAME, accepted); setCookie(COOKIE_NAME, accepted, COOKIE_EXPIRY_DAYS); // Track the consent acceptance if (typeof _paq ! undefined) { _paq.push(trackEvent, Cookie, ConsentAccepted, CookieBanner); } // Hide the banner hideConsent(); } // Event listeners if (acceptAllBtn) { acceptAllBtn.addEventListener(click, acceptConsent); } if (acceptEssentialBtn) { acceptEssentialBtn.addEventListener(click, acceptConsent); } // Initialize if (!hasConsent()) { showConsent(); } else { // Hide banner if already consented if (cookieConsent) { cookieConsent.style.display none; } } // Track when banner is shown if (!hasConsent() && typeof _paq ! undefined) { _paq.push(trackEvent, Cookie, BannerShown, CookieBanner); } } // Initialize cookie consent initCookieConsent(); // Handle logo click behavior for smooth scroll to top function initLogoScrollBehavior() { const headerLogo document.getElementById(header-logo); if (headerLogo && (window.location.pathname / || window.location.pathname /index.html)) { headerLogo.addEventListener(click, function(e) { e.preventDefault(); window.scrollTo({ top: 0, behavior: smooth }); // Track logo click if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, LogoScrollTop, HeaderLogo); } }); } } // Initialize logo scroll behavior initLogoScrollBehavior(); // Scroll to Top Button functionality function initScrollToTop() { const scrollToTopBtn document.getElementById(scroll-to-top); if (!scrollToTopBtn) return; // Show/hide button based on scroll position function toggleScrollButton() { const scrollPosition window.pageYOffset || document.documentElement.scrollTop; const windowHeight window.innerHeight; if (scrollPosition > windowHeight * 0.5) { scrollToTopBtn.classList.add(visible); } else { scrollToTopBtn.classList.remove(visible); } } // Scroll to top on click scrollToTopBtn.addEventListener(click, function() { window.scrollTo({ top: 0, behavior: smooth }); // Track scroll to top if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, ScrollToTop, ScrollButton); } }); // Listen for scroll events let scrollTimeout; window.addEventListener(scroll, function() { // Throttle scroll events for performance clearTimeout(scrollTimeout); scrollTimeout setTimeout(toggleScrollButton, 10); }, { passive: true }); // Initial check toggleScrollButton(); } // Initialize scroll to top button initScrollToTop(); // Sticky Header Navigation function initStickyHeader() { const stickyHeader document.getElementById(sticky-header); const heroSection document.getElementById(hero); const mobileNavToggle document.getElementById(mobile-nav-toggle); const mobileNavMenu document.getElementById(mobile-nav-menu); if (!stickyHeader || !heroSection) return; let lastScrollY window.scrollY; let ticking false; function updateStickyHeader() { const currentScrollY window.scrollY; const heroRect heroSection.getBoundingClientRect(); const heroBottom heroRect.bottom; // Show header when scrolled past hero section if (heroBottom 0) { stickyHeader.classList.add(visible); } else { stickyHeader.classList.remove(visible); // Close mobile menu when header disappears mobileNavMenu.classList.remove(active); } lastScrollY currentScrollY; ticking false; } function requestTick() { if (!ticking) { requestAnimationFrame(updateStickyHeader); ticking true; } } // Throttled scroll event window.addEventListener(scroll, requestTick, { passive: true }); // Mobile navigation toggle with over-engineered enhancements if (mobileNavToggle && mobileNavMenu) { // Preload hover state for smoother interactions mobileNavToggle.addEventListener(mouseenter, () > { mobileNavMenu.style.willChange transform, opacity; mobileNavMenu.style.transform translateY(99%); // Pre-position for faster animation }); mobileNavToggle.addEventListener(mouseleave, () > { if (!mobileNavMenu.classList.contains(active)) { mobileNavMenu.style.transform translateY(100%); setTimeout(() > { mobileNavMenu.style.willChange auto; }, 300); } }); mobileNavToggle.addEventListener(click, (e) > { e.preventDefault(); // Add subtle button feedback mobileNavToggle.style.transform scale(0.95); setTimeout(() > { mobileNavToggle.style.transform scale(1); }, 150); const isOpening !mobileNavMenu.classList.contains(active); mobileNavMenu.classList.toggle(active); // Enhanced opening animation if (isOpening) { // Reset any previous positioning mobileNavMenu.style.willChange transform, opacity; // Trigger reflow for smoother animation mobileNavMenu.offsetHeight; // Add anticipatory micro-animation to buttons const navButtons mobileNavMenu.querySelectorAll(.nav-btn); navButtons.forEach((btn, index) > { btn.style.transform translateY(30px) scale(0.9); btn.style.opacity 0; btn.style.transition `all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)`; btn.style.transitionDelay `${0.1 + index * 0.05}s`; }); // Trigger button animations after a frame requestAnimationFrame(() > { navButtons.forEach(btn > { btn.style.transform translateY(0) scale(1); btn.style.opacity 1; }); }); } // Track mobile nav toggle if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, MobileToggle, mobileNavMenu.classList.contains(active) ? Open : Close); } }); // Enhanced swipe gesture detection for side switching let touchStartX 0; let touchStartY 0; let touchEndX 0; let touchEndY 0; let isSwipeGesture false; let swipeThreshold 50; let velocityThreshold 0.5; let swipeStartTime 0; mobileNavMenu.addEventListener(touchstart, (e) > { if (!mobileNavMenu.classList.contains(active)) return; touchStartX e.touches0.clientX; touchStartY e.touches0.clientY; swipeStartTime Date.now(); isSwipeGesture true; }, { passive: true }); mobileNavMenu.addEventListener(touchmove, (e) > { if (!isSwipeGesture || !mobileNavMenu.classList.contains(active)) return; const currentX e.touches0.clientX; const currentY e.touches0.clientY; const deltaX Math.abs(currentX - touchStartX); const deltaY Math.abs(currentY - touchStartY); // If vertical movement is greater than horizontal, its not a horizontal swipe if (deltaY > deltaX) { isSwipeGesture false; return; } // Prevent default to avoid scrolling during horizontal swipe if (deltaX > 20) { e.preventDefault(); } }, { passive: false }); mobileNavMenu.addEventListener(touchend, (e) > { if (!isSwipeGesture || !mobileNavMenu.classList.contains(active)) return; touchEndX e.changedTouches0.clientX; touchEndY e.changedTouches0.clientY; const deltaX touchEndX - touchStartX; const deltaY Math.abs(touchEndY - touchStartY); const swipeDistance Math.abs(deltaX); const swipeTime Date.now() - swipeStartTime; const velocity swipeDistance / swipeTime; // Only process horizontal swipes if (deltaY 100 && (swipeDistance > swipeThreshold || velocity > velocityThreshold)) { const currentlyLeftAligned mobileNavMenu.classList.contains(left-aligned); // Add micro-interaction feedback if (navigator.vibrate) { navigator.vibrate(10); // Subtle haptic feedback } // Enhanced gesture processing with intent prediction const adjustedVelocity predictUserIntent(deltaX, velocity, gestureHistory.length); // Store gesture in history for learning gestureHistory.push({ deltaX: deltaX, velocity: velocity, time: Date.now() }); // Keep only recent gestures gestureHistory gestureHistory.filter(g > Date.now() - g.time 5000); // Swipe right to switch to right-aligned (default) if (deltaX > 0 && currentlyLeftAligned) { // Store user preference localStorage.setItem(nav-preferred-side, right); // Enhanced transition with performance awareness const transitionDuration performanceOptimizer.shouldOptimize() ? 0.4s : 0.8s; mobileNavMenu.style.transition `all ${transitionDuration} cubic-bezier(0.175, 0.885, 0.32, 1.275)`; // Trigger the change with optimized animation requestAnimationFrame(() > { mobileNavMenu.classList.remove(left-aligned); // Flip header back to normal when moving to right stickyHeader.classList.remove(flipped); // Adaptive scale effect based on gesture confidence const scaleIntensity Math.min(1.05, 1 + adjustedVelocity * 0.02); mobileNavMenu.style.transform `translateY(0) scale(${scaleIntensity})`; setTimeout(() > { mobileNavMenu.style.transform translateY(0) scale(1); }, performanceOptimizer.shouldOptimize() ? 150 : 200); }); // Track the enhanced gesture if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, SwipeGesture, SwitchToRight, Math.round(adjustedVelocity * 100)); } } // Swipe left to switch to left-aligned else if (deltaX 0 && !currentlyLeftAligned) { // Store user preference localStorage.setItem(nav-preferred-side, left); // Enhanced transition with performance awareness const transitionDuration performanceOptimizer.shouldOptimize() ? 0.4s : 0.8s; mobileNavMenu.style.transition `all ${transitionDuration} cubic-bezier(0.175, 0.885, 0.32, 1.275)`; // Trigger the change with optimized animation requestAnimationFrame(() > { mobileNavMenu.classList.add(left-aligned); // Flip header layout when dropdown moves to left stickyHeader.classList.add(flipped); // Adaptive scale effect based on gesture confidence const scaleIntensity Math.min(1.05, 1 + adjustedVelocity * 0.02); mobileNavMenu.style.transform `translateY(0) scale(${scaleIntensity})`; setTimeout(() > { mobileNavMenu.style.transform translateY(0) scale(1); }, performanceOptimizer.shouldOptimize() ? 150 : 200); }); // Track the enhanced gesture if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, SwipeGesture, SwitchToLeft, Math.round(adjustedVelocity * 100)); } } } isSwipeGesture false; }, { passive: true }); // Auto-collapse dropdown when navigation links are clicked const mobileNavLinks mobileNavMenu.querySelectorAll(.nav-btn); mobileNavLinks.forEach(link > { link.addEventListener(click, (e) > { // Add smooth collapse animation mobileNavMenu.style.transition transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); mobileNavMenu.classList.remove(active); // Track navigation click in dropdown if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, DropdownLinkClick, link.textContent.trim()); } }); }); // Close mobile menu when clicking outside document.addEventListener(click, (e) > { if (!stickyHeader.contains(e.target) && !mobileNavMenu.contains(e.target)) { mobileNavMenu.classList.remove(active); } }); // Intelligent positioning based on screen space and user preference const intelligentPositioning () > { const screenWidth window.innerWidth; const preferredSide localStorage.getItem(nav-preferred-side) || right; // Smart positioning based on screen space if (screenWidth 400 && preferredSide right) { // On very small screens, slightly favor left for thumb accessibility const leftSpace window.innerWidth * 0.3; const rightSpace window.innerWidth * 0.7; if (leftSpace > rightSpace) { mobileNavMenu.classList.add(left-aligned); } } }; // Remember users swipe preference const originalTouchEnd mobileNavMenu.addEventListener; // Enhanced gesture recognition with momentum and intent prediction let gestureHistory ; let lastGestureTime 0; const predictUserIntent (deltaX, velocity, gestureCount) > { // If user has made multiple quick swipes in same direction, amplify response const recentGestures gestureHistory.filter(g > Date.now() - g.time 2000); const sameDirectionGestures recentGestures.filter(g > (deltaX > 0 && g.deltaX > 0) || (deltaX 0 && g.deltaX 0) ); if (sameDirectionGestures.length > 2) { return velocity * 1.5; // Amplify for confident gestures } return velocity; }; // Advanced performance monitoring const performanceOptimizer { lastFrameTime: 0, frameCount: 0, shouldOptimize: function() { this.frameCount++; const now performance.now(); if (this.frameCount % 60 0) { // Check every 60 frames const fps 1000 / (now - this.lastFrameTime) * 60; this.lastFrameTime now; // If FPS drops below 45, reduce animation complexity if (fps 45) { document.documentElement.style.setProperty(--reduce-motion, 1); return true; } } return false; } }; // Initialize intelligent positioning intelligentPositioning(); window.addEventListener(resize, intelligentPositioning, { passive: true }); // Close mobile menu on escape key with enhanced feedback document.addEventListener(keydown, (e) > { if (e.key Escape && mobileNavMenu.classList.contains(active)) { // Add subtle close animation mobileNavMenu.style.transition transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); mobileNavMenu.classList.remove(active); // Haptic feedback if available if (navigator.vibrate) { navigator.vibrate(5); } } }); } // Track navigation clicks const navLinks stickyHeader.querySelectorAll(.nav-btn); navLinks.forEach(link > { link.addEventListener(click, (e) > { const linkText link.textContent.trim(); const linkHref link.href; // Track navigation click if (typeof _paq ! undefined) { _paq.push(trackEvent, Navigation, Click, linkText); } // Handle internal anchor links if (linkHref.includes(#) && !linkHref.includes(mailto:)) { const targetId linkHref.split(#)1; const targetElement document.getElementById(targetId); if (targetElement) { e.preventDefault(); // Close mobile menu with smooth animation setTimeout(() > { mobileNavMenu.classList.remove(active); }, 150); // Calculate responsive offset for sticky header const headerOffset window.innerWidth 480 ? 110 : window.innerWidth 768 ? 130 : 150; const elementPosition targetElement.getBoundingClientRect().top; const offsetPosition elementPosition + window.pageYOffset - headerOffset; // Smooth scroll with offset window.scrollTo({ top: offsetPosition, behavior: smooth }); // Update URL without triggering navigation if (history.pushState) { history.pushState(null, null, `#${targetId}`); } } } else { // Close mobile menu for external links with animation setTimeout(() > { mobileNavMenu.classList.remove(active); }, 100); } }); }); // Initial check updateStickyHeader(); } // Header system now handled by /js/header.js // Enhanced Snap Scroll System class SnapScrollManager { constructor() { this.sections sections; this.currentSectionIndex 0; this.isSnapping false; this.isUserScrolling false; this.scrollTimeout null; this.lastScrollTime Date.now(); this.scrollVelocity 0; this.lastScrollY window.scrollY; this.snapThreshold 12; // Minimum scroll distance to trigger snap (more responsive) this.snapDelay 60; // Delay before snapping after scroll ends (more immediate) this.snapDuration 280; // Smooth scroll duration (faster, more Apple-like) this.momentumMultiplier 0.8; // Natural momentum feel this.currentSectionBounds null; this.allowIntraSectionScroll false; this.isAnchorNavigating false; // Flag to prevent snapping during anchor navigation this.footerElement null; // Reference to footer element for visibility calculations this.init(); } init() { this.setupScrollDetection(); this.setupKeyboardNavigation(); this.setupAnchorLinks(); this.setupIntersectionObserver(); this.setupParallax(); this.setupResizeHandler(); } setupScrollDetection() { let scrollStartTime Date.now(); let scrollStartY window.scrollY; let isScrolling false; const handleScrollStart () > { if (isScrolling) return; isScrolling true; this.isUserScrolling true; scrollStartTime Date.now(); scrollStartY window.scrollY; clearTimeout(this.scrollTimeout); }; const handleScroll () > { if (this.isSnapping) return; const now Date.now(); const currentScrollY window.scrollY; const timeDelta now - this.lastScrollTime; const scrollDelta currentScrollY - this.lastScrollY; // Calculate scroll velocity (pixels per millisecond) if (timeDelta > 0) { this.scrollVelocity Math.abs(scrollDelta / timeDelta); } this.lastScrollTime now; this.lastScrollY currentScrollY; // Update parallax during natural scrolling this.updateParallax(); }; const handleScrollEnd () > { isScrolling false; this.isUserScrolling false; clearTimeout(this.scrollTimeout); // Check if were within a large section that allows intra-section scrolling if (this.currentSectionBounds && this.allowIntraSectionScroll) { const viewportCenter window.scrollY + (window.innerHeight / 2); const bufferZone window.innerHeight * 0.1; // If viewport center is still within section bounds (with buffer), dont snap if (viewportCenter > this.currentSectionBounds.top - bufferZone && viewportCenter this.currentSectionBounds.bottom + bufferZone) { return; // No snapping needed } } // Only snap if weve scrolled more than threshold const totalScrollDistance Math.abs(window.scrollY - scrollStartY); const scrollDuration Date.now() - scrollStartTime; // Snap if weve scrolled enough distance and user has stopped if (totalScrollDistance > this.snapThreshold && scrollDuration > 50) { this.scrollTimeout setTimeout(() > { this.snapToNearestSection(); }, this.snapDelay); } }; // Main scroll event listener - this handles all scroll events window.addEventListener(scroll, () > { if (this.isSnapping) return; // If this is the first scroll event in a series if (!isScrolling) { handleScrollStart(); } // Update scroll tracking handleScroll(); // Reset the scroll end timeout with dynamic timing clearTimeout(this.scrollTimeout); // Use velocity-based timing for more responsive feel const dynamicTimeout this.scrollVelocity > 1 ? 70 : 90; this.scrollTimeout setTimeout(handleScrollEnd, dynamicTimeout); }, { passive: true }); // Touch events for mobile (additional handling) let touchStartY 0; window.addEventListener(touchstart, (e) > { touchStartY e.touches0.clientY; handleScrollStart(); }, { passive: true }); window.addEventListener(touchmove, (e) > { const touchY e.touches0.clientY; const touchDelta Math.abs(touchY - touchStartY); if (touchDelta > 5) { handleScroll(); } }, { passive: true }); window.addEventListener(touchend, handleScrollEnd, { passive: true }); // Enhanced wheel events for desktop (Apple-like precision) let wheelTimeout; let wheelVelocity 0; let lastWheelTime Date.now(); window.addEventListener(wheel, (e) > { const now Date.now(); const timeDelta now - lastWheelTime; // Calculate wheel velocity for better snap detection if (timeDelta > 0) { wheelVelocity Math.abs(e.deltaY / timeDelta); } lastWheelTime now; handleScrollStart(); // Clear existing timeouts clearTimeout(this.scrollTimeout); clearTimeout(wheelTimeout); // Use different delays based on wheel velocity (Apple-like responsiveness) const dynamicDelay wheelVelocity > 2 ? 50 : 80; // Set both timeouts for redundancy this.scrollTimeout setTimeout(handleScrollEnd, dynamicDelay); wheelTimeout setTimeout(handleScrollEnd, dynamicDelay + 10); }, { passive: true }); } snapToNearestSection() { if (this.isSnapping || this.isAnchorNavigating) return; // Find the section closest to viewport center const viewportCenter window.scrollY + (window.innerHeight / 2); let closestSectionIndex 0; let smallestDistance Infinity; this.sections.forEach((section, index) > { const sectionTop section.offsetTop; const sectionCenter sectionTop + (section.offsetHeight / 2); const distance Math.abs(viewportCenter - sectionCenter); if (distance smallestDistance) { smallestDistance distance; closestSectionIndex index; } }); const targetSection this.sectionsclosestSectionIndex; const sectionHeight targetSection.offsetHeight; const viewportHeight window.innerHeight; let finalTargetY; // Check if this is the last section const isLastSection closestSectionIndex this.sections.length - 1; // Compare section height vs viewport height if (sectionHeight > viewportHeight) { // Large section: snap to TOP of section (not center) finalTargetY targetSection.offsetTop; // Update section bounds for intra-section scrolling this.currentSectionBounds { top: targetSection.offsetTop, bottom: targetSection.offsetTop + sectionHeight, height: sectionHeight }; this.allowIntraSectionScroll true; } else { // Small section: snap to center of section const sectionCenter targetSection.offsetTop + (sectionHeight / 2); finalTargetY sectionCenter - (viewportHeight / 2); // Update section bounds this.currentSectionBounds { top: targetSection.offsetTop, bottom: targetSection.offsetTop + sectionHeight, height: sectionHeight }; this.allowIntraSectionScroll false; } // Special handling for the last section to ensure footer visibility if (isLastSection) { // Check if theres a footer below the last section const footerElement this.footerElement || document.getElementById(footer-links) || document.querySelector(divstyle*text-align: centerstyle*border-top); if (footerElement && footerElement.offsetHeight > 0) { const footerHeight footerElement.offsetHeight; const footerTop footerElement.offsetTop; // If the calculated snap position would hide the footer, adjust it if (finalTargetY + viewportHeight footerTop + footerHeight) { // Position the viewport so the footer is visible at the bottom const adjustedTargetY footerTop + footerHeight - viewportHeight; finalTargetY Math.max(finalTargetY, adjustedTargetY); } } } // Ensure we dont scroll beyond document bounds const maxScroll document.body.scrollHeight - window.innerHeight; finalTargetY Math.max(0, Math.min(finalTargetY, maxScroll)); const currentDistance Math.abs(window.scrollY - finalTargetY); // Only snap if were not already very close to the target // and if the distance is significant enough to warrant snapping if (currentDistance > this.snapThreshold && currentDistance window.innerHeight * 0.9) { this.performSmoothScroll(finalTargetY); } } performSmoothScroll(targetY) { this.isSnapping true; // Validate target position const maxScroll document.body.scrollHeight - window.innerHeight; const validTargetY Math.max(0, Math.min(targetY, maxScroll)); // Apple-style smooth scroll with premium easing const startY window.scrollY; const distance validTargetY - startY; const startTime Date.now(); // Apple-style easing: fast start, smooth deceleration const easeOutExpo (t) > t 1 ? 1 : 1 - Math.pow(2, -10 * t); const easeOutQuart (t) > 1 - Math.pow(1 - t, 4); const easeOutCubic (t) > 1 - Math.pow(1 - t, 3); // Smoother for shorter distances // Use different easing based on distance for optimal feel const absDistance Math.abs(distance); let easingFunction; if (absDistance > window.innerHeight * 0.8) { easingFunction easeOutExpo; // Longest distances } else if (absDistance > window.innerHeight * 0.3) { easingFunction easeOutQuart; // Medium distances } else { easingFunction easeOutCubic; // Short distances - smoother } const animateScroll () > { const elapsed Date.now() - startTime; const rawProgress Math.min(elapsed / this.snapDuration, 1); // Apply premium easing function const easedProgress easingFunction(rawProgress); // Calculate target position with sub-pixel precision const currentY startY + (distance * easedProgress); // Premium bounds checking with minimal clamping const finalY Math.max(0, Math.min(currentY, maxScroll)); window.scrollTo(0, finalY); if (rawProgress 1) { requestAnimationFrame(animateScroll); } else { // Final position with perfect precision window.scrollTo(0, validTargetY); this.isSnapping false; this.updateCurrentSection(); } }; requestAnimationFrame(animateScroll); } setupIntersectionObserver() { const observer new IntersectionObserver((entries) > { entries.forEach(entry > { if (entry.isIntersecting && entry.intersectionRatio > 0.6) { this.currentSectionIndex this.sections.indexOf(entry.target); } }); }, { threshold: 0.6, rootMargin: -10% 0px -10% 0px }); this.sections.forEach(section > observer.observe(section)); } setupKeyboardNavigation() { document.addEventListener(keydown, (e) > { if (this.isSnapping) return; // Check if chat input is focused - if so, dont handle navigation keys const chatInput document.getElementById(chat-input); if (chatInput && chatInput document.activeElement) { return; // Let the input handle the key event } switch(e.key) { case ArrowDown: case PageDown: e.preventDefault(); this.navigateToSection(this.currentSectionIndex + 1); break; case ArrowUp: case PageUp: e.preventDefault(); this.navigateToSection(this.currentSectionIndex - 1); break; case Home: e.preventDefault(); this.navigateToSection(0); break; case End: e.preventDefault(); this.navigateToSection(this.sections.length - 1); break; // Removed space bar navigation } }); } setupAnchorLinks() { document.querySelectorAll(ahref^#).forEach(anchor > { anchor.addEventListener(click, (e) > { e.preventDefault(); const targetId anchor.getAttribute(href); const targetSection document.querySelector(targetId); if (targetSection) { // Disable scroll snapping temporarily during anchor navigation this.isAnchorNavigating true; setTimeout(() > { this.isAnchorNavigating false; }, 2000); // Disable snapping for 2 seconds after anchor click // Check if we need to account for sticky header const isHeroSection targetSection.id hero; if (isHeroSection) { // For hero section, scroll to top normally targetSection.scrollIntoView({ behavior: smooth, block: start }); } else { // For other sections, account for sticky header const headerOffset window.innerWidth 480 ? 110 : window.innerWidth 768 ? 130 : 150; const elementPosition targetSection.getBoundingClientRect().top; const offsetPosition elementPosition + window.pageYOffset - headerOffset; window.scrollTo({ top: offsetPosition, behavior: smooth }); } } }); }); } navigateToSection(index) { const newIndex Math.max(0, Math.min(this.sections.length - 1, index)); if (newIndex ! this.currentSectionIndex) { this.performSmoothScroll(this.sectionsnewIndex.offsetTop); } } updateCurrentSection() { const viewportHeight window.innerHeight; const currentScrollY window.scrollY; const viewportCenter currentScrollY + (viewportHeight / 2); let currentIndex 0; let found false; // First, check if were within any section bounds this.sections.forEach((section, index) > { const sectionTop section.offsetTop; const sectionBottom sectionTop + section.offsetHeight; const sectionHeight section.offsetHeight; if (viewportCenter > sectionTop && viewportCenter sectionBottom) { currentIndex index; found true; // Update current section bounds this.currentSectionBounds { top: sectionTop, bottom: sectionBottom, height: sectionHeight }; this.allowIntraSectionScroll sectionHeight > viewportHeight; } }); // If not within any section, find the closest if (!found) { let smallestDistance Infinity; this.sections.forEach((section, index) > { const sectionCenter section.offsetTop + (section.offsetHeight / 2); const distance Math.abs(viewportCenter - sectionCenter); if (distance smallestDistance) { smallestDistance distance; currentIndex index; } }); // Update bounds for closest section const closestSection this.sectionscurrentIndex; const sectionHeight closestSection.offsetHeight; this.currentSectionBounds { top: closestSection.offsetTop, bottom: closestSection.offsetTop + sectionHeight, height: sectionHeight }; this.allowIntraSectionScroll sectionHeight > viewportHeight; } this.currentSectionIndex currentIndex; } setupParallax() { this.parallaxElements document.querySelectorAll(.floating-element); this.updateParallax(); } updateParallax() { if (this.isSnapping || !this.parallaxElements.length) return; const scrolled window.scrollY; const rate scrolled * -0.5; this.parallaxElements.forEach((element, index) > { // Apple-style parallax with subtle, natural movement const speed 0.25 + (index * 0.08); // Slower, more subtle const yOffset rate * speed; const rotation scrolled * 0.03; // Reduced rotation for subtlety // Add slight horizontal movement for more natural feel const xOffset Math.sin(scrolled * 0.001 + index) * 2; element.style.transform `translate(${xOffset}px, ${yOffset}px) rotate(${rotation + (index * 30)}deg)`; }); } setupResizeHandler() { let resizeTimeout; window.addEventListener(resize, () > { clearTimeout(resizeTimeout); resizeTimeout setTimeout(() > { // Recalculate section positions after resize this.updateCurrentSection(); }, 200); }); } } // Initialize the enhanced snap scroll system window.snapScrollManager new SnapScrollManager(); // Check for footer after a delay to ensure its been added setTimeout(() > { if (window.snapScrollManager && !window.snapScrollManager.footerElement) { window.snapScrollManager.footerElement document.getElementById(footer-links); } }, 1000); // Responsive behavior adjustments function handleResponsiveBehavior() { const viewportWidth window.innerWidth; const viewportHeight window.innerHeight; // Disable scroll snapping on very small screens if (viewportWidth 320 || viewportHeight 400) { document.documentElement.style.scrollSnapType none; } else { document.documentElement.style.scrollSnapType y proximity; } // Adjust scroll thresholds based on screen size if (window.snapScrollManager) { if (viewportWidth 480) { window.snapScrollManager.snapThreshold 10; window.snapScrollManager.snapDelay 60; } else if (viewportWidth 768) { window.snapScrollManager.snapThreshold 15; window.snapScrollManager.snapDelay 70; } else { window.snapScrollManager.snapThreshold 20; window.snapScrollManager.snapDelay 80; } } } // Initial call handleResponsiveBehavior(); // Handle orientation changes and resize window.addEventListener(resize, () > { setTimeout(handleResponsiveBehavior, 100); }); window.addEventListener(orientationchange, () > { setTimeout(handleResponsiveBehavior, 200); }); // Chat functionality function initChat() { const chatForm document.getElementById(chat-form); const chatInput document.getElementById(chat-input); const chatMessages document.getElementById(chat-messages); const apiEndpoint /api/chat/completions; let conversationId crypto.randomUUID ? crypto.randomUUID() : conv- + Date.now() + - + Math.random().toString(36).substr(2, 9); function addMessage(role, content) { const messageDiv document.createElement(div); messageDiv.className `chat-message ${role}`; const p document.createElement(p); p.textContent content; p.style.margin 0; messageDiv.appendChild(p); chatMessages.appendChild(messageDiv); chatMessages.scrollTop chatMessages.scrollHeight; } function showLoading() { const loadingDiv document.createElement(div); loadingDiv.className chat-message assistant loading; loadingDiv.id loading-message; loadingDiv.innerHTML ` div classloading-dots> div classloading-dot>/div> div classloading-dot>/div> div classloading-dot>/div> /div> p stylemargin: 0; font-size: 0.9rem; color: var(--text-tertiary);>AI is thinking.../p> `; chatMessages.appendChild(loadingDiv); chatMessages.scrollTop chatMessages.scrollHeight; return loadingDiv; } function removeLoading() { const loading document.getElementById(loading-message); if (loading) loading.remove(); } function logToMatomo(role, text) { // Use enhanced tracking function window.trackChatInteraction(role, text, conversationId); // Also send to local API for additional logging const data { role, text, conversationId, page: location.pathname }; fetch(/api/matomo/chatlog.php, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(data) }).catch(() > {}); } // Track when chat interface becomes visible const chatObserver new IntersectionObserver((entries) > { entries.forEach(entry > { if (entry.isIntersecting && typeof _paq ! undefined) { _paq.push(trackEvent, Chat, InterfaceVisible, ChatOpened); } }); }, { threshold: 0.5 }); chatObserver.observe(document.getElementById(chat-messages)); async function streamPortfolioAI(userMessage, onDelta) { const resp await fetch(/api/ai-proxy.php, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ model: portfolio-ollama, stream: true, messages: { role: user, content: userMessage } , max_tokens: 400 }) }); if (!resp.ok) { const errorText await resp.text(); throw new Error(`AI HTTP ${resp.status}: ${errorText}`); } if (!resp.body) { throw new Error(No response body available); } const reader resp.body.getReader(); const decoder new TextDecoder(); let buffer ; while (true) { const { value, done } await reader.read(); if (done) break; buffer + decoder.decode(value, { stream: true }); const frames buffer.split(\n\n); buffer frames.pop() || ; for (const frame of frames) { if (frame.startsWith(:)) continue; if (!frame.startsWith(data:)) continue; const json frame.slice(5).trim(); if (!json || json DONE) continue; try { const evt JSON.parse(json); const delta evt && evt.choices && evt.choices0 && evt.choices0.delta && evt.choices0.delta.content || ; if (delta) onDelta(delta); } catch {} } } } // Expose for DevTools testing window.streamPortfolioAI streamPortfolioAI; chatForm.addEventListener(submit, async (e) > { e.preventDefault(); const message chatInput.value.trim(); if (!message) return; // Add user message addMessage(user, message); logToMatomo(user, message); chatInput.value ; // Show loading initially const loadingEl showLoading(); // Create assistant message container with streaming indicator const assistantEl document.createElement(div); assistantEl.className chat-message assistant streaming; const p document.createElement(p); p.textContent ; p.style.margin 0; assistantEl.appendChild(p); chatMessages.appendChild(assistantEl); chatMessages.scrollTop chatMessages.scrollHeight; let responseText ; let lastScrollTime 0; let hasStarted false; try { await streamPortfolioAI(message, (delta) > { if (!hasStarted) { removeLoading(); hasStarted true; } responseText + delta; p.textContent responseText; // Smooth scroll: throttle to avoid jank const now Date.now(); if (now - lastScrollTime > 100) { chatMessages.scrollTop chatMessages.scrollHeight; lastScrollTime now; } }); // Remove streaming indicator when complete assistantEl.classList.remove(streaming); chatMessages.scrollTop chatMessages.scrollHeight; logToMatomo(assistant, responseText); } catch (error) { removeLoading(); assistantEl.classList.remove(streaming); console.error(Chat error details:, error); const banner document.createElement(div); banner.className retry-banner; banner.textContent ⚠️ Network error. Click to retry.; banner.style.cursor pointer; banner.style.padding 12px 16px; banner.style.margin 8px 0; banner.style.borderRadius 8px; banner.style.background rgba(255, 59, 48, 0.1); banner.style.border 1px solid rgba(255, 59, 48, 0.3); banner.style.color #ff3b30; banner.style.fontSize 0.9em; banner.style.fontWeight 500; banner.style.textAlign center; banner.style.transition all 0.2s ease; banner.onmouseenter () > banner.style.background rgba(255, 59, 48, 0.15); banner.onmouseleave () > banner.style.background rgba(255, 59, 48, 0.1); banner.onclick async () > { banner.remove(); p.textContent ; assistantEl.classList.add(streaming); responseText ; hasStarted false; try { await streamPortfolioAI(message, (delta) > { responseText + delta; p.textContent responseText; const now Date.now(); if (now - lastScrollTime > 100) { chatMessages.scrollTop chatMessages.scrollHeight; lastScrollTime now; } }); assistantEl.classList.remove(streaming); logToMatomo(assistant, responseText); } catch (e2) { assistantEl.classList.remove(streaming); console.error(Retry failed:, e2); } }; chatMessages.appendChild(banner); logToMatomo(error, error.message); } }); // Removed auto-focus behavior to prevent unwanted scrolling during navigation } // Initialize chat initChat(); // Demo modal functionality function initDemo() { const launchBtn document.getElementById(launch-demo); if (launchBtn) { launchBtn.addEventListener(click, async (e) > { e.preventDefault(); // Track demo button click if (typeof _paq ! undefined) { _paq.push(trackEvent, Demo, LaunchAttempt, DemoStarted); } try { // Simulate demo start API call const response await fetch(/api/demo/start, { method: POST, headers: { Content-Type: application/json } }); if (response.ok) { const data await response.json(); if (data.sessionUrl) { // Track successful demo launch if (typeof _paq ! undefined) { _paq.push(trackEvent, Demo, LaunchSuccess, DemoLaunched); } window.open(data.sessionUrl, _blank); } else { // Track demo unavailable if (typeof _paq ! undefined) { _paq.push(trackEvent, Demo, LaunchFailed, NoSessionUrl); } alert(Demo temporarily unavailable. Please try again later or contact me directly.); } } else { // Track demo unavailable if (typeof _paq ! undefined) { _paq.push(trackEvent, Demo, LaunchFailed, HTTPError); } throw new Error(Demo unavailable); } } catch (error) { // Track demo launch failure if (typeof _paq ! undefined) { _paq.push(trackEvent, Demo, LaunchFailed, Exception); } alert(Demo temporarily unavailable. Please try again later or contact me directly.); } }); } } // Initialize demo initDemo(); }); // Tiny footer links document.addEventListener(DOMContentLoaded, function() { const footerHtml ` div idfooter-links styletext-align: center; padding: 2rem 1rem 1rem; border-top: 1px solid rgba(84, 84, 88, 0.25); margin-top: 3rem; min-height: 60px;> div stylefont-size: 0.75rem; color: rgba(235, 235, 245, 0.5);> a href/press/ stylecolor: inherit; text-decoration: none; margin: 0 0.5rem;>Press/a> a href/recruiters/ stylecolor: inherit; text-decoration: none; margin: 0 0.5rem;>Recruiters/a> /div> /div> `; document.body.insertAdjacentHTML(beforeend, footerHtml); // Notify the snap system that the footer has been added if (window.snapScrollManager) { // Give the DOM a moment to update setTimeout(() > { window.snapScrollManager.footerElement document.getElementById(footer-links); }, 100); } }); // Load Matomo silently (handled by /analytics/matomo.js) setTimeout(function() { var _paq window._paq window._paq || ; var _mtm window._mtm window._mtm || ; // Track page view (MatomoAnalytics handles the rest) _paq.push(trackPageView); _mtm.push({mtm.startTime: (new Date().getTime()), event: mtm.Start}); var tmScript document.createElement(script); tmScript.async true; tmScript.src https://analytics.cameronkfox.com/js/container_SRw0bU3i.js; document.head.appendChild(tmScript); var mScript document.createElement(script); mScript.async true; mScript.src https://analytics.cameronkfox.com/matomo.js; document.head.appendChild(mScript); }, 1000); /script> !-- Ultimate Fallback: Noscript pixel tracking (works even if JS is completely disabled) --> noscript> img srchttps://analytics.cameronkfox.com/matomo.php?idsite1&rec1&action_nameMain%20Page&urlhttps%3A%2F%2Fcameronkfox.com&_id_noscript1&rand1234567890&noscript1 styleborder:0; position:absolute; left:-9999px; altMatomo tracking pixel /> /noscript> !-- Unified Header System --> script src/js/header.js defer type377e939800cdf2b3e0a8259d-text/javascript>/script> !-- Section Navigator & Analytics --> script src/analytics/matomo.js defer type377e939800cdf2b3e0a8259d-text/javascript>/script> script src/components/SectionNav.js defer type377e939800cdf2b3e0a8259d-text/javascript>/script> !-- PWA Service Worker --> script type377e939800cdf2b3e0a8259d-text/javascript> if (serviceWorker in navigator) { window.addEventListener(load, () > navigator.serviceWorker.register(/service-worker.js)); } /script>script src/cdn-cgi/scripts/7d0fa10a/cloudflare-static/rocket-loader.min.js data-cf-settings377e939800cdf2b3e0a8259d-|49 defer>/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]