Initial commit — Singular Particular Space v1
Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!--
|
||||
Integration v7 — THE NIGHT MARKET: FINAL DESCENT — 2026-03-26
|
||||
AUTHOR: Gemini (Master Designer)
|
||||
|
||||
PARALLAX & FLOW:
|
||||
- Stars & Grid (Back): Fixed-ish, drifting UP slowly (0.1x).
|
||||
- Skyline (Mid): Rises gracefully from the bottom.
|
||||
- Fading: Grid fades first, then Stars, as you descend into the city.
|
||||
- Aesthetics: Buildings 25% darker (#040712), lush vines, dynamic grid pulses.
|
||||
- Widescreen: Ultra-dense skyline (100 buildings) with clustered nav.
|
||||
-->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>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=Noto+Emoji&family=Space+Grotesk:wght@300;400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
:root {
|
||||
--bg-void: #04060b;
|
||||
--bg-deep: #040712; /* 25% darker */
|
||||
--bg-warm: #0d1320;
|
||||
--fire-amber: #e8943a;
|
||||
--fire-coral: #d4654a;
|
||||
--neon-green: #32dc8c;
|
||||
--neon-teal: #2ac4b3;
|
||||
--orchid: #c558d9;
|
||||
--mint-glow: #86efac;
|
||||
--fairy-pink: #f472b6;
|
||||
--phosphor: #00ff41;
|
||||
--text-warm: #e8d5b8;
|
||||
--text-muted: #6a7a8a;
|
||||
}
|
||||
|
||||
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: auto; overflow-x: hidden; }
|
||||
|
||||
body {
|
||||
font-family: 'Space Grotesk', system-ui, -apple-system, sans-serif;
|
||||
background: var(--bg-void);
|
||||
color: var(--text-warm);
|
||||
min-height: 200vh; /* Enable scroll for transition */
|
||||
}
|
||||
|
||||
body.content-mode { overflow-y: auto; }
|
||||
|
||||
/* ── CRT Scanlines ── */
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0, 0, 0, 0.04) 2px, rgba(0, 0, 0, 0.04) 4px);
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
/* ── Star Zone (The Fixed-ish Back) ── */
|
||||
#star-zone {
|
||||
position: fixed;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100vh;
|
||||
z-index: 1;
|
||||
background: radial-gradient(ellipse at 50% 40%, #090d1c 0%, var(--bg-void) 100%);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#nebula-canvas, #star-canvas, #grid-canvas {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
#grid-canvas {
|
||||
height: 45vh;
|
||||
top: auto; bottom: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* ── Star Nodes ── */
|
||||
.star-node {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
cursor: pointer;
|
||||
transform: translate(-50%, -50%);
|
||||
background: none; border: none; outline: none;
|
||||
}
|
||||
|
||||
.star-visual {
|
||||
position: relative;
|
||||
width: 40px; height: 40px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
|
||||
.star-dot {
|
||||
width: 7px; height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--text-muted);
|
||||
box-shadow: 0 0 6px rgba(255,255,255,0.2);
|
||||
transition: all 150ms ease;
|
||||
}
|
||||
|
||||
.star-label {
|
||||
font-size: clamp(12px, 2vw, 16px);
|
||||
color: var(--text-muted);
|
||||
opacity: 0;
|
||||
transition: all 200ms ease;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.star-node:hover .star-label, .star-node.current .star-label { opacity: 1; color: var(--text-warm); }
|
||||
.star-node:hover .star-dot { width: 20px; height: 20px; background: var(--fire-amber) !important; box-shadow: 0 0 12px var(--fire-amber); }
|
||||
.star-node.current .star-dot { width: 40px; height: 40px; background: var(--fire-amber) !important; box-shadow: 0 0 20px var(--fire-amber); }
|
||||
|
||||
/* Per-star colors */
|
||||
.star-node[data-star="writings"] .star-dot { background: #a0c4ff; }
|
||||
.star-node[data-star="videos"] .star-dot { background: #d4654a; }
|
||||
.star-node[data-star="music"] .star-dot { background: #2ac4b3; }
|
||||
.star-node[data-star="images"] .star-dot { background: #86efac; }
|
||||
.star-node[data-star="playlists"] .star-dot { background: #ffcf40; }
|
||||
.star-node[data-star="watchlists"] .star-dot { background: #c558d9; }
|
||||
.star-node[data-star="toolsntoys"] .star-dot { background: #f472b6; }
|
||||
.star-node[data-star="creatorlists"] .star-dot { background: #c4a24a; }
|
||||
|
||||
/* ── Transition Zone (The Rising Mid) ── */
|
||||
#transition-zone {
|
||||
position: relative;
|
||||
width: 100%; height: 100vh;
|
||||
margin-top: 100vh; /* Sits below the star zone */
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#skyline {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
padding-bottom: 5vh;
|
||||
}
|
||||
|
||||
.building {
|
||||
background: var(--bg-deep);
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
border-top: 1px solid rgba(232, 148, 58, 0.1);
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.vine {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
background: var(--mint-glow);
|
||||
opacity: 0.2;
|
||||
box-shadow: 0 0 4px var(--mint-glow);
|
||||
animation: vine-glow 4s infinite;
|
||||
}
|
||||
.vine.orchid { background: var(--orchid); box-shadow: 0 0 4px var(--orchid); animation-delay: 2s; }
|
||||
|
||||
@keyframes vine-glow { 0%, 100% { opacity: 0.15; } 50% { opacity: 0.4; } }
|
||||
|
||||
.billboard-nav {
|
||||
position: absolute;
|
||||
top: -2px; left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
background: rgba(4, 6, 11, 0.95);
|
||||
border: 1px solid var(--neon-teal);
|
||||
color: var(--neon-teal);
|
||||
padding: 4px 10px;
|
||||
font-size: 18px;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
pointer-events: auto;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* ── Content Zone ── */
|
||||
#content-zone {
|
||||
position: relative;
|
||||
width: 100%; min-height: 100vh;
|
||||
background: var(--bg-warm);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#content-frame {
|
||||
width: 100%; height: 100vh;
|
||||
border: none; background: var(--bg-warm);
|
||||
opacity: 0; transition: opacity 0.5s;
|
||||
}
|
||||
#content-frame.loaded { opacity: 1; }
|
||||
|
||||
#hud {
|
||||
position: fixed; top: 15px; right: 15px; z-index: 100;
|
||||
background: rgba(4,6,11,0.9); border: 1px solid rgba(0,255,65,0.2);
|
||||
padding: 8px 12px; font-family: monospace; color: var(--phosphor);
|
||||
}
|
||||
#back-btn { background: none; border: none; color: inherit; cursor: pointer; }
|
||||
#back-btn::before { content: '> '; }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="star-zone">
|
||||
<canvas id="nebula-canvas"></canvas>
|
||||
<canvas id="star-canvas"></canvas>
|
||||
<canvas id="grid-canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<div id="transition-zone">
|
||||
<div id="skyline"></div>
|
||||
</div>
|
||||
|
||||
<div id="content-zone">
|
||||
<iframe id="content-frame"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="hud">
|
||||
<button id="back-btn">STARS</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const STARS = [
|
||||
{ id: 'writings', label: 'Writings', sign: '文', x: 25, y: 18 },
|
||||
{ id: 'videos', label: 'Videos', sign: '映', x: 68, y: 15 },
|
||||
{ id: 'music', label: 'Music', sign: '♬', x: 12, y: 42 },
|
||||
{ id: 'images', label: 'Images', sign: '絵', x: 55, y: 35 },
|
||||
{ id: 'playlists', label: 'Playlists', sign: '≡', x: 78, y: 48 },
|
||||
{ id: 'watchlists', label: 'Watchlists', sign: '視', x: 22, y: 65 },
|
||||
{ id: 'toolsntoys', label: 'ToolsnToys', sign: '⚙', x: 50, y: 72 },
|
||||
{ id: 'creatorlists', label: 'Creatorlists', sign: '創', x: 75, y: 70 }
|
||||
];
|
||||
|
||||
const starCanvas = document.getElementById('star-canvas');
|
||||
const nebulaCanvas = document.getElementById('nebula-canvas');
|
||||
const gridCanvas = document.getElementById('grid-canvas');
|
||||
const transitionZone = document.getElementById('transition-zone');
|
||||
const skyline = document.getElementById('skyline');
|
||||
const starZone = document.getElementById('star-zone');
|
||||
|
||||
// ── Syncronized Parallax ──
|
||||
function updateParallax() {
|
||||
const scrollY = window.scrollY || window.pageYOffset;
|
||||
const vh = window.innerHeight;
|
||||
const progress = Math.min(scrollY / vh, 1);
|
||||
|
||||
// 1. Background Layers (Stars/Grid) - Move SLOWLY (10% speed)
|
||||
const bgTranslate = -scrollY * 0.1;
|
||||
starCanvas.style.transform = `translateY(${bgTranslate}px)`;
|
||||
nebulaCanvas.style.transform = `translateY(${bgTranslate}px)`;
|
||||
gridCanvas.style.transform = `translateY(${bgTranslate}px)`;
|
||||
|
||||
// 2. Fading Sequence (Grid first, then Stars)
|
||||
gridCanvas.style.opacity = Math.max(0, 1 - progress * 2.5); // Fades out by 40% scroll
|
||||
starCanvas.style.opacity = Math.max(0, 1 - progress * 1.2); // Fades out by 85% scroll
|
||||
nebulaCanvas.style.opacity = Math.max(0, 1 - progress * 1.5);
|
||||
|
||||
// 3. Skyline (Mid) - Rises gracefully as you scroll
|
||||
// Since transition-zone is already moving with scroll, we just tweak its relative position
|
||||
const skyRise = -scrollY * 0.2;
|
||||
skyline.style.transform = `translateY(${skyRise}px)`;
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', updateParallax, { passive: true });
|
||||
|
||||
// ── Starfield Drift ──
|
||||
let bgStars = [];
|
||||
function initStars() {
|
||||
const w = window.innerWidth, h = window.innerHeight;
|
||||
starCanvas.width = w; starCanvas.height = h;
|
||||
bgStars = Array.from({length: 300}, () => ({
|
||||
x: Math.random() * w, y: Math.random() * h,
|
||||
r: Math.random() * 2,
|
||||
dx: (Math.random() - 0.5) * 0.05,
|
||||
dy: (Math.random() - 0.5) * 0.05,
|
||||
o: 0.1 + Math.random() * 0.5
|
||||
}));
|
||||
}
|
||||
|
||||
function drawStars() {
|
||||
const ctx = starCanvas.getContext('2d');
|
||||
ctx.clearRect(0,0,starCanvas.width, starCanvas.height);
|
||||
bgStars.forEach(s => {
|
||||
s.x += s.dx; s.y += s.dy;
|
||||
if (s.x < 0) s.x = starCanvas.width; if (s.x > starCanvas.width) s.x = 0;
|
||||
if (s.y < 0) s.y = starCanvas.height; if (s.y > starCanvas.height) s.y = 0;
|
||||
ctx.fillStyle = `rgba(232, 213, 184, ${s.o})`;
|
||||
ctx.beginPath(); ctx.arc(s.x, s.y, s.r, 0, 6.28); ctx.fill();
|
||||
});
|
||||
requestAnimationFrame(drawStars);
|
||||
}
|
||||
|
||||
// ── Data Pulse Grid ──
|
||||
function drawGrid(time) {
|
||||
const ctx = gridCanvas.getContext('2d');
|
||||
const w = gridCanvas.width = gridCanvas.offsetWidth;
|
||||
const h = gridCanvas.height = gridCanvas.offsetHeight;
|
||||
ctx.clearRect(0,0,w,h);
|
||||
const pulsePos = (time * 0.05) % 100;
|
||||
|
||||
for(let i=0; i<15; i++) {
|
||||
let s = i/14;
|
||||
let y = h * (s*s);
|
||||
ctx.strokeStyle = `rgba(42, 196, 179, ${0.04 + s*0.15})`;
|
||||
if (Math.abs(s*100 - pulsePos) < 5) ctx.strokeStyle = `rgba(232, 148, 58, 0.35)`;
|
||||
ctx.lineWidth = 1.2;
|
||||
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke();
|
||||
}
|
||||
requestAnimationFrame(drawGrid);
|
||||
}
|
||||
|
||||
// ── Skyline ──
|
||||
function buildSkyline() {
|
||||
const container = document.getElementById('skyline');
|
||||
container.innerHTML = '';
|
||||
const vw = window.innerWidth, vh = window.innerHeight;
|
||||
const count = 100; // High density
|
||||
const clusterStart = Math.floor(count/2) - 4;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const b = document.createElement('div');
|
||||
b.className = 'building';
|
||||
b.style.width = (vw < 768 ? 30 : 60) + Math.random()*50 + 'px';
|
||||
b.style.height = (vh * 0.15 + Math.random() * (vh * 0.55)) + 'px';
|
||||
|
||||
if(Math.random() > 0.4) {
|
||||
const v = document.createElement('div');
|
||||
v.className = 'vine' + (Math.random() > 0.5 ? ' orchid' : '');
|
||||
v.style.left = '30%'; v.style.top = '10%'; v.style.height = '30%';
|
||||
b.appendChild(v);
|
||||
}
|
||||
|
||||
if (i >= clusterStart && i < clusterStart + 8) {
|
||||
const star = STARS[i - clusterStart];
|
||||
const link = document.createElement('a');
|
||||
link.className = 'billboard-nav';
|
||||
link.textContent = star.sign;
|
||||
link.href = '#';
|
||||
link.onclick = (e) => { e.preventDefault(); selectStar(STARS.indexOf(star)); };
|
||||
b.appendChild(link);
|
||||
}
|
||||
container.appendChild(b);
|
||||
}
|
||||
}
|
||||
|
||||
function selectStar(idx) {
|
||||
const frame = document.getElementById('content-frame');
|
||||
frame.classList.remove('loaded');
|
||||
frame.srcdoc = `<html><body style="background:#0d1320;color:#e8d5b8;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;">
|
||||
<p>Entering ${STARS[idx].label}...</p></body></html>`;
|
||||
setTimeout(() => frame.classList.add('loaded'), 500);
|
||||
window.scrollTo({ top: document.getElementById('content-zone').offsetTop, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
document.getElementById('back-btn').onclick = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
STARS.forEach((s, i) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'star-node';
|
||||
btn.setAttribute('data-star', s.id);
|
||||
btn.style.left = s.x + '%'; btn.style.top = s.y + '%';
|
||||
btn.innerHTML = `<div class="star-visual"><span class="star-dot"></span></div><span class="star-label">${s.label}</span>`;
|
||||
btn.onclick = () => selectStar(i);
|
||||
starZone.appendChild(btn);
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => { initStars(); buildSkyline(); });
|
||||
initStars(); buildSkyline(); drawStars(); requestAnimationFrame(drawGrid);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user