LukasBe's picture
Add 3 files
195fc82 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exploratory Atlas | 3D Knowledge Navigator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/controls/OrbitControls.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.132.2/examples/js/loaders/GLTFLoader.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#container {
position: absolute;
width: 100%;
height: 100%;
}
#ui {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
}
#ui > * {
pointer-events: auto;
}
.panel {
background: rgba(10, 20, 30, 0.85);
backdrop-filter: blur(10px);
border: 1px solid rgba(100, 180, 255, 0.2);
border-radius: 12px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
color: white;
}
.holographic {
position: relative;
overflow: hidden;
}
.holographic::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(100, 200, 255, 0.1),
rgba(100, 200, 255, 0.05),
rgba(100, 200, 255, 0.1)
);
transform: rotate(30deg);
z-index: -1;
}
.glow {
text-shadow: 0 0 10px rgba(100, 200, 255, 0.7);
}
.node-info {
transition: all 0.3s ease;
max-height: 0;
opacity: 0;
overflow: hidden;
}
.node-info.active {
max-height: 500px;
opacity: 1;
}
.connection-line {
stroke-dasharray: 1000;
stroke-dashoffset: 1000;
animation: dash 5s linear forwards;
}
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.8; }
50% { opacity: 1; }
}
</style>
</head>
<body>
<div id="container"></div>
<div id="ui">
<!-- Header -->
<div class="absolute top-4 left-4 right-4 flex justify-between items-start">
<div class="panel p-4 w-1/3">
<h1 class="text-2xl font-bold glow">EXPLORATORY ATLAS</h1>
<p class="text-blue-300">3D Knowledge Navigation System</p>
</div>
<div class="panel p-4 flex space-x-4">
<div>
<label class="block text-blue-300 text-sm mb-1">FROM</label>
<input type="text" id="topicX" value="Artificial Intelligence"
class="bg-transparent border-b border-blue-500 focus:outline-none text-white">
</div>
<div>
<label class="block text-blue-300 text-sm mb-1">TO</label>
<input type="text" id="topicY" value="Climate Change"
class="bg-transparent border-b border-blue-500 focus:outline-none text-white">
</div>
<button id="generateBtn" class="bg-blue-600 hover:bg-blue-700 px-4 py-1 rounded-lg flex items-center">
<i class="fas fa-satellite-dish mr-2"></i> Generate
</button>
</div>
</div>
<!-- Control Panel -->
<div class="panel absolute bottom-4 left-4 p-4 w-80">
<h2 class="text-lg font-semibold mb-3 flex items-center">
<i class="fas fa-cogs mr-2 text-blue-400"></i> Navigation Controls
</h2>
<div class="grid grid-cols-2 gap-3 mb-4">
<button class="bg-blue-800 hover:bg-blue-700 p-2 rounded-lg flex flex-col items-center">
<i class="fas fa-flag mb-1"></i>
<span class="text-xs">Place Marker</span>
</button>
<button class="bg-purple-800 hover:bg-purple-700 p-2 rounded-lg flex flex-col items-center">
<i class="fas fa-digging mb-1"></i>
<span class="text-xs">Excavate</span>
</button>
<button class="bg-green-800 hover:bg-green-700 p-2 rounded-lg flex flex-col items-center">
<i class="fas fa-expand mb-1"></i>
<span class="text-xs">Focus Node</span>
</button>
<button class="bg-yellow-800 hover:bg-yellow-700 p-2 rounded-lg flex flex-col items-center">
<i class="fas fa-route mb-1"></i>
<span class="text-xs">Trace Path</span>
</button>
</div>
<div class="mb-3">
<h3 class="text-sm font-semibold mb-2 flex items-center">
<i class="fas fa-map-marked-alt mr-1 text-blue-400"></i> Legend
</h3>
<div class="grid grid-cols-2 gap-2 text-xs">
<div class="flex items-center">
<div class="w-3 h-3 bg-green-500 rounded-full mr-2"></div>
<span>Core Concepts</span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 bg-red-500 rounded-full mr-2"></div>
<span>Contradictions</span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 bg-blue-500 rounded-full mr-2"></div>
<span>Relationships</span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 bg-gray-400 rounded-full mr-2 pulse"></div>
<span>Uncertainty</span>
</div>
</div>
</div>
</div>
<!-- Node Information Panel -->
<div class="panel absolute top-1/2 right-4 transform -translate-y-1/2 w-80 max-h-[70vh] overflow-y-auto">
<div class="p-4">
<h2 id="nodeTitle" class="text-xl font-bold mb-2">Knowledge Node</h2>
<div id="nodeType" class="text-xs px-2 py-1 bg-blue-900 rounded-full inline-block mb-3">Concept</div>
<div id="nodeContent" class="text-sm">
<p>Select a node in the visualization to view detailed information.</p>
</div>
<div id="nodeConnections" class="mt-4">
<h3 class="text-sm font-semibold mb-2">Connected Concepts</h3>
<div class="space-y-2">
<!-- Connections would be populated here -->
</div>
</div>
<div class="mt-6 pt-4 border-t border-gray-700">
<button class="bg-blue-600 hover:bg-blue-700 px-3 py-1 rounded text-sm mr-2">
<i class="fas fa-bookmark mr-1"></i> Save
</button>
<button class="bg-green-600 hover:bg-green-700 px-3 py-1 rounded text-sm">
<i class="fas fa-share-alt mr-1"></i> Share
</button>
</div>
</div>
</div>
<!-- Exploration Path -->
<div class="panel absolute bottom-4 right-4 w-1/3">
<div class="p-4">
<h2 class="text-lg font-semibold mb-3 flex items-center">
<i class="fas fa-history mr-2 text-blue-400"></i> Exploration Path
</h2>
<div class="flex overflow-x-auto space-x-2 py-2">
<!-- Path items would be added here -->
</div>
</div>
</div>
</div>
<script>
// Three.js Scene Setup
let scene, camera, renderer, controls;
let knowledgeNodes = [];
let connections = [];
let selectedNode = null;
initThreeJS();
createDemoKnowledgeGraph();
animate();
function initThreeJS() {
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x020a13);
scene.fog = new THREE.FogExp2(0x020a13, 0.002);
// Camera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;
camera.position.y = 30;
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.getElementById('container').appendChild(renderer.domElement);
// Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
controls.minDistance = 20;
controls.maxDistance = 200;
// Lighting
const ambientLight = new THREE.AmbientLight(0x404040);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
scene.add(directionalLight);
const hemisphereLight = new THREE.HemisphereLight(0x00aaff, 0xffaa00, 0.3);
scene.add(hemisphereLight);
// Stars
const starsGeometry = new THREE.BufferGeometry();
const starVertices = [];
for (let i = 0; i < 10000; i++) {
const x = (Math.random() - 0.5) * 2000;
const y = (Math.random() - 0.5) * 2000;
const z = (Math.random() - 0.5) * 2000;
starVertices.push(x, y, z);
}
starsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(starVertices, 3));
const starsMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.1 });
const starField = new THREE.Points(starsGeometry, starsMaterial);
scene.add(starField);
// Window resize handler
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
}
function createDemoKnowledgeGraph() {
// Clear existing nodes and connections
knowledgeNodes.forEach(node => scene.remove(node.mesh));
connections.forEach(conn => scene.remove(conn.line));
knowledgeNodes = [];
connections = [];
// Create central node
createNode("AI & Climate", 0, 0, 0, "core", 5);
// Create related nodes
const node1 = createNode("Ethical AI", -20, 5, 15, "core", 4);
const node2 = createNode("Carbon Footprint", 15, 8, -10, "core", 4);
const node3 = createNode("Data Centers", 5, -10, -15, "relationship", 3.5);
const node4 = createNode("AI Solutions", -10, -15, 20, "relationship", 3.5);
const node5 = createNode("Policy Impact", 25, -5, 5, "contradiction", 3);
const node6 = createNode("Future Predictions", -5, 25, -5, "uncertainty", 3);
// Create connections
createConnection(knowledgeNodes[0], node1, "strong");
createConnection(knowledgeNodes[0], node2, "strong");
createConnection(knowledgeNodes[0], node3, "medium");
createConnection(knowledgeNodes[0], node4, "medium");
createConnection(node1, node4, "weak");
createConnection(node2, node3, "strong");
createConnection(node2, node5, "weak");
createConnection(node3, node6, "weak");
createConnection(node4, node6, "medium");
// Add some floating tech elements
addTechElements();
}
function createNode(title, x, y, z, type, size) {
let geometry, material;
const color = getColorForType(type);
if (type === "core") {
geometry = new THREE.IcosahedronGeometry(size, 1);
material = new THREE.MeshPhongMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.2,
shininess: 100,
wireframe: false
});
} else if (type === "relationship") {
geometry = new THREE.TorusGeometry(size * 0.7, size * 0.3, 16, 32);
material = new THREE.MeshPhongMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.1,
shininess: 50,
wireframe: false
});
} else if (type === "contradiction") {
geometry = new THREE.OctahedronGeometry(size, 0);
material = new THREE.MeshPhongMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.3,
shininess: 30,
wireframe: false
});
} else { // uncertainty
geometry = new THREE.SphereGeometry(size, 32, 32);
material = new THREE.MeshPhongMaterial({
color: color,
emissive: color,
emissiveIntensity: 0.05,
transparent: true,
opacity: 0.7,
wireframe: true
});
}
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(x, y, z);
mesh.castShadow = true;
mesh.receiveShadow = true;
// Add pulsing animation for uncertain nodes
if (type === "uncertainty") {
mesh.userData.pulseSpeed = 0.02 + Math.random() * 0.03;
mesh.userData.originalScale = size;
}
// Add hover effect
mesh.userData = { title, type, description: generateDescription(title, type) };
mesh.userData.connections = [];
scene.add(mesh);
// Create label
const label = document.createElement('div');
label.className = `absolute text-white text-xs font-medium bg-black bg-opacity-50 px-2 py-1 rounded pointer-events-none`;
label.textContent = title;
document.getElementById('ui').appendChild(label);
mesh.userData.label = label;
knowledgeNodes.push({ mesh, label, title, type });
return { mesh, label, title, type };
}
function createConnection(node1, node2, strength) {
const start = node1.mesh.position;
const end = node2.mesh.position;
// Create a curve for the connection
const curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(start.x, start.y, start.z),
new THREE.Vector3(
(start.x + end.x) / 2 + (Math.random() - 0.5) * 10,
(start.y + end.y) / 2 + (Math.random() - 0.5) * 10,
(start.z + end.z) / 2 + (Math.random() - 0.5) * 10
),
new THREE.Vector3(end.x, end.y, end.z)
]);
const points = curve.getPoints(50);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
let color, linewidth;
if (strength === "strong") {
color = 0x00ff00;
linewidth = 2;
} else if (strength === "medium") {
color = 0x0088ff;
linewidth = 1.5;
} else {
color = 0x888888;
linewidth = 1;
}
const material = new THREE.LineBasicMaterial({
color: color,
linewidth: linewidth,
transparent: true,
opacity: 0.7
});
const line = new THREE.Line(geometry, material);
scene.add(line);
// Store connection data
const connection = {
node1, node2, strength, line,
description: `${node1.title}${node2.title} (${strength})`
};
connections.push(connection);
node1.mesh.userData.connections.push(connection);
node2.mesh.userData.connections.push(connection);
return connection;
}
function addTechElements() {
// Add some floating tech elements for atmosphere
const techGeometry = new THREE.CylinderGeometry(0.5, 0.5, 0.2, 6);
const techMaterial = new THREE.MeshPhongMaterial({
color: 0x00aaff,
emissive: 0x0044ff,
emissiveIntensity: 0.5,
transparent: true,
opacity: 0.8
});
for (let i = 0; i < 20; i++) {
const tech = new THREE.Mesh(techGeometry, techMaterial);
tech.position.set(
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100,
(Math.random() - 0.5) * 100
);
tech.rotation.x = Math.random() * Math.PI;
tech.rotation.y = Math.random() * Math.PI;
tech.userData = { speed: 0.01 + Math.random() * 0.02 };
scene.add(tech);
}
}
function getColorForType(type) {
switch(type) {
case "core": return 0x00aa00;
case "relationship": return 0x0066ff;
case "contradiction": return 0xff3300;
case "uncertainty": return 0x888888;
default: return 0xffffff;
}
}
function generateDescription(title, type) {
const descriptors = {
core: ["Fundamental concept", "Key principle", "Central idea"],
relationship: ["Connected to", "Influences", "Related to"],
contradiction: ["Contrasts with", "Opposes", "Conflicts with"],
uncertainty: ["Emerging concept", "Debated topic", "Unclear relationship"]
};
const aspects = {
"AI & Climate": "the intersection of artificial intelligence and climate science",
"Ethical AI": "ethical considerations in AI development",
"Carbon Footprint": "environmental impact of technology",
"Data Centers": "energy consumption of computing infrastructure",
"AI Solutions": "how AI can help address climate change",
"Policy Impact": "how policies affect AI and climate initiatives",
"Future Predictions": "speculative outcomes of AI on climate"
};
const descType = descriptors[type][Math.floor(Math.random() * descriptors[type].length)];
const aspect = aspects[title] || "this concept";
return `${descType} about ${aspect}. This ${type} node represents important knowledge in the network.`;
}
function updateNodeInfo(node) {
document.getElementById('nodeTitle').textContent = node.title;
document.getElementById('nodeType').textContent = node.type.charAt(0).toUpperCase() + node.type.slice(1);
document.getElementById('nodeType').className = `text-xs px-2 py-1 rounded-full inline-block mb-3 ${
node.type === 'core' ? 'bg-green-900' :
node.type === 'relationship' ? 'bg-blue-900' :
node.type === 'contradiction' ? 'bg-red-900' : 'bg-gray-700'
}`;
document.getElementById('nodeContent').innerHTML = `
<p class="mb-3">${node.mesh.userData.description}</p>
<div class="bg-gray-800 p-3 rounded-lg text-xs">
<p class="font-semibold mb-1">Key Attributes:</p>
<ul class="list-disc pl-4">
<li>${node.type === 'core' ? 'Well-established knowledge' :
node.type === 'relationship' ? 'Demonstrated connection' :
node.type === 'contradiction' ? 'Opposing evidence exists' : 'Emerging/uncertain evidence'}</li>
<li>Connected to ${node.mesh.userData.connections.length} other nodes</li>
<li>${node.type === 'uncertainty' ? 'Requires further research' : 'Substantial evidence available'}</li>
</ul>
</div>
`;
// Update connections list
const connectionsContainer = document.getElementById('nodeConnections');
connectionsContainer.innerHTML = `
<h3 class="text-sm font-semibold mb-2">Connected Concepts</h3>
<div class="space-y-2">
${node.mesh.userData.connections.map(conn => {
const otherNode = conn.node1 === node ? conn.node2 : conn.node1;
return `
<div class="flex items-center p-2 bg-gray-800 rounded-lg cursor-pointer hover:bg-gray-700"
onclick="focusNode('${otherNode.title}')">
<div class="w-3 h-3 rounded-full mr-2 ${
otherNode.type === 'core' ? 'bg-green-500' :
otherNode.type === 'relationship' ? 'bg-blue-500' :
otherNode.type === 'contradiction' ? 'bg-red-500' : 'bg-gray-400'
}"></div>
<span class="text-sm">${otherNode.title}</span>
<span class="ml-auto text-xs opacity-70">${conn.strength}</span>
</div>
`;
}).join('')}
</div>
`;
}
function focusNode(title) {
const node = knowledgeNodes.find(n => n.title === title);
if (node) {
// Animate camera to focus on this node
const targetPosition = new THREE.Vector3();
node.mesh.getWorldPosition(targetPosition);
// Move slightly back from the node
const direction = new THREE.Vector3().subVectors(camera.position, targetPosition).normalize();
const focusPosition = new THREE.Vector3().copy(targetPosition).add(direction.multiplyScalar(15));
// Add some height
focusPosition.y += 5;
// Animate camera
animateCameraToPosition(focusPosition, targetPosition);
// Highlight the node
highlightNode(node);
}
}
function animateCameraToPosition(newPosition, lookAt) {
const startPosition = camera.position.clone();
const startQuaternion = camera.quaternion.clone();
camera.lookAt(lookAt);
const endQuaternion = camera.quaternion.clone();
camera.quaternion.copy(startQuaternion);
let progress = 0;
const duration = 1000; // ms
const startTime = Date.now();
function updateCamera() {
progress = (Date.now() - startTime) / duration;
if (progress >= 1) {
camera.position.copy(newPosition);
camera.quaternion.copy(endQuaternion);
return;
}
// Ease in-out
progress = progress < 0.5 ?
2 * progress * progress :
1 - Math.pow(-2 * progress + 2, 2) / 2;
camera.position.lerpVectors(startPosition, newPosition, progress);
// Slerp for quaternion rotation
THREE.Quaternion.slerp(startQuaternion, endQuaternion, camera.quaternion, progress);
requestAnimationFrame(updateCamera);
}
updateCamera();
}
function highlightNode(node) {
// Reset all nodes to normal
knowledgeNodes.forEach(n => {
n.mesh.material.emissiveIntensity = n.type === 'uncertainty' ? 0.05 : 0.2;
n.mesh.scale.set(1, 1, 1);
});
// Highlight the selected node
node.mesh.material.emissiveIntensity = 0.8;
node.mesh.scale.set(1.2, 1.2, 1.2);
// Update UI
updateNodeInfo(node);
selectedNode = node;
// Add to exploration path
addToExplorationPath(node);
}
function addToExplorationPath(node) {
const pathContainer = document.querySelector('#ui > .panel.absolute.bottom-4.right-4 .flex');
const pathItem = document.createElement('div');
pathItem.className = `flex-shrink-0 px-3 py-1 rounded-full text-sm flex items-center ${
node.type === 'core' ? 'bg-green-900 text-green-300' :
node.type === 'relationship' ? 'bg-blue-900 text-blue-300' :
node.type === 'contradiction' ? 'bg-red-900 text-red-300' : 'bg-gray-800 text-gray-300'
}`;
pathItem.innerHTML = `
<i class="fas ${
node.type === 'core' ? 'fa-certificate' :
node.type === 'relationship' ? 'fa-link' :
node.type === 'contradiction' ? 'fa-exclamation-triangle' : 'fa-question-circle'
} mr-1"></i> ${node.title}
`;
pathContainer.appendChild(pathItem);
pathContainer.scrollLeft = pathContainer.scrollWidth;
}
// Animation loop
function animate() {
requestAnimationFrame(animate);
// Update controls
controls.update();
// Update node labels
knowledgeNodes.forEach(node => {
const vector = node.mesh.position.clone().project(camera);
const x = (vector.x * 0.5 + 0.5) * window.innerWidth;
const y = (-(vector.y * 0.5) + 0.5) * window.innerHeight;
node.label.style.transform = `translate(-50%, -50%) translate(${x}px,${y}px)`;
node.label.style.zIndex = vector.z > 1 ? -1 : Math.round((1 - vector.z) * 10000);
});
// Animate uncertain nodes
knowledgeNodes.filter(n => n.type === 'uncertainty').forEach(node => {
const scale = node.mesh.userData.originalScale * (1 + Math.sin(Date.now() * node.mesh.userData.pulseSpeed) * 0.1);
node.mesh.scale.set(scale, scale, scale);
});
// Animate floating tech elements
scene.children.filter(obj => obj.userData?.speed).forEach(obj => {
obj.rotation.x += obj.userData.speed * 0.5;
obj.rotation.y += obj.userData.speed;
obj.position.y += Math.sin(Date.now() * 0.001 + obj.position.x) * 0.02;
});
renderer.render(scene, camera);
}
// Raycasting for node selection
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// Calculate mouse position in normalized device coordinates
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Update the raycaster
raycaster.setFromCamera(mouse, camera);
// Calculate objects intersecting the ray
const intersects = raycaster.intersectObjects(
knowledgeNodes.map(n => n.mesh)
);
if (intersects.length > 0) {
const clickedNode = knowledgeNodes.find(n => n.mesh === intersects[0].object);
if (clickedNode) {
highlightNode(clickedNode);
}
}
}
window.addEventListener('click', onMouseClick, false);
// Generate button functionality
document.getElementById('generateBtn').addEventListener('click', function() {
const topicX = document.getElementById('topicX').value || 'Topic X';
const topicY = document.getElementById('topicY').value || 'Topic Y';
// Update UI
document.querySelector('header h1').textContent = `EXPLORATORY ATLAS: ${topicX}${topicY}`;
// In a real app, this would generate a new knowledge graph
// For demo, we'll just recreate the same graph with new titles
createDemoKnowledgeGraph();
// Show notification
const notification = document.createElement('div');
notification.className = 'fixed top-4 right-4 bg-blue-600 text-white px-4 py-2 rounded-lg shadow-lg transform translate-x-0 transition-transform';
notification.innerHTML = `<i class="fas fa-satellite-dish mr-2"></i> Generating knowledge map for ${topicX} and ${topicY}`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.transform = 'translateX(200%)';
setTimeout(() => notification.remove(), 300);
}, 3000);
});
// Expose functions to global scope for UI interactions
window.focusNode = focusNode;
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=LukasBe/3d-knowledge-navigation-system" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>