Files
singular-particular-space/Images/exopraxist.html
JL Kruger e57d2b0a72 Add Images section — hub + 4 collection galleries, wire nav star
- Images/images.html: hub page linking all 4 collections
- Images/wayback.html, nomad-soul.html, myster-wizzard.html, exopraxist.html: collection galleries
- 429 thumbnails (360px) committed across 4 collections
- Gallery renders from baked filename arrays (no on-load fetch)
- Lightbox lazy-fetches FileBrowser for full-res on click, falls back to thumbnail
- .gitignore: allow Images/*/thumbnails/, anchor /GEMINI.md to root only, add .venv/
- index.html: wire Images star node to Images/images.html

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 20:06:19 +02:00

365 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Exopraxist (2026present) | Singular Particular Space</title>
<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=Sixtyfour+Convergence&family=Lora:ital,wght@0,400..700;1,400..700&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
:root {
--bg-void: #04060b;
--bg-deep: #050810;
--bg-warm: #0d1320;
--phosphor: #00ff41;
--text-warm: #e8d5b8;
--text-muted: #6a7a8a;
--accent: var(--phosphor);
--accent-glow: rgba(0, 255, 65, 0.3);
--transition: 100ms ease;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
border-radius: 0 !important;
}
body {
background-color: var(--bg-void);
color: var(--text-warm);
font-family: 'Lora', serif;
line-height: 1.6;
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
padding: 2rem;
border-bottom: 2px solid var(--accent);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--bg-deep);
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 4px 15px var(--accent-glow);
}
.header-title-wrap {
text-align: left;
}
h1 {
font-family: 'Sixtyfour Convergence', system-ui;
font-size: 3rem;
color: var(--accent);
line-height: 1.2;
text-transform: uppercase;
text-shadow: 0 0 15px var(--accent-glow);
}
.subtitle {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.15em;
}
.nav-back {
font-family: 'Share Tech Mono', monospace;
color: var(--text-muted);
text-decoration: none;
text-transform: uppercase;
border: 1px solid var(--text-muted);
padding: 0.5rem 1.5rem;
transition: var(--transition);
}
.nav-back:hover {
color: var(--accent);
border-color: var(--accent);
background: var(--accent-glow);
box-shadow: 0 0 10px var(--accent-glow);
}
main {
flex: 1;
padding: 2rem;
}
#gallery {
column-count: 5;
column-gap: 1.5rem;
width: 100%;
}
@media (max-width: 1200px) {
#gallery { column-count: 4; }
}
@media (max-width: 900px) {
#gallery { column-count: 3; }
}
@media (max-width: 600px) {
#gallery { column-count: 2; }
h1 { font-size: 1.8rem; }
}
.thumb-link {
display: inline-block;
width: 100%;
margin-bottom: 1.5rem;
break-inside: avoid;
border: 1px solid var(--bg-warm);
overflow: hidden;
transition: var(--transition);
background: var(--bg-deep);
cursor: pointer;
position: relative;
}
.thumb-link:hover {
border-color: var(--accent);
box-shadow: 0 0 15px var(--accent-glow);
z-index: 10;
}
.thumb-link img {
width: 100%;
height: auto;
display: block;
transition: var(--transition);
}
/* Lightbox */
#lightbox {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(4, 6, 11, 0.98);
display: none;
justify-content: center;
align-items: center;
z-index: 1000;
padding: 2rem;
}
.lightbox-content {
position: relative;
max-width: 100%;
max-height: 90vh;
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
}
#lightbox-img {
max-width: 100%;
max-height: 80vh;
object-fit: contain;
border: 2px solid var(--accent);
box-shadow: 0 0 30px var(--accent-glow);
}
.lightbox-controls {
margin-top: 1.5rem;
display: flex;
gap: 1.5rem;
align-items: center;
width: 100%;
justify-content: center;
}
.lightbox-btn {
background: var(--bg-deep);
border: 1px solid var(--text-muted);
color: var(--text-warm);
padding: 0.75rem 1.5rem;
cursor: pointer;
font-family: 'Share Tech Mono', monospace;
transition: var(--transition);
text-transform: uppercase;
}
.lightbox-btn:hover {
border-color: var(--accent);
color: var(--accent);
background: var(--accent-glow);
box-shadow: 0 0 10px var(--accent-glow);
}
.close-btn {
position: absolute;
top: -4rem;
right: 0;
}
#lightbox-filename {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
margin-top: 0.75rem;
letter-spacing: 0.05em;
}
.loading-indicator {
font-family: 'Share Tech Mono', monospace;
color: var(--accent);
font-size: 0.8rem;
margin-top: 0.5rem;
display: none;
}
</style>
</head>
<body>
<header>
<div class="header-title-wrap">
<span class="subtitle">2026present Collection</span>
<h1>Exopraxist</h1>
</div>
<a href="images.html" class="nav-back">← Back to Hub</a>
</header>
<main>
<div id="gallery"></div>
</main>
<div id="lightbox">
<div class="lightbox-content">
<button class="lightbox-btn close-btn" id="close-btn">CLOSE [ESC]</button>
<img id="lightbox-img" src="" alt="">
<div id="lightbox-filename"></div>
<div id="loading-indicator" class="loading-indicator">UPGRADING TO FULL RESOLUTION...</div>
<div class="lightbox-controls">
<button class="lightbox-btn" id="prev-btn">PREV</button>
<a href="#" id="dl-btn" class="lightbox-btn" target="_blank">DOWNLOAD</a>
<button class="lightbox-btn" id="next-btn">NEXT</button>
</div>
</div>
</div>
<script>
const COLLECTION_TOKEN = '97AGC2WQ';
const THUMBNAILS = ["jl_bg_intermediate3.jpg","jl_wallpaper_desktop_F.jpg"];
let fbIndex = null;
let currentIndex = -1;
const galleryEl = document.getElementById('gallery');
const lightboxEl = document.getElementById('lightbox');
const lightboxImg = document.getElementById('lightbox-img');
const lightboxFilename = document.getElementById('lightbox-filename');
const dlBtn = document.getElementById('dl-btn');
const loadingIndicator = document.getElementById('loading-indicator');
function renderGallery() {
THUMBNAILS.forEach((filename, index) => {
const thumbSrc = `Exopraxist/thumbnails/${filename}`;
const link = document.createElement('div');
link.className = 'thumb-link';
link.innerHTML = `<img src="${thumbSrc}" alt="${filename}" loading="lazy">`;
link.onclick = () => openLightbox(index);
galleryEl.appendChild(link);
});
}
async function getFBIndex() {
if (fbIndex) return fbIndex;
try {
const res = await fetch(`https://files.exopraxist.org/api/public/share/${COLLECTION_TOKEN}`);
const data = await res.json();
fbIndex = {};
for (const item of data.items) {
const stem = item.name.replace(/\.[^.]+$/, '');
fbIndex[stem] = item.name;
}
} catch (e) {
console.error('FileBrowser fetch failed:', e);
fbIndex = {};
}
return fbIndex;
}
async function openLightbox(index) {
currentIndex = index;
const thumbFilename = THUMBNAILS[currentIndex];
const thumbUrl = `Exopraxist/thumbnails/${thumbFilename}`;
lightboxImg.src = thumbUrl;
lightboxFilename.textContent = thumbFilename;
dlBtn.href = thumbUrl;
loadingIndicator.style.display = 'block';
lightboxEl.style.display = 'flex';
document.body.style.overflow = 'hidden';
const stem = thumbFilename.replace('.jpg', '');
const idx = await getFBIndex();
const originalName = idx[stem];
if (originalName) {
const fullResUrl = `https://files.exopraxist.org/api/public/dl/${COLLECTION_TOKEN}?files=/${encodeURIComponent(originalName)}`;
if (currentIndex === index) {
const img = new Image();
img.onload = () => {
if (currentIndex === index) {
lightboxImg.src = fullResUrl;
dlBtn.href = fullResUrl;
loadingIndicator.style.display = 'none';
}
};
img.src = fullResUrl;
}
} else {
loadingIndicator.style.display = 'none';
}
}
function closeLightbox() {
lightboxEl.style.display = 'none';
lightboxImg.src = '';
document.body.style.overflow = 'auto';
}
function nextImage() {
currentIndex = (currentIndex + 1) % THUMBNAILS.length;
openLightbox(currentIndex);
}
function prevImage() {
currentIndex = (currentIndex - 1 + THUMBNAILS.length) % THUMBNAILS.length;
openLightbox(currentIndex);
}
document.getElementById('close-btn').onclick = closeLightbox;
document.getElementById('next-btn').onclick = nextImage;
document.getElementById('prev-btn').onclick = prevImage;
lightboxEl.onclick = (e) => {
if (e.target === lightboxEl) closeLightbox();
};
window.addEventListener('keydown', (e) => {
if (lightboxEl.style.display === 'flex') {
if (e.key === 'Escape') closeLightbox();
if (e.key === 'ArrowRight') nextImage();
if (e.key === 'ArrowLeft') prevImage();
}
});
document.addEventListener('DOMContentLoaded', renderGallery);
</script>
</body>
</html>