237 lines
7.5 KiB
HTML
237 lines
7.5 KiB
HTML
|
|
<style>
|
|
html {
|
|
background: #000;
|
|
}
|
|
|
|
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
#matrix-wrap {
|
|
background: #000;
|
|
min-height: 1200px;
|
|
width: 100%;
|
|
border-radius: var(--border-radius-lg);
|
|
overflow: hidden;
|
|
position: relative;
|
|
font-family: var(--font-mono);
|
|
}
|
|
canvas#rain {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
position: absolute;
|
|
top: 0; left: 0;
|
|
}
|
|
#terminal {
|
|
position: relative;
|
|
z-index: 2;
|
|
padding: 2rem 2.5rem;
|
|
min-height: 600px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
#boot-lines {
|
|
flex: 1;
|
|
}
|
|
.boot-line {
|
|
font-size: 13px;
|
|
color: #00ff41;
|
|
line-height: 1.8;
|
|
opacity: 0;
|
|
white-space: pre;
|
|
}
|
|
.boot-line.dim { color: #00aa2b; }
|
|
.boot-line.bright { color: #a3ffb4; font-weight: 500; }
|
|
.boot-line.warn { color: #ffcc00; }
|
|
#prompt-area {
|
|
margin-top: 1.5rem;
|
|
opacity: 0;
|
|
transition: opacity 0.5s;
|
|
}
|
|
#prompt-area.visible { opacity: 1; }
|
|
#input-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
#prompt-label {
|
|
color: #00ff41;
|
|
font-size: 14px;
|
|
white-space: nowrap;
|
|
}
|
|
#cmd-input {
|
|
background: transparent;
|
|
border: none;
|
|
border-bottom: 1px solid #00ff4166;
|
|
border-radius: 0;
|
|
color: #00ff41;
|
|
font-family: var(--font-mono);
|
|
font-size: 14px;
|
|
outline: none;
|
|
flex: 1;
|
|
padding: 2px 4px;
|
|
caret-color: #00ff41;
|
|
}
|
|
#cmd-input:focus { border-bottom-color: #00ff41; }
|
|
#output {
|
|
margin-top: 1rem;
|
|
font-size: 13px;
|
|
color: #00ff41;
|
|
line-height: 1.9;
|
|
min-height: 60px;
|
|
white-space: pre-wrap;
|
|
}
|
|
.o-dim { color: #00aa2b; }
|
|
.o-bright { color: #a3ffb4; }
|
|
.o-warn { color: #ffcc00; }
|
|
.o-pink { color: #ff79c6; }
|
|
</style>
|
|
|
|
<div id="matrix-wrap">
|
|
<canvas id="rain"></canvas>
|
|
<div id="terminal">
|
|
<div id="boot-lines"></div>
|
|
<div id="prompt-area">
|
|
<div id="input-row">
|
|
<span id="prompt-label">samantha42@secret:~$</span>
|
|
<input id="cmd-input" type="text" autocomplete="off" spellcheck="false" placeholder="type 'help'" aria-label="Terminal input" />
|
|
</div>
|
|
<div id="output"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('rain');
|
|
const ctx = canvas.getContext('2d');
|
|
const wrap = document.getElementById('matrix-wrap');
|
|
|
|
function resizeCanvas() {
|
|
canvas.width = wrap.offsetWidth;
|
|
canvas.height = wrap.offsetHeight;
|
|
}
|
|
resizeCanvas();
|
|
|
|
const chars = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワヲン0123456789ABCDEF@#$%&*';
|
|
const fontSize = 14;
|
|
let cols, drops;
|
|
|
|
function initDrops() {
|
|
cols = Math.floor(canvas.width / fontSize);
|
|
drops = Array(cols).fill(1);
|
|
}
|
|
initDrops();
|
|
|
|
function drawRain() {
|
|
ctx.fillStyle = 'rgba(0,0,0,0.05)';
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
ctx.font = fontSize + 'px monospace';
|
|
for (let i = 0; i < drops.length; i++) {
|
|
const bright = Math.random() > 0.95;
|
|
ctx.fillStyle = bright ? '#a3ffb4' : '#00ff41';
|
|
ctx.globalAlpha = bright ? 1 : 0.7;
|
|
const ch = chars[Math.floor(Math.random() * chars.length)];
|
|
ctx.fillText(ch, i * fontSize, drops[i] * fontSize);
|
|
ctx.globalAlpha = 1;
|
|
if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) drops[i] = 0;
|
|
drops[i]++;
|
|
}
|
|
}
|
|
|
|
setInterval(drawRain, 50);
|
|
|
|
const bootSequence = [
|
|
{ text: 'SAMANTHA42 OS v4.2.0 — initializing...', cls: 'dim', delay: 200 },
|
|
{ text: 'Loading kernel modules... [OK]', cls: 'dim', delay: 400 },
|
|
{ text: 'Mounting secret filesystem... [OK]', cls: 'dim', delay: 600 },
|
|
{ text: 'Verifying identity... [OK]', cls: 'dim', delay: 900 },
|
|
{ text: '> ACCESS GRANTED ', cls: 'bright', delay: 1200 },
|
|
{ text: '', cls: '', delay: 1350 },
|
|
{ text: 'Welcome back, curious one. ', cls: 'warn', delay: 1500 },
|
|
{ text: 'You found the secret page. Nice work. ', cls: '', delay: 1700 },
|
|
{ text: '', cls: '', delay: 1850 },
|
|
{ text: "Type 'help' to see what's hidden here.", cls: 'dim', delay: 2000 },
|
|
];
|
|
|
|
const bootEl = document.getElementById('boot-lines');
|
|
const promptArea = document.getElementById('prompt-area');
|
|
|
|
bootSequence.forEach(({ text, cls, delay }) => {
|
|
setTimeout(() => {
|
|
const line = document.createElement('div');
|
|
line.className = 'boot-line' + (cls ? ' ' + cls : '');
|
|
line.textContent = text;
|
|
bootEl.appendChild(line);
|
|
requestAnimationFrame(() => { line.style.opacity = '1'; });
|
|
}, delay);
|
|
});
|
|
|
|
setTimeout(() => { promptArea.classList.add('visible'); }, 2200);
|
|
|
|
const commands = {
|
|
help: () => `<span class="o-bright">available commands:</span>
|
|
<span class="o-dim"> whoami </span>— the real story
|
|
<span class="o-dim"> skills </span>— the unofficial skill list
|
|
<span class="o-dim"> facts </span>— things my CV won't tell you
|
|
<span class="o-dim"> crush </span>— current hyperfixations
|
|
<span class="o-dim"> 42 </span>— the answer
|
|
<span class="o-dim"> clear </span>— clear the terminal`,
|
|
|
|
whoami: () => `<span class="o-bright">samantha</span> — numberphile, programmer, overthinker.
|
|
loves: movies, pixel-perfect layouts or fun design, staying up too late debugging.
|
|
hates: Comic Sans and broken padding.
|
|
found here by: <span class="o-pink">being curious</span> (good trait btw)`,
|
|
|
|
skills: () => `<span class="o-bright">UNOFFICIAL SKILL TREE</span>
|
|
<span class="o-dim"> ████████████████████</span> pretending to understand the error
|
|
<span class="o-dim"> █████████████████░░░</span> googling things I definitely know
|
|
<span class="o-dim"> ████████████████████</span> making things pretty at 2am
|
|
<span class="o-dim"> █████████░░░░░░░░░░░</span> replying to emails promptly
|
|
<span class="o-dim"> ████████████████████</span> convincing clients the bug is a feature`,
|
|
|
|
facts: () => `<span class="o-bright">fun facts not on
|
|
→ 42 is my favourite number (obviously)
|
|
→ I debug best when listening to lo-fi`,
|
|
|
|
crush: () => `<span class="o-bright">current hyperfixations:</span>
|
|
<span class="o-pink"> ♡</span> linux ricing
|
|
<span class="o-pink"> ♡</span> Japanese Katakana typography
|
|
<span class="o-pink"> ♡</span> number go up. But Fuck btc, lol
|
|
<span class="o-pink"> ♡</span> praying yto loard gaben. If you know, you know
|
|
<span class="o-pink"> ♡</span> building useless website pages, clearly`,
|
|
|
|
42: () => `<span class="o-bright">"The Answer to the Ultimate Question of Life,
|
|
the Universe, and Everything."</span>
|
|
— Deep Thought, after 7.5 million years
|
|
|
|
also: my lucky number. also: this website's name.
|
|
<span class="o-dim">don't ask what the question is. nobody knows.</span>`,
|
|
|
|
clear: () => '__clear__',
|
|
};
|
|
|
|
const input = document.getElementById('cmd-input');
|
|
const output = document.getElementById('output');
|
|
|
|
input.addEventListener('keydown', (e) => {
|
|
if (e.key !== 'Enter') return;
|
|
const cmd = input.value.trim().toLowerCase();
|
|
input.value = '';
|
|
if (!cmd) return;
|
|
|
|
if (commands[cmd]) {
|
|
const result = commands[cmd]();
|
|
if (result === '__clear__') {
|
|
output.innerHTML = '';
|
|
} else {
|
|
output.innerHTML = `<span class="o-dim">$ ${cmd}</span>\n` + result + '\n\n';
|
|
}
|
|
} else {
|
|
output.innerHTML = `<span class="o-dim">$ ${cmd}</span>\n<span class="o-warn">command not found: ${cmd}</span>\ntype <span class="o-bright">help</span> to see available commands\n\n`;
|
|
}
|
|
});
|
|
|
|
input.addEventListener('focus', () => {});
|
|
setTimeout(() => { input.focus(); }, 2300);
|
|
</script>
|