Files
singular-particular-space/ToolsnToys/guides.html
JL Kruger 0290b29395 Add ToolsnToys section — hub, tools, guides, FOSS catalog, 6 guide pages
Hub (toolsntoys.html), JL's early tools (jl-early-tools.html), guides hub
(guides.html), FOSS Arsenal catalog (foss-tools.html), and 6 individual
guide pages covering AppImage, Linux install/uninstall, Docker, and
Facebook streaming. All pages use the night-sky adaptation of the
adventurers_toolbox3 aesthetic with dimmed ember alpha (0.38/0.65).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:14:21 +02:00

822 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FIELD MANUALS // FIELD UNIT SP-07</title>
<!-- Sixtyfour, Share Tech Mono, Rambla, Noto Emoji -->
<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&family=Share+Tech+Mono&family=Rambla:ital,wght@0,400;0,700;1,400;1,700&family=Noto+Emoji&display=swap" rel="stylesheet">
<style>
:root {
--bg-deep: #030812;
--bg-mid: #060f1a;
--bg-surface: #081524;
--bg-panel: #0d1e32;
--text-main: #c8d8e8;
--text-muted: #3a5570;
--phosphor: #00ff41;
--neon-green: #32dc8c;
--neon-green-dim: #229966;
--fire-amber: #e8943a;
--fire-amber-dim: #b06020;
--toucan: #ffcf40;
--fire-coral: #d4654a;
--neon-teal: #2ac4b3;
--orchid: #c558d9;
--border-neon: rgba(50,220,140,0.3);
--border-amber: rgba(232,148,58,0.4);
--campfire-glow: #e8943a;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg-deep);
color: var(--text-main);
font-family: 'Rambla', sans-serif;
min-height: 100vh;
overflow-x: hidden;
position: relative;
}
/* Night atmosphere bg */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(ellipse 60% 50% at 50% 80%, rgba(232,148,58,0.05) 0%, transparent 70%),
radial-gradient(ellipse 100% 60% at 20% 0%, rgba(3,8,18,0.9) 0%, transparent 60%),
radial-gradient(ellipse 100% 60% at 80% 0%, rgba(3,8,18,0.9) 0%, transparent 60%),
repeating-linear-gradient(
0deg,
transparent,
transparent 2px,
rgba(0,0,0,0.12) 2px,
rgba(0,0,0,0.12) 4px
);
pointer-events: none;
z-index: 0;
}
/* Scanline effect */
body::after {
content: '';
position: fixed;
inset: 0;
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 3px,
rgba(0,0,0,0.15) 3px,
rgba(0,0,0,0.15) 4px
);
pointer-events: none;
z-index: 1000;
animation: scanline 15s linear infinite;
}
@keyframes scanline {
0% { background-position: 0 0; }
100% { background-position: 0 400px; }
}
/* Noise texture overlay */
.noise {
position: fixed;
inset: 0;
opacity: 0.04;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
pointer-events: none;
z-index: 999;
}
.app {
position: relative;
z-index: 1;
max-width: 1100px;
margin: 0 auto;
padding: 2rem 1.5rem;
}
/* ===== HEADER ===== */
.header {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-neon);
position: relative;
}
.header::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 40%;
height: 1px;
background: var(--neon-green);
box-shadow: 0 0 10px var(--neon-green);
animation: headerGlow 4s ease-in-out infinite alternate;
}
@keyframes headerGlow {
from { opacity: 0.4; width: 25%; }
to { opacity: 1; width: 55%; }
}
.unit-tag {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--fire-amber);
letter-spacing: 0.25em;
text-transform: uppercase;
margin-bottom: 0.4rem;
}
.title {
font-family: 'Sixtyfour', cursive;
font-size: 1.8rem;
font-weight: 400;
color: var(--neon-green);
text-shadow: 0 0 8px var(--neon-green), 0 0 20px rgba(50,220,140,0.6);
line-height: 1.2;
margin-bottom: 0.5rem;
}
.subtitle {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
letter-spacing: 0.2em;
text-transform: uppercase;
}
.status-badge {
font-family: 'Share Tech Mono', monospace;
font-size: 1rem;
padding: 0.25rem 0.8rem;
border: 1px solid var(--neon-green);
color: var(--neon-green);
letter-spacing: 0.15em;
text-shadow: 0 0 8px var(--neon-green);
box-shadow: inset 0 0 10px rgba(50,220,140,0.15), 0 0 8px rgba(50,220,140,0.2);
animation: badgePulse 2.5s ease-in-out infinite;
}
@keyframes badgePulse {
0%, 100% { opacity: 0.8; box-shadow: inset 0 0 8px rgba(50,220,140,0.1); }
50% { opacity: 1; box-shadow: inset 0 0 16px rgba(50,220,140,0.3), 0 0 12px rgba(50,220,140,0.4); }
}
.header-right {
text-align: right;
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: flex-end;
}
.coord {
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
letter-spacing: 0.1em;
}
/* ===== STAT BAR ===== */
.stats-bar {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 0.75rem;
margin-bottom: 2rem;
}
.stat-card {
background: var(--bg-panel);
border: 1px solid var(--border-neon);
padding: 0.85rem 1.1rem;
position: relative;
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
box-shadow: 0 0 15px rgba(0,0,0,0.3), inset 0 0 15px rgba(50,220,140,0.05);
}
.stat-card::before {
content: '';
position: absolute;
top: 0; right: 0;
width: 0; height: 0;
border-style: solid;
border-width: 0 12px 12px 0;
border-color: transparent var(--bg-deep) transparent transparent;
}
.stat-label {
font-family: 'Share Tech Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
letter-spacing: 0.18em;
margin-bottom: 0.4rem;
text-transform: uppercase;
}
.stat-value {
font-family: 'Sixtyfour', cursive;
font-size: 0.9rem;
color: var(--fire-amber);
text-shadow: 0 0 10px rgba(232,148,58,0.5);
margin-bottom: 0.2rem;
}
.stat-sub {
font-family: 'Share Tech Mono', monospace;
font-size: 0.85rem;
color: var(--text-muted);
}
.stat-bar-track {
height: 3px;
background: rgba(255,255,255,0.05);
margin-top: 0.6rem;
position: relative;
overflow: hidden;
}
.stat-bar-fill {
height: 100%;
background: var(--neon-green);
box-shadow: 0 0 6px var(--neon-green);
}
/* ===== MAIN LAYOUT ===== */
.main-grid {
display: grid;
grid-template-columns: 240px 1fr;
gap: 1.5rem;
}
/* ===== SIDEBAR ===== */
.panel {
background: var(--bg-panel);
border: 1px solid var(--border-neon);
margin-bottom: 1.5rem;
box-shadow: 0 0 15px rgba(0,0,0,0.4);
}
.panel-header {
font-family: 'Share Tech Mono', monospace;
font-size: 1rem;
letter-spacing: 0.2em;
color: var(--fire-amber);
padding: 0.6rem 0.8rem;
border-bottom: 1px solid rgba(232,148,58,0.2);
display: flex;
align-items: center;
gap: 0.5rem;
}
.panel-header::before { content: '//'; color: var(--text-muted); }
.panel-body { padding: 0.8rem; }
.category-list { display: flex; flex-direction: column; gap: 0.4rem; }
.cat-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.7rem;
cursor: pointer;
font-size: 0.9rem;
color: var(--text-main);
transition: all 150ms;
border: 1px solid transparent;
position: relative;
}
.cat-item:hover, .cat-item.active {
background: rgba(50,220,140,0.08);
border-color: rgba(50,220,140,0.3);
color: var(--neon-green);
}
.cat-item.active::before {
content: '';
position: absolute;
left: 0; top: 0; bottom: 0;
width: 2px;
background: var(--neon-green);
box-shadow: 0 0 8px var(--neon-green);
}
.status-line {
font-family: 'Share Tech Mono', monospace;
font-size: 0.8rem;
display: flex;
justify-content: space-between;
margin-bottom: 0.4rem;
}
/* ===== ITEM GRID ===== */
.item-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1rem;
}
.item-card {
background: var(--bg-panel);
border: 1px solid rgba(50,220,140,0.15);
position: relative;
transition: all 200ms ease;
overflow: hidden;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 10px 100%, 0 calc(100% - 10px));
display: flex;
flex-direction: column;
}
.item-card:hover {
border-color: rgba(50,220,140,0.5);
background: var(--bg-surface);
box-shadow: 0 8px 32px rgba(0,0,0,0.5), 0 0 15px rgba(50,220,140,0.1);
}
.item-card.legendary { border-color: rgba(255,207,64,0.3); }
.item-card.legendary:hover { border-color: var(--toucan); box-shadow: 0 0 20px rgba(255,207,64,0.15); }
.item-card.rare { border-color: rgba(42,196,179,0.3); }
.item-card.rare:hover { border-color: var(--neon-teal); }
.item-card.uncommon { border-color: rgba(50,220,140,0.3); }
.item-card.common { border-color: rgba(58,85,112,0.3); }
.item-card.damaged { border-color: rgba(212,101,74,0.3); }
.item-card::before {
content: '';
position: absolute;
top: 0; right: 0;
width: 0; height: 0;
border-style: solid;
border-width: 0 10px 10px 0;
border-color: transparent var(--bg-deep) transparent transparent;
}
.item-img-zone {
height: 100px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0,0,0,0.4);
font-size: 2.8rem;
font-family: 'Noto Emoji', sans-serif;
border-bottom: 1px solid rgba(255,255,255,0.05);
position: relative;
color: var(--text-muted);
}
.item-card.legendary .item-img-zone { color: var(--toucan); text-shadow: 0 0 12px rgba(255,207,64,0.5); }
.item-card.rare .item-img-zone { color: var(--neon-teal); text-shadow: 0 0 12px rgba(42,196,179,0.5); }
.item-card.uncommon .item-img-zone { color: var(--neon-green);text-shadow: 0 0 12px rgba(50,220,140,0.5); }
.item-card.common .item-img-zone { color: var(--text-main); text-shadow: 0 0 12px rgba(200,216,232,0.3); }
.item-card.damaged .item-img-zone { color: var(--fire-coral);text-shadow: 0 0 12px rgba(212,101,74,0.5); }
.item-body { padding: 1rem; flex: 1; display: flex; flex-direction: column; }
.item-name {
font-family: 'Rambla', sans-serif;
font-weight: 700;
font-size: 1rem;
color: var(--text-main);
margin-bottom: 0.3rem;
letter-spacing: 0.02em;
}
.item-meta {
display: flex;
justify-content: space-between;
font-family: 'Share Tech Mono', monospace;
font-size: 0.9rem;
color: var(--text-muted);
margin-bottom: 0.8rem;
}
.item-desc {
font-family: 'Rambla', sans-serif;
font-size: 0.9rem;
line-height: 1.4;
color: var(--text-main);
margin-bottom: 1rem;
flex: 1;
}
.condition-track {
height: 3px;
background: rgba(255,255,255,0.05);
margin-bottom: 1rem;
}
.condition-fill { height: 100%; box-shadow: 0 0 4px var(--neon-green); }
.rarity-pip {
position: absolute;
top: 0.6rem;
left: 0.6rem;
width: 6px;
height: 6px;
border-radius: 50%;
}
.pip-legendary { background: var(--toucan); box-shadow: 0 0 8px var(--toucan); animation: legendaryPip 2s infinite; }
.pip-rare { background: var(--neon-teal); box-shadow: 0 0 6px var(--neon-teal); }
.pip-uncommon { background: var(--neon-green); }
.pip-common { background: var(--text-muted); }
.pip-damaged { background: var(--fire-coral); }
@keyframes legendaryPip {
0%, 100% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.4); opacity: 1; }
}
.visit-btn {
font-family: 'Share Tech Mono', monospace;
font-size: 1rem;
padding: 0.6rem;
text-align: center;
background: transparent;
border: 1px solid var(--fire-amber);
color: var(--fire-amber);
text-decoration: none;
transition: all 150ms;
letter-spacing: 0.15em;
text-transform: uppercase;
}
.visit-btn:hover {
background: rgba(232,148,58,0.1);
box-shadow: 0 0 15px rgba(232,148,58,0.3);
text-shadow: 0 0 8px var(--fire-amber);
}
/* Back Link */
.back-link {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: 'Share Tech Mono', monospace;
color: var(--text-muted);
text-decoration: none;
margin-bottom: 1.5rem;
transition: color 150ms;
letter-spacing: 0.1em;
}
.back-link:hover { color: var(--neon-green); }
/* Canvas and FX */
#embers {
position: fixed;
inset: 0;
pointer-events: none;
z-index: 3;
}
.campfire-glow {
position: fixed;
bottom: -100px;
left: 50%;
transform: translateX(-50%);
width: 600px;
height: 400px;
background: radial-gradient(ellipse at center bottom, rgba(232,148,58,0.12) 0%, transparent 70%);
pointer-events: none;
z-index: 0;
animation: glowPulse 4s ease-in-out infinite;
}
@keyframes glowPulse {
0%, 100% { opacity: 0.5; }
50% { opacity: 0.9; transform: translateX(-50%) scale(1.05); }
}
::-webkit-scrollbar { width: 4px; }
::-webkit-scrollbar-track { background: var(--bg-deep); }
::-webkit-scrollbar-thumb { background: var(--text-muted); }
</style>
</head>
<body>
<div class="noise"></div>
<div class="campfire-glow"></div>
<canvas id="embers"></canvas>
<div class="app">
<a href="toolsntoys.html" class="back-link" target="_top">← TOOLS N TOYS</a>
<header class="header">
<div class="header-left">
<div class="unit-tag">▶ FIELD UNIT SP-07 // FIELD MANUALS</div>
<h1 class="title">FIELD MANUALS</h1>
<div class="subtitle">STEP-BY-STEP GUIDES AND REFERENCES</div>
</div>
<div class="header-right">
<div class="status-badge">● LOCAL_ACTIVE</div>
<div class="coord">SECTOR: MANUALS</div>
<div class="coord">SYNC: REALTIME</div>
</div>
</header>
<div class="stats-bar">
<div class="stat-card">
<div class="stat-label">GUIDES</div>
<div class="stat-value">6</div>
<div class="stat-sub">TOTAL</div>
<div class="stat-bar-track"><div class="stat-bar-fill" style="width:100%"></div></div>
</div>
<div class="stat-card">
<div class="stat-label">LINUX</div>
<div class="stat-value">3</div>
<div class="stat-sub">SYSTEM GUIDES</div>
<div class="stat-bar-track"><div class="stat-bar-fill" style="width:50%"></div></div>
</div>
<div class="stat-card">
<div class="stat-label">STREAMING</div>
<div class="stat-value">1</div>
<div class="stat-sub">SETUP GUIDE</div>
<div class="stat-bar-track"><div class="stat-bar-fill" style="width:16%"></div></div>
</div>
<div class="stat-card">
<div class="stat-label">DOCKER</div>
<div class="stat-value">1</div>
<div class="stat-sub">REFERENCE</div>
<div class="stat-bar-track"><div class="stat-bar-fill" style="width:16%"></div></div>
</div>
<div class="stat-card">
<div class="stat-label">APPIMAGE</div>
<div class="stat-value">2</div>
<div class="stat-sub">GUIDES</div>
<div class="stat-bar-track"><div class="stat-bar-fill" style="width:33%"></div></div>
</div>
</div>
<div class="main-grid">
<aside class="sidebar">
<div class="panel">
<div class="panel-header">CATEGORIES</div>
<div class="panel-body">
<div class="category-list">
<div class="cat-item active">
<span>ALL</span>
</div>
<div class="cat-item">
<span>LINUX</span>
</div>
<div class="cat-item">
<span>APPIMAGE</span>
</div>
<div class="cat-item">
<span>DOCKER</span>
</div>
<div class="cat-item">
<span>STREAMING</span>
</div>
</div>
</div>
</div>
<div class="panel">
<div class="panel-header">FIELD STATUS</div>
<div class="panel-body">
<div class="status-line">
<span style="color:var(--text-muted)">CONNECTIVITY</span>
<span style="color:var(--neon-green)">LOCAL</span>
</div>
<div class="status-line">
<span style="color:var(--text-muted)">LAST SYNC</span>
<span style="color:var(--fire-amber)">NOW</span>
</div>
</div>
</div>
</aside>
<main class="item-grid">
<!-- LINUX INSTALLATION METHODS -->
<div class="item-card legendary">
<div class="rarity-pip pip-legendary"></div>
<div class="item-img-zone">🗂</div>
<div class="item-body">
<div class="item-name">LINUX INSTALLATION METHODS</div>
<div class="item-meta">
<span>LINUX</span>
<span style="color:var(--toucan)">LEGENDARY</span>
</div>
<p class="item-desc">"Every method from APT to source builds. Comparison table. When to use what."</p>
<div class="condition-track"><div class="condition-fill" style="width:100%; background:var(--neon-green)"></div></div>
<a href="linux-installation-methods.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
<!-- APPIMAGE INSTALLATION -->
<div class="item-card rare">
<div class="rarity-pip pip-rare"></div>
<div class="item-img-zone">📦</div>
<div class="item-body">
<div class="item-name">APPIMAGE INSTALLATION</div>
<div class="item-meta">
<span>APPIMAGE</span>
<span style="color:var(--neon-teal)">RARE</span>
</div>
<p class="item-desc">"Full walkthrough. Three methods, desktop integration, icon extraction, troubleshooting."</p>
<div class="condition-track"><div class="condition-fill" style="width:85%; background:var(--neon-teal)"></div></div>
<a href="appimage-installation-guide.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
<!-- DOCKER COMMAND CHEATSHEET -->
<div class="item-card rare">
<div class="rarity-pip pip-rare"></div>
<div class="item-img-zone">🐳</div>
<div class="item-body">
<div class="item-name">DOCKER COMMAND CHEATSHEET</div>
<div class="item-meta">
<span>DOCKER</span>
<span style="color:var(--neon-teal)">RARE</span>
</div>
<p class="item-desc">"Complete Docker and Docker Compose command reference with examples."</p>
<div class="condition-track"><div class="condition-fill" style="width:85%; background:var(--neon-teal)"></div></div>
<a href="docker-cheatsheet.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
<!-- LINUX UNINSTALLATION & CLEANUP -->
<div class="item-card uncommon">
<div class="rarity-pip pip-uncommon"></div>
<div class="item-img-zone">🧹</div>
<div class="item-body">
<div class="item-name">LINUX UNINSTALLATION & CLEANUP</div>
<div class="item-meta">
<span>LINUX</span>
<span style="color:var(--neon-green)">UNCOMMON</span>
</div>
<p class="item-desc">"Seven removal methods, a case study, and nuclear options. Leave no orphans."</p>
<div class="condition-track"><div class="condition-fill" style="width:75%; background:var(--neon-green)"></div></div>
<a href="linux-uninstallation.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
<!-- APPIMAGE QUICK REFERENCE -->
<div class="item-card common">
<div class="rarity-pip pip-common"></div>
<div class="item-img-zone"></div>
<div class="item-body">
<div class="item-name">APPIMAGE QUICK REFERENCE</div>
<div class="item-meta">
<span>APPIMAGE</span>
<span style="color:var(--text-muted)">COMMON</span>
</div>
<p class="item-desc">"One-page cheat sheet. Commands, templates, pro tips. Print it out."</p>
<div class="condition-track"><div class="condition-fill" style="width:60%; background:var(--text-muted)"></div></div>
<a href="appimage-quick-reference.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
<!-- FACEBOOK PAGE LIVESTREAMING -->
<div class="item-card damaged">
<div class="rarity-pip pip-damaged"></div>
<div class="item-img-zone">📡</div>
<div class="item-body">
<div class="item-name">FACEBOOK PAGE LIVESTREAMING</div>
<div class="item-meta">
<span>STREAMING</span>
<span style="color:var(--fire-coral)">DAMAGED</span>
</div>
<p class="item-desc">"Multi-camera OBS setup on Mac M2. Hardware-specific. Dated but functional."</p>
<div class="condition-track"><div class="condition-fill" style="width:30%; background:var(--fire-coral)"></div></div>
<a href="facebook-streaming.html" class="visit-btn">READ GUIDE</a>
</div>
</div>
</main>
</div>
</div>
<script>
/* === EMBER PARTICLE SYSTEM (Night Sky Adaptation) === */
const canvas = document.getElementById('embers');
const ctx = canvas.getContext('2d');
function resize() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
resize();
window.addEventListener('resize', resize);
const EMBER_COUNT = 60;
const embers = [];
const mouse = { x: -9999, y: -9999 };
window.addEventListener('mousemove', ev => { mouse.x = ev.clientX; mouse.y = ev.clientY; });
const ZONES = [
{ r: 40, strength: 0.012 },
{ r: 90, strength: 0.004 },
{ r: 130, strength: 0.001 },
];
const COLORS = [
{ r: 232, g: 148, b: 58 }, // fire-amber
{ r: 212, g: 101, b: 74 }, // fire-coral
{ r: 255, g: 207, b: 64 }, // toucan
{ r: 197, g: 88, b: 217 }, // orchid
];
function randomEmber() {
const col = COLORS[Math.floor(Math.random() * COLORS.length)];
return {
x: Math.random() * canvas.width,
y: canvas.height + 10,
vx: (Math.random() - 0.5) * 0.8,
vy: -(0.4 + Math.random() * 1.2),
life: 0,
maxLife: 200 + Math.random() * 300,
size: 1 + Math.random() * 2,
r: col.r, g: col.g, b: col.b,
wobble: Math.random() * Math.PI * 2,
wobbleSpeed: 0.02 + Math.random() * 0.02,
trail: [],
};
}
for (let i = 0; i < EMBER_COUNT; i++) {
const e = randomEmber();
e.y = Math.random() * canvas.height;
e.life = Math.random() * e.maxLife;
embers.push(e);
}
function drawEmber(e) {
const progress = e.life / e.maxLife;
const alpha = progress < 0.1 ? progress / 0.1 : progress > 0.8 ? (1 - progress) / 0.2 : 1;
if (alpha <= 0) return;
const gAlpha = alpha * 0.38; // Dimmed as per mandate
const glow = ctx.createRadialGradient(e.x, e.y, 0, e.x, e.y, e.size * 5);
glow.addColorStop(0, `rgba(${e.r},${e.g},${e.b},${gAlpha})`);
glow.addColorStop(1, `rgba(${e.r},${e.g},${e.b},0)`);
ctx.beginPath();
ctx.arc(e.x, e.y, e.size * 5, 0, Math.PI * 2);
ctx.fillStyle = glow;
ctx.fill();
// core pixel
ctx.fillStyle = `rgba(${e.r},${e.g},${e.b},${Math.min(0.65, gAlpha * 1.4)})`;
const ps = Math.max(1, Math.round(e.size));
ctx.fillRect(Math.round(e.x) - ps, Math.round(e.y) - ps, ps * 2, ps * 2);
for (let t = 0; t < e.trail.length; t++) {
const pt = e.trail[t];
const ta = (t / e.trail.length) * gAlpha * 0.3;
ctx.fillStyle = `rgba(${e.r},${e.g},${e.b},${ta})`;
ctx.fillRect(Math.round(pt.x), Math.round(pt.y), 2, 2);
}
}
function tick() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const e of embers) {
if (e.life % 6 === 0) {
e.trail.push({ x: e.x, y: e.y });
if (e.trail.length > 5) e.trail.shift();
}
const dx = mouse.x - e.x, dy = mouse.y - e.y, d = Math.sqrt(dx*dx+dy*dy);
if (d < ZONES[2].r) {
let s = 0;
for(const z of ZONES) if(d < z.r) { s = z.strength; break; }
e.vx += (dx/d)*s; e.vy += (dy/d)*s;
e.vx *= 0.98; e.vy *= 0.98;
}
e.wobble += e.wobbleSpeed;
e.x += e.vx + Math.sin(e.wobble) * 0.3;
e.y += e.vy;
e.life++;
if (e.life >= e.maxLife || e.y < -20) Object.assign(e, randomEmber());
drawEmber(e);
}
requestAnimationFrame(tick);
}
tick();
// Simple category switching logic
const catItems = document.querySelectorAll('.cat-item');
catItems.forEach(item => {
item.addEventListener('click', () => {
catItems.forEach(c => c.classList.remove('active'));
item.classList.add('active');
});
});
</script>
</body>
</html>