first commit

This commit is contained in:
bs-sensei
2026-03-24 20:30:43 -04:00
commit de259dba5b
27 changed files with 3242 additions and 0 deletions

BIN
static/bitcoin_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

1491
static/css/main.css Normal file

File diff suppressed because it is too large Load Diff

BIN
static/ethereum_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

9
static/favicon.svg Normal file
View File

@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<rect width="16" height="16" fill="#130e07"/>
<!-- Pixel "C" in amber -->
<rect x="2" y="2" width="8" height="2" fill="#d4a228"/>
<rect x="2" y="4" width="2" height="8" fill="#d4a228"/>
<rect x="2" y="12" width="8" height="2" fill="#d4a228"/>
<!-- Pixel dot in orange -->
<rect x="10" y="10" width="4" height="4" fill="#c4601c"/>
</svg>

After

Width:  |  Height:  |  Size: 441 B

152
static/js/main.js Normal file
View File

@@ -0,0 +1,152 @@
/* ============================================================
CODEX OBSCURA — main.js
Dust motes drifting in candlelight.
============================================================ */
(function () {
'use strict';
document.documentElement.classList.remove('no-js');
document.documentElement.classList.add('js');
/* ── Uptime Counter ──────────────────────────────────────── */
const uptimeEl = document.getElementById('js-uptime');
if (uptimeEl) {
const startTime = Date.now();
function updateUptime() {
const elapsed = Math.floor((Date.now() - startTime) / 1000);
const h = String(Math.floor(elapsed / 3600)).padStart(2, '0');
const m = String(Math.floor((elapsed % 3600) / 60)).padStart(2, '0');
const s = String(elapsed % 60).padStart(2, '0');
uptimeEl.textContent = h + ':' + m + ':' + s;
}
setInterval(updateUptime, 1000);
updateUptime();
}
/* ── Keyboard shortcuts ──────────────────────────────────── */
document.addEventListener('keydown', function (e) {
if (e.key === '/' && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
const search = document.getElementById('search-input');
if (search) { e.preventDefault(); search.focus(); }
}
if (e.key === 'Escape' && document.activeElement) {
document.activeElement.blur();
}
});
/* ── Dust Motes ──────────────────────────────────────────── */
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const canvas = document.createElement('canvas');
canvas.style.cssText = [
'position:absolute', 'top:0', 'left:0',
'width:100%', 'height:100%',
'pointer-events:none', 'z-index:2'
].join(';');
document.body.appendChild(canvas);
const ctx = canvas.getContext('2d');
let W = document.body.scrollWidth;
let H = document.body.scrollHeight;
canvas.width = W;
canvas.height = H;
window.addEventListener('resize', function () {
W = canvas.width = document.body.scrollWidth;
H = canvas.height = document.body.scrollHeight;
});
// Warm amber/parchment dust colours
const COLORS = [
'212, 162, 40', // amber
'201, 176, 122', // parchment warm
'196, 96, 28', // orange
'138, 96, 48', // umber
'232, 213, 163', // parchment light
' 78, 120, 48', // forest
];
const MOTE_COUNT = 500;
function rand(min, max) { return min + Math.random() * (max - min); }
function makeMote(scatterY) {
// Each mote has its own independent fade cycle driven by a sine wave
// so they breathe in and out at different rates rather than all
// appearing and disappearing together.
const peakAlpha = rand(0.06, 0.22);
return {
x: rand(0, W),
y: scatterY !== undefined ? scatterY : rand(0, H),
// very slow drift — tiny random velocity
vx: rand(-0.12, 0.12),
vy: rand(-0.08, 0.06), // slight upward bias like warm air
// size: 12.5px radius, soft and small
r: rand(1.2, 2.4),
color: COLORS[Math.floor(Math.random() * COLORS.length)],
peakAlpha,
// sine-wave phase and speed for opacity breathing
phase: rand(0, Math.PI * 2),
phaseSpeed: rand(0.003, 0.009), // full breath every ~1235 seconds
// gentle Brownian wobble
wobbleX: rand(0, Math.PI * 2),
wobbleY: rand(0, Math.PI * 2),
wobbleSpeedX: rand(0.002, 0.007),
wobbleSpeedY: rand(0.002, 0.006),
wobbleAmp: rand(0.04, 0.18), // pixels of wobble per frame
};
}
// Seed motes scattered across the whole viewport
let motes = Array.from({ length: MOTE_COUNT }, function () { return makeMote(rand(0, H)); });
function animate() {
ctx.clearRect(0, 0, W, H);
const now = performance.now() * 0.001; // seconds, for wobble
for (let i = 0; i < motes.length; i++) {
const m = motes[i];
// Advance fade phase
m.phase += m.phaseSpeed;
// Opacity: sine wave between 0 and peakAlpha, always non-negative
const alpha = m.peakAlpha * (0.5 + 0.5 * Math.sin(m.phase));
// Drift position
m.x += m.vx + Math.sin(m.wobbleX) * m.wobbleAmp;
m.y += m.vy + Math.cos(m.wobbleY) * m.wobbleAmp * 0.6;
// Advance wobble phases independently of main phase
m.wobbleX += m.wobbleSpeedX;
m.wobbleY += m.wobbleSpeedY;
// Wrap around edges with a small buffer so motes never pop in visibly
const buf = 4;
if (m.x < -buf) m.x = W + buf;
if (m.x > W + buf) m.x = -buf;
if (m.y < -buf) m.y = H + buf;
if (m.y > H + buf) m.y = -buf;
// Draw as a soft radial-gradient circle (blurred disc, not a hard pixel)
const grad = ctx.createRadialGradient(m.x, m.y, 0, m.x, m.y, m.r * 2.5);
grad.addColorStop(0, 'rgba(' + m.color + ', ' + alpha + ')');
grad.addColorStop(0.5, 'rgba(' + m.color + ', ' + (alpha * 0.4) + ')');
grad.addColorStop(1, 'rgba(' + m.color + ', 0)');
ctx.beginPath();
ctx.arc(m.x, m.y, m.r * 2.5, 0, Math.PI * 2);
ctx.fillStyle = grad;
ctx.fill();
}
requestAnimationFrame(animate);
}
animate();
})();

