Files
blackroad-os-web/.trinity/redlight/templates/earth-replica.html
Alexa Amundson 458c2c044b feat: real-time live data integration
- lib/live-data.ts: Shared TypeScript client for blackroad-live-data Worker
- components/live-stats.tsx: LiveStatsBar, RecentRepos, AgentStatusGrid components
- app/page.tsx: Import LiveStatsBar in main page header

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-24 14:29:09 -06:00

618 lines
26 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Earth</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #000;
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
}
#ui {
position: fixed;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 100;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 25px;
background: rgba(255,255,255,0.1);
color: white;
font-size: 13px;
cursor: pointer;
backdrop-filter: blur(10px);
transition: all 0.3s;
border: 1px solid rgba(255,255,255,0.2);
}
.btn:hover { background: rgba(255,255,255,0.2); }
.btn.active { background: rgba(255,255,255,0.3); }
#info {
position: fixed;
top: 30px;
left: 30px;
color: white;
z-index: 100;
}
#info h1 {
font-size: 24px;
font-weight: 300;
letter-spacing: 4px;
text-transform: uppercase;
margin-bottom: 5px;
}
#info p {
font-size: 12px;
opacity: 0.5;
}
#data {
position: fixed;
top: 30px;
right: 30px;
color: white;
text-align: right;
z-index: 100;
font-size: 12px;
}
.data-row {
margin: 8px 0;
opacity: 0.7;
}
.data-value {
font-size: 18px;
font-weight: 300;
opacity: 1;
}
#loading {
position: fixed;
inset: 0;
background: #000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 1000;
transition: opacity 1s;
}
#loading.hidden { opacity: 0; pointer-events: none; }
#loading h2 {
color: white;
font-weight: 300;
font-size: 18px;
letter-spacing: 3px;
margin-bottom: 30px;
}
.loader {
width: 50px;
height: 50px;
border: 2px solid rgba(255,255,255,0.1);
border-top-color: white;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
</style>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
:root{--black:#000;--ink:#0A0A0A;--surface:#111;--border:#1A1A1A;--dim:#666;--sub:#999;--white:#FFF;--pink:#FF1D6C;--amber:#F5A623;--violet:#9C27B0;--blue:#2979FF;--green:#00FF88;--gradient:linear-gradient(135deg,#F5A623 0%,#FF1D6C 38.2%,#9C27B0 61.8%,#2979FF 100%);--xs:8px;--sm:13px;--md:21px;--lg:34px;--xl:55px;--xxl:89px}
*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
body{font-family:'JetBrains Mono',monospace;background:#000;color:#fff;line-height:1.618;overflow-x:hidden;-webkit-font-smoothing:antialiased;padding-top:70px}
h1{font-size:clamp(40px,8vw,100px);font-weight:600;letter-spacing:-.02em;line-height:1.1;background:linear-gradient(135deg,#F5A623 0%,#FF1D6C 38.2%,#9C27B0 61.8%,#2979FF 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:21px}
h2{font-size:clamp(24px,4vw,48px);font-weight:600;letter-spacing:-.02em;color:#fff;margin-bottom:21px}
h3{font-size:clamp(16px,2vw,24px);font-weight:600;color:#fff;margin-bottom:13px}
h4,h5,h6{color:#fff}
p{color:#999;line-height:1.8;margin-bottom:21px}
a{color:#2979FF;text-decoration:none;transition:color .2s}
a:hover{color:#FF1D6C}
strong{color:#fff}
code{font-family:inherit;background:#111;color:#00FF88;padding:2px 6px;border:1px solid #1A1A1A}
pre{background:#0A0A0A;border:1px solid #1A1A1A;border-left:2px solid #FF1D6C;padding:21px 34px;color:#00FF88;overflow-x:auto;margin-bottom:34px}
hr{border:none;height:1px;background:linear-gradient(135deg,#F5A623,#FF1D6C,#9C27B0,#2979FF);opacity:.3;margin:55px 0}
nav,.nav{position:fixed;top:0;left:0;right:0;z-index:1000;display:flex;justify-content:space-between;align-items:center;padding:21px 55px;background:rgba(0,0,0,.85);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-bottom:1px solid #1A1A1A}
nav a,.nav a{color:#999;font-size:13px}
nav a:hover,.nav a:hover{color:#fff}
.logo{font-size:20px;font-weight:700;background:linear-gradient(135deg,#F5A623 0%,#FF1D6C 38.2%,#9C27B0 61.8%,#2979FF 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
header{padding:21px 55px 0;display:flex;justify-content:space-between;align-items:center}
header .logo{font-size:22px;font-weight:700;background:linear-gradient(135deg,#F5A623 0%,#FF1D6C 38.2%,#9C27B0 61.8%,#2979FF 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
header nav{position:static;background:none;backdrop-filter:none;border:none;padding:0;display:flex;gap:34px}
.hero,section.hero{min-height:90vh;display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;padding:89px 55px;position:relative;overflow:hidden}
.hero::before{content:'';position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:500px;height:500px;background:radial-gradient(circle,rgba(255,29,108,.15) 0%,rgba(156,39,176,.1) 40%,transparent 70%);pointer-events:none;animation:orb 8s ease-in-out infinite}
@keyframes orb{0%,100%{transform:translate(-50%,-50%) scale(1);opacity:.6}50%{transform:translate(-50%,-50%) scale(1.3);opacity:1}}
.hero p,.tagline{font-size:18px;max-width:600px;color:#999;margin-bottom:55px;position:relative;z-index:1}
.hero h1{position:relative;z-index:1}
.earth-glow,.hero-glow,.neural-bg{position:absolute;inset:0;background:radial-gradient(ellipse at center,rgba(255,29,108,.07) 0%,transparent 60%);pointer-events:none}
section{padding:89px 55px;position:relative}
section:not(.hero){max-width:1200px;margin-left:auto;margin-right:auto}
section h2{color:#fff;-webkit-text-fill-color:#fff}
.btn,.btn-primary{display:inline-block;padding:13px 34px;font-family:inherit;font-size:14px;font-weight:600;background:#FF1D6C;color:#fff;text-decoration:none;border:none;cursor:pointer;transition:all .2s;position:relative;z-index:1}
.btn:hover,.btn-primary:hover{background:#fff;color:#000;transform:translateY(-2px)}
.features-grid,.ai-features,.pillars{display:flex;flex-wrap:wrap;gap:34px;justify-content:center}
.feature-card,.ai-feature,.pillar{background:rgba(255,255,255,.02);border:1px solid #1A1A1A;padding:55px;transition:all .3s;flex:1;min-width:220px;max-width:360px}
.feature-card:hover,.ai-feature:hover,.pillar:hover{background:rgba(255,255,255,.05);border-color:rgba(255,29,108,.4);transform:translateY(-4px)}
.feature-icon,.ai-feature-icon,.pillar-icon{font-size:28px;margin-bottom:21px;display:block}
.feature-card h3,.ai-feature-title,.pillar-name{color:#fff;margin-bottom:8px}
.feature-card p,.ai-feature-desc{font-size:14px;color:#666}
.badge,.ai-badge{display:inline-block;padding:4px 13px;font-size:11px;letter-spacing:.1em;text-transform:uppercase;border:1px solid #1A1A1A;color:#999;margin-bottom:21px}
footer,.footer{padding:55px;border-top:1px solid #1A1A1A;color:#666;font-size:13px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:21px}
footer a,.footer a{color:#999}
footer a:hover,.footer a:hover{color:#FF1D6C}
@media(max-width:768px){nav,.nav{padding:21px}.hero,section.hero{padding:89px 21px}.features-grid,.ai-features,.pillars{flex-direction:column}.feature-card,.ai-feature{min-width:unset;max-width:unset}footer,.footer{flex-direction:column;text-align:center;padding:34px 21px}}
</style>
</head>
<body>
<div id="loading">
<h2>LOADING EARTH</h2>
<div class="loader"></div>
</div>
<div id="info">
<h1>Earth</h1>
<p>Third planet from the Sun</p>
</div>
<div id="data">
<div class="data-row">
<div>Diameter</div>
<div class="data-value">12,742 km</div>
</div>
<div class="data-row">
<div>Distance from Sun</div>
<div class="data-value">149.6M km</div>
</div>
<div class="data-row">
<div>Rotation Period</div>
<div class="data-value">23h 56m</div>
</div>
<div class="data-row">
<div>Surface Temp</div>
<div class="data-value">15°C avg</div>
</div>
</div>
<div id="ui">
<button class="btn active" id="btnRotate">Auto Rotate</button>
<button class="btn active" id="btnClouds">Clouds</button>
<button class="btn active" id="btnAtmo">Atmosphere</button>
<button class="btn" id="btnNight">Night Side</button>
<button class="btn" id="btnStars">Stars</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
(function() {
// === CONFIGURATION ===
const EARTH_RADIUS = 100;
const CLOUD_HEIGHT = 1.5;
const ATMO_HEIGHT = 12;
// High-quality texture URLs
const TEXTURES = {
// 8K Earth texture from NASA Blue Marble
earth: 'https://unpkg.com/three-globe/example/img/earth-blue-marble.jpg',
// Bump map for terrain elevation
bump: 'https://unpkg.com/three-globe/example/img/earth-topology.png',
// Specular map - water reflects, land doesn't
specular: 'https://unpkg.com/three-globe/example/img/earth-water.png',
// Cloud layer
clouds: 'https://unpkg.com/three-globe/example/img/earth-clouds.png',
// City lights at night
night: 'https://unpkg.com/three-globe/example/img/earth-night.jpg'
};
// === GLOBALS ===
let scene, camera, renderer;
let earth, cloudMesh, atmosphere, nightLights, starField;
let sunLight;
let autoRotate = true;
let showClouds = true;
let showAtmosphere = true;
let showNight = false;
let showStars = false;
let isDragging = false;
let prevMouse = { x: 0, y: 0 };
let spherical = { theta: 0, phi: Math.PI / 2.5, radius: 300 };
// === INITIALIZE ===
function init() {
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
// Camera
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 10000);
updateCamera();
// Renderer with high quality settings
renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true,
powerPreference: "high-performance"
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 1.2;
document.body.appendChild(renderer.domElement);
// Create stars immediately (no texture needed)
createStars();
// Lighting
createLighting();
// Load textures
loadTextures();
// Events
setupEvents();
}
// === STARS ===
function createStars() {
const geometry = new THREE.BufferGeometry();
const count = 15000;
const positions = new Float32Array(count * 3);
const sizes = new Float32Array(count);
for (let i = 0; i < count; i++) {
// Distribute on a sphere far away
const radius = 2000 + Math.random() * 2000;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(Math.random() * 2 - 1);
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = radius * Math.cos(phi);
sizes[i] = 0.5 + Math.random() * 1.5;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1));
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 1.2,
transparent: true,
opacity: 0.9,
sizeAttenuation: true
});
starField = new THREE.Points(geometry, material);
starField.visible = showStars;
scene.add(starField);
}
// === LIGHTING ===
function createLighting() {
// Main sunlight - key light
sunLight = new THREE.DirectionalLight(0xffffff, 1.5);
sunLight.position.set(500, 200, 500);
scene.add(sunLight);
// Subtle fill light from opposite side (simulates light bouncing off moon/space)
const fillLight = new THREE.DirectionalLight(0x4466aa, 0.1);
fillLight.position.set(-300, -100, -300);
scene.add(fillLight);
// Ambient light for subtle overall illumination
const ambient = new THREE.AmbientLight(0x222233, 0.2);
scene.add(ambient);
// Hemisphere light for natural sky/ground variation
const hemi = new THREE.HemisphereLight(0x88aaff, 0x080820, 0.15);
scene.add(hemi);
}
// === TEXTURE LOADING ===
function loadTextures() {
const loader = new THREE.TextureLoader();
const textureData = {};
let loadedCount = 0;
const totalTextures = Object.keys(TEXTURES).length;
Object.entries(TEXTURES).forEach(([name, url]) => {
loader.load(
url,
(texture) => {
// Configure texture for best quality
texture.anisotropy = renderer.capabilities.getMaxAnisotropy();
texture.encoding = THREE.sRGBEncoding;
textureData[name] = texture;
loadedCount++;
if (loadedCount === totalTextures) {
createEarth(textureData);
document.getElementById('loading').classList.add('hidden');
animate();
}
},
undefined,
(error) => {
console.warn(`Failed to load ${name}:`, error);
loadedCount++;
if (loadedCount === totalTextures) {
createEarth(textureData);
document.getElementById('loading').classList.add('hidden');
animate();
}
}
);
});
}
// === CREATE EARTH ===
function createEarth(textures) {
// High-poly sphere for smooth appearance
const geometry = new THREE.SphereGeometry(EARTH_RADIUS, 128, 128);
// === MAIN EARTH SURFACE ===
const earthMaterial = new THREE.MeshPhongMaterial({
map: textures.earth,
bumpMap: textures.bump,
bumpScale: 0.8,
specularMap: textures.specular,
specular: new THREE.Color(0x333333),
shininess: 15
});
earth = new THREE.Mesh(geometry, earthMaterial);
scene.add(earth);
// === NIGHT LIGHTS LAYER ===
const nightMaterial = new THREE.MeshBasicMaterial({
map: textures.night,
blending: THREE.AdditiveBlending,
transparent: true,
opacity: 0
});
nightLights = new THREE.Mesh(geometry.clone(), nightMaterial);
nightLights.scale.setScalar(1.002); // Slightly larger to prevent z-fighting
scene.add(nightLights);
// === CLOUD LAYER ===
const cloudGeometry = new THREE.SphereGeometry(EARTH_RADIUS + CLOUD_HEIGHT, 96, 96);
const cloudMaterial = new THREE.MeshPhongMaterial({
map: textures.clouds,
transparent: true,
opacity: 0.4,
depthWrite: false
});
cloudMesh = new THREE.Mesh(cloudGeometry, cloudMaterial);
scene.add(cloudMesh);
// === ATMOSPHERE ===
createAtmosphere();
}
// === ATMOSPHERE SHADER ===
function createAtmosphere() {
// Outer atmosphere glow
const outerGeometry = new THREE.SphereGeometry(EARTH_RADIUS + ATMO_HEIGHT, 64, 64);
const outerMaterial = new THREE.ShaderMaterial({
vertexShader: `
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
vNormal = normalize(normalMatrix * normal);
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vNormal;
varying vec3 vPosition;
void main() {
// Fresnel effect - glow at edges
float intensity = pow(0.65 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);
// Atmosphere color - blue with slight purple tint
vec3 atmosphereColor = vec3(0.3, 0.6, 1.0);
gl_FragColor = vec4(atmosphereColor, intensity * 0.6);
}
`,
blending: THREE.AdditiveBlending,
side: THREE.BackSide,
transparent: true,
depthWrite: false
});
atmosphere = new THREE.Mesh(outerGeometry, outerMaterial);
scene.add(atmosphere);
// Inner atmosphere rim
const innerGeometry = new THREE.SphereGeometry(EARTH_RADIUS + 0.5, 64, 64);
const innerMaterial = new THREE.ShaderMaterial({
vertexShader: `
varying vec3 vNormal;
void main() {
vNormal = normalize(normalMatrix * normal);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
varying vec3 vNormal;
void main() {
float intensity = pow(0.75 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 3.0);
vec3 rimColor = vec3(0.4, 0.7, 1.0);
gl_FragColor = vec4(rimColor, intensity * 0.25);
}
`,
blending: THREE.AdditiveBlending,
side: THREE.FrontSide,
transparent: true,
depthWrite: false
});
const innerAtmosphere = new THREE.Mesh(innerGeometry, innerMaterial);
scene.add(innerAtmosphere);
}
// === CAMERA ===
function updateCamera() {
camera.position.x = spherical.radius * Math.sin(spherical.phi) * Math.sin(spherical.theta);
camera.position.y = spherical.radius * Math.cos(spherical.phi);
camera.position.z = spherical.radius * Math.sin(spherical.phi) * Math.cos(spherical.theta);
camera.lookAt(0, 0, 0);
}
// === EVENTS ===
function setupEvents() {
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Mouse controls
renderer.domElement.addEventListener('mousedown', (e) => {
isDragging = true;
prevMouse.x = e.clientX;
prevMouse.y = e.clientY;
});
renderer.domElement.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaX = e.clientX - prevMouse.x;
const deltaY = e.clientY - prevMouse.y;
spherical.theta -= deltaX * 0.005;
spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi + deltaY * 0.005));
updateCamera();
prevMouse.x = e.clientX;
prevMouse.y = e.clientY;
});
renderer.domElement.addEventListener('mouseup', () => isDragging = false);
renderer.domElement.addEventListener('mouseleave', () => isDragging = false);
renderer.domElement.addEventListener('wheel', (e) => {
spherical.radius = Math.max(150, Math.min(600, spherical.radius + e.deltaY * 0.3));
updateCamera();
});
// Touch controls
renderer.domElement.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
isDragging = true;
prevMouse.x = e.touches[0].clientX;
prevMouse.y = e.touches[0].clientY;
}
}, { passive: true });
renderer.domElement.addEventListener('touchmove', (e) => {
if (!isDragging || e.touches.length !== 1) return;
const deltaX = e.touches[0].clientX - prevMouse.x;
const deltaY = e.touches[0].clientY - prevMouse.y;
spherical.theta -= deltaX * 0.005;
spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi + deltaY * 0.005));
updateCamera();
prevMouse.x = e.touches[0].clientX;
prevMouse.y = e.touches[0].clientY;
}, { passive: true });
renderer.domElement.addEventListener('touchend', () => isDragging = false);
// UI Buttons
document.getElementById('btnRotate').addEventListener('click', function() {
autoRotate = !autoRotate;
this.classList.toggle('active', autoRotate);
});
document.getElementById('btnClouds').addEventListener('click', function() {
showClouds = !showClouds;
if (cloudMesh) cloudMesh.visible = showClouds;
this.classList.toggle('active', showClouds);
});
document.getElementById('btnAtmo').addEventListener('click', function() {
showAtmosphere = !showAtmosphere;
if (atmosphere) atmosphere.visible = showAtmosphere;
this.classList.toggle('active', showAtmosphere);
});
document.getElementById('btnNight').addEventListener('click', function() {
showNight = !showNight;
if (nightLights) {
nightLights.material.opacity = showNight ? 0.9 : 0;
}
// Dim sunlight when showing night side
if (sunLight) {
sunLight.intensity = showNight ? 0.3 : 1.5;
}
this.classList.toggle('active', showNight);
});
document.getElementById('btnStars').addEventListener('click', function() {
showStars = !showStars;
if (starField) starField.visible = showStars;
this.classList.toggle('active', showStars);
});
}
// === ANIMATION ===
function animate() {
requestAnimationFrame(animate);
// Rotate Earth (real rotation: 360° in 24 hours, but sped up for visualization)
if (earth) {
earth.rotation.y += 0.0003;
}
// Night lights rotate with Earth
if (nightLights) {
nightLights.rotation.y = earth.rotation.y;
}
// Clouds rotate slightly faster than Earth (due to wind)
if (cloudMesh) {
cloudMesh.rotation.y += 0.00035;
}
// Auto-rotate camera
if (autoRotate && !isDragging) {
spherical.theta += 0.0008;
updateCamera();
}
renderer.render(scene, camera);
}
// === START ===
init();
})();
</script>
</body>
</html>