3D Model & UI
<!-- Complete 3D Model Embed with Controls -->
<div class="a3e-model-embed-container">
<!-- Controls Section -->
<div class="a3e-controls-section-wrapper">
<div class="a3e-controls-panel">
<div class="a3e-loading-indicator" id="a3e-loading">
<div class="a3e-spinner"></div>
<p>Loading model...</p>
</div>
<div class="a3e-controls-headers" style="display: none;">
<div class="a3e-section-header">Individual Layers:</div>
<div class="a3e-section-header">Global:</div>
</div>
<div class="a3e-controls-separator" style="display: none;"></div>
<div class="a3e-controls-row" style="display: none;">
<div class="a3e-collections-list" id="a3e-collections-list"></div>
<div class="a3e-vertical-separator"></div>
<div class="a3e-global-controls">
<button id="a3e-show-all" class="a3e-btn">All On/Off</button>
</div>
</div>
</div>
</div>
<!-- 3D Model Container -->
<div class="a3e-model-container">
<iframe id="a3e-sketchfab-viewer" title="Dutch 3D Model" frameborder="0"
allow="autoplay; fullscreen; vr"
mozallowfullscreen="true" webkitallowfullscreen="true">
</iframe>
<div class="a3e-model-loading-overlay" id="a3e-loadingOverlay">
<div class="a3e-loading-indicator">
<div class="a3e-spinner"></div>
<p>Loading 3D Model...</p>
</div>
</div>
</div>
</div>
<style>
/* CSS Reset and Isolation for 3D Model Embed */
.a3e-model-embed-container,
.a3e-model-embed-container *,
.a3e-model-embed-container *::before,
.a3e-model-embed-container *::after {
box-sizing: border-box !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
outline: none !important;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
line-height: 1 !important;
}
.a3e-model-embed-container {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
background: #0a0a0a !important;
color: #e0e0e0 !important;
padding: 20px !important;
border-radius: 8px !important;
display: block !important;
position: relative !important;
}
.a3e-controls-section-wrapper {
width: 100% !important;
display: flex !important;
justify-content: center !important;
padding: 20px 0 !important;
}
.a3e-controls-panel {
border: 2px solid #ffffff !important;
border-radius: 8px !important;
background: linear-gradient(135deg, #1a1a1a 0%, #333 100%) !important;
backdrop-filter: blur(10px) !important;
-webkit-backdrop-filter: blur(10px) !important;
padding: 25px !important;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important;
min-width: 600px !important;
max-width: 900px !important;
display: block !important;
}
.a3e-loading-indicator {
display: flex !important;
flex-direction: column !important;
align-items: center !important;
gap: 15px !important;
padding: 20px !important;
}
.a3e-spinner {
width: 40px !important;
height: 40px !important;
border: 4px solid #333 !important;
border-top: 4px solid #e0e0e0 !important;
border-radius: 50% !important;
animation: a3e-spin 1s linear infinite !important;
display: block !important;
}
@keyframes a3e-spin {
0% { transform: rotate(0deg) !important; }
100% { transform: rotate(360deg) !important; }
}
.a3e-loading-indicator p {
color: #e0e0e0 !important;
font-size: 1rem !important;
display: block !important;
}
.a3e-controls-headers {
display: flex !important;
justify-content: space-between !important;
margin-bottom: 15px !important;
padding: 0 10px !important;
}
.a3e-section-header {
font-weight: bold !important;
font-size: 1.1rem !important;
color: #ffffff !important;
display: block !important;
}
.a3e-controls-separator {
height: 1px !important;
background: linear-gradient(90deg, transparent, #666, transparent) !important;
margin: 15px 0 !important;
display: block !important;
}
.a3e-controls-row {
display: flex !important;
gap: 30px !important;
align-items: flex-start !important;
}
.a3e-collections-list {
display: grid !important;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)) !important;
gap: 10px !important;
flex: 1 !important;
min-width: 0 !important;
}
.a3e-vertical-separator {
width: 1px !important;
background: linear-gradient(180deg, transparent, #666, transparent) !important;
min-height: 100px !important;
flex-shrink: 0 !important;
display: block !important;
}
.a3e-global-controls {
display: flex !important;
flex-direction: column !important;
gap: 10px !important;
min-width: 120px !important;
}
.a3e-collection-btn, .a3e-btn {
padding: 8px 16px !important;
border: 1px solid #555 !important;
border-radius: 4px !important;
background: #333 !important;
color: #e0e0e0 !important;
font-size: 0.9rem !important;
cursor: pointer !important;
transition: all 0.3s ease !important;
white-space: nowrap !important;
text-align: center !important;
display: block !important;
}
.a3e-collection-btn:hover, .a3e-btn:hover {
background: #444 !important;
border-color: #666 !important;
transform: translateY(-1px) !important;
}
.a3e-show-btn {
background: #4a5568 !important;
border-color: #6b7280 !important;
}
.a3e-hide-btn {
background: #7c3aed !important;
border-color: #8b5cf6 !important;
}
.a3e-model-container {
background: #111 !important;
border: 2px solid #ffffff !important;
border-radius: 8px !important;
overflow: hidden !important;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3) !important;
position: relative !important;
width: 100% !important;
height: 0 !important;
padding-bottom: 56.25% !important;
margin: 20px 0 !important;
max-width: 100% !important;
display: block !important;
}
#a3e-sketchfab-viewer {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
border: none !important;
opacity: 0 !important;
transition: opacity 0.8s ease-in-out !important;
pointer-events: none !important;
display: block !important;
}
#a3e-sketchfab-viewer.a3e-loaded {
opacity: 1 !important;
}
.a3e-model-loading-overlay {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background: rgba(0, 0, 0, 0.9) !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
z-index: 10 !important;
opacity: 1 !important;
transition: opacity 0.5s ease-in-out !important;
}
.a3e-model-loading-overlay.a3e-hidden {
opacity: 0 !important;
pointer-events: none !important;
}
/* Responsive Design with higher specificity */
@media screen and (max-width: 768px) {
.a3e-controls-panel {
min-width: auto !important;
max-width: 100% !important;
padding: 12px !important;
margin: 0 !important;
width: 100% !important;
}
/* Stack the headers vertically on mobile - HIDDEN */
.a3e-controls-headers {
display: none !important;
}
.a3e-section-header {
display: none !important;
}
/* Remove separator to save space on mobile */
.a3e-controls-separator {
display: none !important;
}
/* Stack controls vertically with reduced gap */
.a3e-controls-row {
flex-direction: column !important;
gap: 10px !important;
align-items: center !important;
}
/* Remove vertical separator */
.a3e-vertical-separator {
display: none !important;
}
/* Compact mobile grid layout */
.a3e-collections-list {
display: grid !important;
grid-template-columns: repeat(3, 1fr) !important;
gap: 6px !important;
width: 100% !important;
max-width: 350px !important;
justify-items: center !important;
}
/* Compact collection items */
.a3e-collection-btn {
width: 100% !important;
padding: 6px 4px !important;
font-size: 9px !important;
line-height: 1.1 !important;
min-height: 28px !important;
background: rgba(255, 255, 255, 0.08) !important;
border: 1px solid rgba(255, 255, 255, 0.15) !important;
border-radius: 3px !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
text-align: center !important;
font-weight: 500 !important;
}
/* Global controls styling for mobile */
.a3e-global-controls {
width: 100% !important;
max-width: 180px !important;
display: flex !important;
justify-content: center !important;
}
.a3e-btn {
width: 100% !important;
padding: 10px !important;
font-size: 13px !important;
border-radius: 5px !important;
}
/* Make 3D model container taller on mobile while keeping model size */
.a3e-model-container {
padding-bottom: 112.5% !important; /* 100% taller than standard 56.25% */
}
/* Make the iframe fill the entire taller container on mobile */
#a3e-sketchfab-viewer {
position: absolute !important;
top: 0 !important;
left: 0 !important;
transform: none !important;
width: 100% !important;
height: 100% !important; /* Fill the entire container */
}
/* Also adjust the loading overlay to fill the entire container */
.a3e-model-loading-overlay {
position: absolute !important;
top: 0 !important;
left: 0 !important;
transform: none !important;
width: 100% !important;
height: 100% !important;
}
}
/* Extra small mobile devices */
@media screen and (max-width: 480px) {
.a3e-controls-panel {
padding: 10px !important;
}
.a3e-section-header {
font-size: 11px !important;
}
/* Compact layout for very small screens */
.a3e-collections-list {
grid-template-columns: repeat(2, 1fr) !important;
max-width: 280px !important;
gap: 5px !important;
}
.a3e-collection-btn {
padding: 5px 3px !important;
min-height: 26px !important;
font-size: 8px !important;
line-height: 1 !important;
}
.a3e-btn {
padding: 10px !important;
font-size: 12px !important;
}
.a3e-global-controls {
max-width: 150px !important;
}
}
</style>
<script src="https://static.sketchfab.com/api/sketchfab-viewer-1.12.1.js"></script>
<script>
// Complete JavaScript for 3D Model functionality with namespace
(function() {
'use strict';
class A3ESketchfabModelController {
constructor() {
this.api = null;
this.nodeMap = {};
this.visibility = {};
this.isInitialized = false;
this.iframe = null;
this.loadingOverlay = null;
this.isModelReady = false;
this.isInteractionEnabled = false;
this.modelUID = 'd1d5477e94744311a1c01e512625d81b';
this.targets = {
"Jumps_37": "Jumps", "Track_79": "Track", "Finish_81": "Finish",
"Start_83": "Start", "Check-In_87": "Check-In", "Entrance_91": "Entrance",
"Road_93": "Road", "50cc_98": "50cc", "Restrooms_113": "Restrooms",
"Entrance Fee Booth_117": "Entrance Fee Booth"
};
this.init();
}
init() {
this.iframe = document.getElementById('a3e-sketchfab-viewer');
this.loadingOverlay = document.getElementById('a3e-loadingOverlay');
if (!this.iframe) return;
const client = new window.Sketchfab('1.12.1', this.iframe);
client.init(this.modelUID, {
success: (api) => { this.api = api; this.setupAPI(); },
error: (error) => { console.error('Sketchfab API failed', error); }
});
}
setupAPI() {
this.api.start(() => {
this.api.addEventListener('viewerready', () => this.onViewerReady());
});
}
onViewerReady() {
if (this.isInitialized) return;
this.isInitialized = true;
this.api.getNodeMap((err, nodes) => {
if (err) return;
for (const id in nodes) {
const node = nodes[id];
if (this.targets[node.name]) {
this.nodeMap[node.name] = id;
this.visibility[id] = false;
}
}
this.createUI();
setTimeout(() => this.hideAllCollectionsOnLoad(), 1500);
});
}
hideAllCollectionsOnLoad() {
const hidePromises = Object.keys(this.nodeMap).map(name => {
return new Promise(resolve => {
this.api.hide(this.nodeMap[name], () => resolve());
});
});
Promise.all(hidePromises).then(() => this.revealModel());
}
revealModel() {
this.isModelReady = true;
if (this.iframe) {
this.iframe.classList.add('a3e-loaded');
this.setupInteractionHandler();
}
setTimeout(() => this.hideLoadingOverlay(), 800);
}
setupInteractionHandler() {
if (this.iframe && !this.isInteractionEnabled) {
const modelContainer = this.iframe.parentElement;
if (modelContainer) {
modelContainer.addEventListener('click', () => this.enableInteraction());
modelContainer.style.cursor = 'pointer';
modelContainer.title = 'Click to enable 3D model interaction';
}
}
}
enableInteraction() {
if (!this.isInteractionEnabled && this.iframe) {
this.iframe.style.pointerEvents = 'auto';
this.isInteractionEnabled = true;
const modelContainer = this.iframe.parentElement;
if (modelContainer) {
modelContainer.style.cursor = 'default';
modelContainer.title = '';
}
}
}
hideLoadingOverlay() {
if (this.loadingOverlay) {
this.loadingOverlay.classList.add('a3e-hidden');
setTimeout(() => {
if (this.loadingOverlay && this.loadingOverlay.parentNode) {
this.loadingOverlay.style.display = 'none';
}
}, 500);
}
}
createUI() {
const loadingDiv = document.getElementById('a3e-loading');
const headersDiv = document.querySelector('.a3e-controls-headers');
const separatorDiv = document.querySelector('.a3e-controls-separator');
const rowDiv = document.querySelector('.a3e-controls-row');
const collectionsList = document.getElementById('a3e-collections-list');
const showAllBtn = document.getElementById('a3e-show-all');
if (loadingDiv) loadingDiv.style.display = 'none';
if (headersDiv) headersDiv.style.display = 'flex';
if (separatorDiv) separatorDiv.style.display = 'block';
if (rowDiv) rowDiv.style.display = 'flex';
if (collectionsList) {
collectionsList.innerHTML = '';
Object.entries(this.targets).forEach(([nodeName, label]) => {
const button = document.createElement('button');
button.textContent = 'Show';
button.className = 'a3e-collection-btn a3e-show-btn';
button.onclick = () => this.toggleVisibility(nodeName, button);
collectionsList.appendChild(button);
const labelDiv = document.createElement('div');
labelDiv.textContent = label;
labelDiv.style.fontSize = '0.8rem';
labelDiv.style.textAlign = 'center';
labelDiv.style.marginTop = '5px';
collectionsList.appendChild(labelDiv);
});
}
if (showAllBtn) {
showAllBtn.onclick = () => this.toggleAllVisibility();
}
}
toggleVisibility(name, button) {
const id = this.nodeMap[name];
if (!id) return;
const currentlyVisible = this.visibility[id];
if (currentlyVisible) {
this.api.hide(id, () => {
this.visibility[id] = false;
button.textContent = 'Show';
button.className = 'a3e-collection-btn a3e-show-btn';
});
} else {
this.api.show(id, () => {
this.visibility[id] = true;
button.textContent = 'Hide';
button.className = 'a3e-collection-btn a3e-hide-btn';
});
}
}
toggleAllVisibility() {
const hasVisible = Object.values(this.visibility).some(v => v);
Object.keys(this.nodeMap).forEach(name => {
const id = this.nodeMap[name];
if (hasVisible) {
this.api.hide(id, () => { this.visibility[id] = false; });
} else {
this.api.show(id, () => { this.visibility[id] = true; });
}
});
const buttons = document.querySelectorAll('.a3e-collection-btn');
buttons.forEach(btn => {
btn.textContent = hasVisible ? 'Show' : 'Hide';
btn.className = hasVisible ? 'a3e-collection-btn a3e-show-btn' : 'a3e-collection-btn a3e-hide-btn';
});
}
}
// Initialize when page loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
if (typeof window.Sketchfab !== 'undefined') {
new A3ESketchfabModelController();
}
});
} else {
if (typeof window.Sketchfab !== 'undefined') {
new A3ESketchfabModelController();
}
}
})();
</script>
AB Slider
<!-- Complete AB Slider Embed with Functionality -->
<div class="a3e-ab-slider-section">
<div class="a3e-ab-slider-title">
<h2>AB Slider</h2>
</div>
<div class="a3e-ab-slider-container">
<div class="a3e-ab-slider-wrapper">
<div class="a3e-ab-image-container">
<img src="https://a3edrone.com/ab-sliders/DutchMX/DutchIII-2.png" alt="Dutch Left Image" class="a3e-ab-image a3e-ab-image-before">
<img src="https://a3edrone.com/ab-sliders/DutchMX/DutchIII-1.png" alt="Dutch Right Image" class="a3e-ab-image a3e-ab-image-after">
<img src="https://a3edrone.com/ab-sliders/DutchMX/DutchIII-2D.png" alt="Dutch Left Date" class="a3e-ab-image a3e-ab-date-before">
<img src="https://a3edrone.com/ab-sliders/DutchMX/DutchIII-1D.png" alt="Dutch Right Date" class="a3e-ab-image a3e-ab-date-after">
</div>
<div class="a3e-ab-slider-divider" id="a3e-ab-slider-divider">
<div class="a3e-ab-slider-handle"></div>
</div>
</div>
</div>
</div>
<style>
/* CSS Reset and Isolation for AB Slider Embed */
.a3e-ab-slider-section,
.a3e-ab-slider-section *,
.a3e-ab-slider-section *::before,
.a3e-ab-slider-section *::after {
box-sizing: border-box !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
outline: none !important;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
line-height: 1 !important;
}
.a3e-ab-slider-section {
width: 100% !important;
padding: 40px 20px !important;
background: #0a0a0a !important;
color: #e0e0e0 !important;
display: block !important;
position: relative !important;
}
.a3e-ab-slider-title {
text-align: center !important;
margin-bottom: 30px !important;
display: block !important;
}
.a3e-ab-slider-title h2 {
font-size: 2rem !important;
color: #e0e0e0 !important;
font-weight: bold !important;
display: block !important;
}
.a3e-ab-slider-container {
position: relative !important;
width: 100% !important;
max-width: 1200px !important;
margin: 0 auto !important;
border: 2px solid #ffffff !important;
border-radius: 8px !important;
overflow: hidden !important;
background: radial-gradient(ellipse at center, #383838 0%, #000000 100%) !important;
z-index: 1 !important;
display: block !important;
}
.a3e-ab-slider-wrapper {
position: relative !important;
width: 100% !important;
height: 0 !important;
padding-bottom: 56.25% !important;
overflow: hidden !important;
display: block !important;
}
.a3e-ab-image-container {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
display: block !important;
}
.a3e-ab-image {
position: absolute !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
object-fit: cover !important;
object-position: center !important;
transition: clip-path 0.1s ease-out !important;
user-select: none !important;
pointer-events: none !important;
display: block !important;
border: none !important;
outline: none !important;
}
.a3e-ab-image-before {
clip-path: inset(0 50% 0 0) !important;
z-index: 2 !important;
}
.a3e-ab-image-after {
z-index: 1 !important;
}
.a3e-ab-date-before,
.a3e-ab-date-after {
z-index: 3 !important;
opacity: 0.9 !important;
}
.a3e-ab-date-before {
clip-path: inset(0 95% 0 0) !important;
}
.a3e-ab-date-after {
clip-path: inset(0 0 0 95%) !important;
}
.a3e-ab-slider-divider {
position: absolute !important;
top: 0 !important;
left: 50% !important;
transform: translateX(-50%) !important;
width: 4px !important;
height: 100% !important;
background: linear-gradient(180deg, #ffffff 0%, #e0e0e0 50%, #ffffff 100%) !important;
cursor: col-resize !important;
z-index: 4 !important;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.3) !important;
display: block !important;
}
.a3e-ab-slider-handle {
position: absolute !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
width: 32px !important;
height: 32px !important;
background: #ffffff !important;
border: 3px solid #333 !important;
border-radius: 50% !important;
box-shadow: 0 0 15px rgba(0, 0, 0, 0.5) !important;
cursor: col-resize !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
.a3e-ab-slider-handle::before {
content: '' !important;
width: 8px !important;
height: 8px !important;
background: #333 !important;
border-radius: 50% !important;
display: block !important;
}
.a3e-ab-slider-handle:hover {
transform: translate(-50%, -50%) scale(1.1) !important;
box-shadow: 0 0 20px rgba(255, 255, 255, 0.4) !important;
}
/* Responsive Design with higher specificity */
@media screen and (max-width: 768px) {
.a3e-ab-slider-title h2 {
font-size: 1.5rem !important;
}
.a3e-ab-slider-handle {
width: 28px !important;
height: 28px !important;
}
.a3e-ab-slider-divider {
width: 3px !important;
}
/* Make AB slider more compact with 16:9 aspect ratio on mobile */
.a3e-ab-slider-wrapper {
padding-bottom: 56.25% !important; /* 16:9 aspect ratio */
}
.a3e-ab-image-container {
min-height: auto !important; /* Remove the large min-height on mobile */
}
}
@media screen and (max-width: 480px) {
.a3e-ab-slider-handle {
width: 24px !important;
height: 24px !important;
}
.a3e-ab-slider-divider {
width: 6px !important;
}
}
</style>
<script>
// Complete JavaScript for AB Slider functionality with namespace
(function() {
'use strict';
class A3EABSlider {
constructor() {
this.container = null;
this.divider = null;
this.beforeImage = null;
this.afterImage = null;
this.beforeDate = null;
this.afterDate = null;
this.isDragging = false;
this.currentPosition = 50;
this.init();
}
init() {
setTimeout(() => this.setupElements(), 100);
}
setupElements() {
this.container = document.querySelector('.a3e-ab-slider-wrapper');
this.divider = document.getElementById('a3e-ab-slider-divider');
this.beforeImage = document.querySelector('.a3e-ab-image-before');
this.afterImage = document.querySelector('.a3e-ab-image-after');
this.beforeDate = document.querySelector('.a3e-ab-date-before');
this.afterDate = document.querySelector('.a3e-ab-date-after');
if (!this.container || !this.divider) {
setTimeout(() => this.setupElements(), 500);
return;
}
this.setupEventListeners();
this.updateSlider(50);
}
setupEventListeners() {
this.divider.addEventListener('mousedown', (e) => this.startDrag(e));
this.divider.addEventListener('touchstart', (e) => this.startDrag(e));
document.addEventListener('mousemove', (e) => this.drag(e));
document.addEventListener('touchmove', (e) => this.drag(e));
document.addEventListener('mouseup', () => this.stopDrag());
document.addEventListener('touchend', () => this.stopDrag());
this.container.addEventListener('click', (e) => this.handleClick(e));
}
startDrag(e) {
this.isDragging = true;
e.preventDefault();
}
drag(e) {
if (!this.isDragging) return;
e.preventDefault();
const rect = this.container.getBoundingClientRect();
const clientX = e.type === 'touchmove' ? e.touches[0].clientX : e.clientX;
const x = clientX - rect.left;
const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));
this.updateSlider(percentage);
}
stopDrag() {
this.isDragging = false;
}
handleClick(e) {
if (e.target === this.divider || e.target.parentElement === this.divider) return;
const rect = this.container.getBoundingClientRect();
const x = e.clientX - rect.left;
const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));
this.updateSlider(percentage);
}
updateSlider(percentage) {
this.currentPosition = percentage;
this.divider.style.left = percentage + '%';
const rightInset = Math.max(0, 100 - percentage);
this.beforeImage.style.clipPath = `inset(0 ${rightInset}% 0 0)`;
const dateRightInset = Math.max(5, 100 - percentage);
const dateLeftInset = Math.max(5, percentage);
this.beforeDate.style.clipPath = `inset(0 ${dateRightInset}% 0 0)`;
this.afterDate.style.clipPath = `inset(0 0 0 ${dateLeftInset}%)`;
}
}
// Initialize AB Slider when page loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => new A3EABSlider());
} else {
new A3EABSlider();
}
})();
</script>