BIN
static/kofi.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

1
static/liberapay.svg Normal file
View File

@@ -0,0 +1 @@
<svg viewBox="0 0 80 80" xmlns="http://www.w3.org/2000/svg"><rect fill="#f6c915" width="80" height="80" rx="10"/><path fill="#1a171b" d="M32.73 56.2651c-2.5867 0-4.6175-.3376-6.0925-1.0107s-2.5308-1.5936-3.1708-2.7611-.9458-2.4933-.9275-4.015.2267-3.1234.6284-4.8357l6.9391-29.0143 8.47-1.3108-7.595 31.4733c-.1458.655-.2275 1.2567-.2458 1.8025s.0817 1.0292.3 1.4475.5917.7566 1.12 1.0108 1.2658.42 2.2133.4925zM40.5333 28.0209c1.46-.4367 3.1267-.8284 5.0025-1.175s3.9075-.5183 6.0933-.5183 3.7985.3092 5.2726.9283 2.6864 1.4667 3.6341 2.5409 1.6475 2.3316 2.1024 3.77.6832 2.9783.6832 4.6183c0 2.6583-.4373 5.09-1.3214 7.2942s-2.076 4.1075-3.6237 5.71-3.4059 2.851-5.5763 3.7475-4.5235 1.3344-7.0718 1.3344c-1.2391 0-2.4775-.1104-3.7158-.3296l-2.4592 9.8912h-8.0865zM43.4867 49.3867c.6192.1458 1.3842.2133 2.295.2133 1.42 0 2.7133-.2583 3.8792-.7875s2.1583-1.265 2.9783-2.2125 1.4557-2.0842 1.9117-3.415.6827-2.795.6827-4.3983-.3467-2.8958-1.0379-3.9892-1.894-1.6391-3.6057-1.6391c-1.1667 0-2.2592.1092-3.2792.3283z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
static/litecoin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
static/monero_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
static/profile_pic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 877 KiB