moved static into source
@@ -0,0 +1,130 @@
|
||||
|
||||
<!-- row ~5% -->
|
||||
<div class="absolute" style="left:5%;top:3%;"><p class="text-red-500 rotate-12">49,020.00</p></div>
|
||||
<div class="absolute" style="left:38%;top:6%;"><img src="/static/images/mac.png" width="50px" class="rotate-6"></div>
|
||||
<div class="absolute" style="left:55%;top:4%;"><img src="/static/images/coffe.png" width="50px" class="rotate-45"></div>
|
||||
<div class="absolute" style="left:72%;top:7%;"><img src="/static/images/book.png" width="50px" class="-rotate-12"></div>
|
||||
<div class="absolute" style="left:88%;top:8%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:88%;top:3%;"><img src="/static/images/books.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:92%;top:3%;"><img src="/static/images/calc.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:65%;top:2%;"><p class="text-green-600 -rotate-6">1,204.50</p></div>
|
||||
<div class="absolute" style="left:20%;top:1%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-12"></div>
|
||||
<div class="absolute" style="left:45%;top:0%;"><img src="/static/images/calc.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:30%;top:5%;"><p class="text-red-500 rotate-8">-750.00</p></div>
|
||||
|
||||
<!-- row ~11% -->
|
||||
<div class="absolute" style="left:2%;top:11%;"><img src="/static/images/book.png" width="50px" class="rotate-25"></div>
|
||||
<div class="absolute" style="left:25%;top:10%;"><img src="/static/images/calc.png" width="50px" class="-rotate-15"></div>
|
||||
<div class="absolute" style="left:42%;top:12%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-10"></div>
|
||||
<div class="absolute" style="left:60%;top:11%;"><img src="/static/images/mac.png" width="50px" class="-rotate-30"></div>
|
||||
<div class="absolute" style="left:78%;top:10%;"><p class="text-green-500 rotate-5">3,180.00</p></div>
|
||||
<div class="absolute" style="left:93%;top:12%;"><img src="/static/images/coffe.png" width="50px" class="rotate-15"></div>
|
||||
|
||||
<!-- row ~18% -->
|
||||
<div class="absolute" style="left:3%;top:18%;"><img src="/static/images/coffe.png" width="50px" class="rotate-78"></div>
|
||||
<div class="absolute" style="left:15%;top:20%;"><img src="/static/images/mac.png" width="50px" class="-rotate-6"></div>
|
||||
<div class="absolute" style="left:47%;top:19%;"><img src="/static/images/books.png" width="50px" class="-rotate-20"></div>
|
||||
<div class="absolute" style="left:78%;top:21%;"><img src="/static/images/coffe.png" width="50px" class="-rotate-35"></div>
|
||||
<div class="absolute" style="left:92%;top:18%;"><img src="/static/images/mac.png" width="50px" class="rotate-90"></div>
|
||||
<div class="absolute" style="left:10%;top:12%;"><p class="text-green-500 rotate-6">8,300.00</p></div>
|
||||
<div class="absolute" style="left:30%;top:17%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:62%;top:18%;"><img src="/static/images/calc.png" width="50px" class="-rotate-10"></div>
|
||||
<div class="absolute" style="left:55%;top:22%;"><p class="text-red-500 -rotate-12">-5,900.00</p></div>
|
||||
|
||||
<!-- row ~25% -->
|
||||
<div class="absolute" style="left:8%;top:25%;"><img src="/static/images/calc.png" width="50px" class="-rotate-20"></div>
|
||||
<div class="absolute" style="left:20%;top:26%;"><p class="text-green-600 rotate-10">14,750.00</p></div>
|
||||
<div class="absolute" style="left:38%;top:24%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-15"></div>
|
||||
<div class="absolute" style="left:55%;top:27%;"><img src="/static/images/book.png" width="50px" class="-rotate-25"></div>
|
||||
<div class="absolute" style="left:72%;top:25%;"><img src="/static/images/coffe.png" width="50px" class="rotate-40"></div>
|
||||
<div class="absolute" style="left:87%;top:26%;"><p class="text-red-500 rotate-18">-320.00</p></div>
|
||||
|
||||
<!-- row ~32% -->
|
||||
<div class="absolute" style="left:8%;top:32%;"><img src="/static/images/book.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:22%;top:34%;"><img src="/static/images/coffe.png" width="50px" class="-rotate-12"></div>
|
||||
<div class="absolute" style="left:37%;top:31%;"><img src="/static/images/mac.png" width="50px" class="rotate-35"></div>
|
||||
<div class="absolute" style="left:67%;top:30%;"><img src="/static/images/books.png" width="50px" class="rotate-6"></div>
|
||||
<div class="absolute" style="left:22%;top:34%;"><p class="text-red-500 -rotate-6">-412.80</p></div>
|
||||
<div class="absolute" style="left:52%;top:33%;"><p class="text-green-600 rotate-15">88,500.00</p></div>
|
||||
<div class="absolute" style="left:83%;top:32%;"><p class="text-red-500 -rotate-20">-2,047.00</p></div>
|
||||
<div class="absolute" style="left:45%;top:36%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-30"></div>
|
||||
<div class="absolute" style="left:75%;top:35%;"><img src="/static/images/calc.png" width="50px" class="rotate-12"></div>
|
||||
|
||||
<!-- row ~39% -->
|
||||
<div class="absolute" style="left:2%;top:39%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-18"></div>
|
||||
<div class="absolute" style="left:15%;top:41%;"><p class="text-green-500 -rotate-8">620.00</p></div>
|
||||
<div class="absolute" style="left:30%;top:40%;"><img src="/static/images/mac.png" width="50px" class="rotate-25"></div>
|
||||
<div class="absolute" style="left:57%;top:38%;"><img src="/static/images/coffe.png" width="50px" class="-rotate-20"></div>
|
||||
<div class="absolute" style="left:71%;top:41%;"><img src="/static/images/books.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:88%;top:40%;"><p class="text-red-500 rotate-6">-11,200.00</p></div>
|
||||
|
||||
<!-- row ~46% -->
|
||||
<div class="absolute" style="left:17%;top:48%;"><img src="/static/images/books.png" width="50px" class="rotate-15"></div>
|
||||
<div class="absolute" style="left:49%;top:47%;"><img src="/static/images/coffe.png" width="50px" class="rotate-35"></div>
|
||||
<div class="absolute" style="left:64%;top:44%;"><img src="/static/images/mac.png" width="50px" class="-rotate-15"></div>
|
||||
<div class="absolute" style="left:80%;top:49%;"><img src="/static/images/book.png" width="50px" class="rotate-45"></div>
|
||||
<div class="absolute" style="left:4%;top:45%;"><img src="/static/images/calc.png" width="50px" class="-rotate-25"></div>
|
||||
<div class="absolute" style="left:33%;top:46%;"><p class="text-green-600 rotate-12">2,890.00</p></div>
|
||||
<div class="absolute" style="left:92%;top:47%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-8"></div>
|
||||
|
||||
<!-- row ~53% -->
|
||||
<div class="absolute" style="left:10%;top:53%;"><img src="/static/images/book.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:26%;top:55%;"><img src="/static/images/calc.png" width="50px" class="-rotate-18"></div>
|
||||
<div class="absolute" style="left:42%;top:52%;"><p class="text-red-500 rotate-20">-8,640.00</p></div>
|
||||
<div class="absolute" style="left:60%;top:54%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-14"></div>
|
||||
<div class="absolute" style="left:76%;top:53%;"><img src="/static/images/books.png" width="50px" class="-rotate-40"></div>
|
||||
<div class="absolute" style="left:91%;top:55%;"><p class="text-green-500 -rotate-10">47,000.00</p></div>
|
||||
|
||||
<!-- row ~60% -->
|
||||
<div class="absolute" style="left:6%;top:60%;"><img src="/static/images/mac.png" width="50px" class="rotate-12"></div>
|
||||
<div class="absolute" style="left:20%;top:62%;"><img src="/static/images/coffe.png" width="50px" class="-rotate-45"></div>
|
||||
<div class="absolute" style="left:36%;top:59%;"><img src="/static/images/books.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:51%;top:63%;"><img src="/static/images/book.png" width="50px" class="-rotate-12"></div>
|
||||
<div class="absolute" style="left:65%;top:61%;"><img src="/static/images/calc.png" width="50px" class="rotate-35"></div>
|
||||
<div class="absolute" style="left:80%;top:60%;"><p class="text-red-500 rotate-10">-990.00</p></div>
|
||||
<div class="absolute" style="left:93%;top:62%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-20"></div>
|
||||
|
||||
<!-- row ~67% -->
|
||||
<div class="absolute" style="left:2%;top:67%;"><p class="text-green-600 rotate-15">5,500.00</p></div>
|
||||
<div class="absolute" style="left:18%;top:68%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-22"></div>
|
||||
<div class="absolute" style="left:35%;top:66%;"><img src="/static/images/mac.png" width="50px" class="-rotate-10"></div>
|
||||
<div class="absolute" style="left:52%;top:69%;"><img src="/static/images/coffe.png" width="50px" class="rotate-50"></div>
|
||||
<div class="absolute" style="left:70%;top:67%;"><img src="/static/images/book.png" width="50px" class="-rotate-30"></div>
|
||||
<div class="absolute" style="left:85%;top:68%;"><img src="/static/images/calc.png" width="50px" class="rotate-5"></div>
|
||||
|
||||
<!-- row ~74% -->
|
||||
<div class="absolute" style="left:4%;top:74%;"><img src="/static/images/books.png" width="50px" class="rotate-35"></div>
|
||||
<div class="absolute" style="left:34%;top:73%;"><img src="/static/images/coffe.png" width="50px" class="rotate-15"></div>
|
||||
<div class="absolute" style="left:50%;top:77%;"><img src="/static/images/mac.png" width="50px" class="-rotate-35"></div>
|
||||
<div class="absolute" style="left:66%;top:75%;"><img src="/static/images/book.png" width="50px" class="rotate-45"></div>
|
||||
<div class="absolute" style="left:95%;top:76%;"><img src="/static/images/coffe.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:90%;top:75%;"><img src="/static/images/calc.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:18%;top:75%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-15"></div>
|
||||
<div class="absolute" style="left:40%;top:72%;"><p class="text-green-500 rotate-8">19,300.00</p></div>
|
||||
<div class="absolute" style="left:78%;top:73%;"><p class="text-red-500 -rotate-12">-3,750.00</p></div>
|
||||
|
||||
<!-- row ~81% -->
|
||||
<div class="absolute" style="left:8%;top:81%;"><img src="/static/images/calc.png" width="50px" class="rotate-20"></div>
|
||||
<div class="absolute" style="left:22%;top:83%;"><img src="/static/images/book.png" width="50px" class="-rotate-35"></div>
|
||||
<div class="absolute" style="left:40%;top:82%;"><p class="text-red-500 rotate-16">-60.50</p></div>
|
||||
<div class="absolute" style="left:55%;top:80%;"><img src="/static/images/keyboard.png" width="50px" class="rotate-28"></div>
|
||||
<div class="absolute" style="left:70%;top:83%;"><img src="/static/images/mac.png" width="50px" class="-rotate-8"></div>
|
||||
<div class="absolute" style="left:85%;top:81%;"><img src="/static/images/books.png" width="50px" class="rotate-42"></div>
|
||||
|
||||
<!-- row ~88% -->
|
||||
<div class="absolute" style="left:25%;top:90%;"><img src="/static/images/mac.png" width="50px" class="rotate-45"></div>
|
||||
<div class="absolute" style="left:58%;top:91%;"><img src="/static/images/books.png" width="50px" class="rotate-12"></div>
|
||||
<div class="absolute" style="left:74%;top:89%;"><img src="/static/images/coffe.png" width="50px" class="-rotate-45"></div>
|
||||
<div class="absolute" style="left:90%;top:88%;"><img src="/static/images/book.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:90%;top:88%;"><img src="/static/images/calc.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:5%;top:89%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-22"></div>
|
||||
<div class="absolute" style="left:42%;top:92%;"><p class="text-green-600 rotate-6">7,840.00</p></div>
|
||||
<div class="absolute" style="left:13%;top:91%;"><img src="/static/images/calc.png" width="50px" class="rotate-18"></div>
|
||||
|
||||
<!-- row ~95% -->
|
||||
<div class="absolute" style="left:10%;top:96%;"><img src="/static/images/coffe.png" width="50px" class="rotate-30"></div>
|
||||
<div class="absolute" style="left:30%;top:97%;"><img src="/static/images/book.png" width="50px" class="-rotate-20"></div>
|
||||
<div class="absolute" style="left:50%;top:95%;"><img src="/static/images/mac.png" width="50px" class="rotate-15"></div>
|
||||
<div class="absolute" style="left:67%;top:98%;"><img src="/static/images/keyboard.png" width="50px" class="-rotate-35"></div>
|
||||
<div class="absolute" style="left:82%;top:96%;"><p class="text-red-500 rotate-22">-1,130.00</p></div>
|
||||
<div class="absolute" style="left:93%;top:95%;"><img src="/static/images/books.png" width="50px" class="rotate-10"></div>
|
||||
@@ -0,0 +1,161 @@
|
||||
|
||||
<style>
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@300;400;500&display=swap');
|
||||
.eq-wrap { font-family: 'IBM Plex Sans', monospace; padding: 2rem 0 1rem; }
|
||||
.eq-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.25rem; }
|
||||
.eq-title { font-family: 'IBM Plex Mono', monospace; font-size: 11px; font-weight: 500; letter-spacing: 0.12em; color: var(--color-text-secondary); text-transform: uppercase; }
|
||||
.eq-ytd { font-family: 'IBM Plex Mono', monospace; font-size: 22px; font-weight: 500; color: var(--color-text-primary); margin: 0.1rem 0 1.5rem; }
|
||||
.eq-ytd span { font-size: 13px; font-weight: 400; color: #1D9E75; margin-left: 8px; }
|
||||
.eq-divider { border: none; border-top: 0.5px solid var(--color-border-tertiary); margin-bottom: 1.5rem; }
|
||||
.eq-legend { display: flex; gap: 20px; margin-bottom: 0.75rem; font-size: 12px; font-family: 'IBM Plex Mono', monospace; color: var(--color-text-secondary); align-items: center; }
|
||||
.eq-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 1.5rem; }
|
||||
.eq-stat { background: var(--color-background-secondary); border-radius: var(--border-radius-md); padding: 0.75rem 1rem; }
|
||||
.eq-stat-label { font-size: 11px; font-family: 'IBM Plex Mono', monospace; color: var(--color-text-secondary); letter-spacing: 0.05em; margin-bottom: 4px; }
|
||||
.eq-stat-val { font-size: 16px; font-weight: 500; font-family: 'IBM Plex Mono', monospace; }
|
||||
.eq-stat-val.pos { color: #1D9E75; }
|
||||
.eq-stat-val.neg { color: #E24B4A; }
|
||||
.eq-note { font-size: 11px; color: var(--color-text-secondary); margin-top: 1rem; font-family: 'IBM Plex Mono', monospace; }
|
||||
</style>
|
||||
|
||||
<div class="eq-wrap">
|
||||
<p class="eq-title">Equity · Portfolio Performance</p>
|
||||
<div class="eq-header">
|
||||
<div class="eq-ytd">+18.61% <span>YTD</span></div>
|
||||
<span style="font-size:11px; font-family:'IBM Plex Mono',monospace; color:var(--color-text-secondary);">2026</span>
|
||||
</div>
|
||||
<hr class="eq-divider">
|
||||
|
||||
<div class="eq-legend">
|
||||
<span style="display:flex;align-items:center;gap:6px;">
|
||||
<span style="width:16px;height:2px;background:#1D9E75;border-radius:1px;display:inline-block;"></span>cumulative
|
||||
</span>
|
||||
<span style="display:flex;align-items:center;gap:6px;">
|
||||
<span style="width:16px;height:10px;background:rgba(29,158,117,0.25);border:1px solid #1D9E75;border-radius:2px;display:inline-block;"></span>monthly
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style="position:relative; width:100%; height:220px;">
|
||||
<canvas id="eqChart" role="img" aria-label="Line chart of cumulative and monthly equity returns. Cumulative YTD is plus 18.61%.">
|
||||
Monthly: Jan +7.75%, Feb −11.71%, Mar +0.91%, Apr +6.67%, May +3.13%, Jun −0.04%, Jul +2.66%, Now +9.45%. Cumulative: Jan +7.75%, Feb −4.87%, Mar −4.00%, Apr +2.40%, May +5.60%, Jun +5.56%, Jul +8.37%, Now +18.61%.
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<div class="eq-stats">
|
||||
<div class="eq-stat">
|
||||
<div class="eq-stat-label">best month</div>
|
||||
<div class="eq-stat-val pos">+9.45%</div>
|
||||
</div>
|
||||
<div class="eq-stat">
|
||||
<div class="eq-stat-label">worst month</div>
|
||||
<div class="eq-stat-val neg">−11.71%</div>
|
||||
</div>
|
||||
<div class="eq-stat">
|
||||
<div class="eq-stat-label">cumulative YTD</div>
|
||||
<div class="eq-stat-val pos">+18.61%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="eq-note">// data through 2026 YTD · monthly returns & cumulative</p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
const monthly = [7.75, -11.71, 0.91, 6.67, 3.13, -0.04, 2.66, 9.45];
|
||||
const cumul = [7.75, -4.87, -4.00, 2.40, 5.60, 5.56, 8.37, 18.61];
|
||||
const labels = ['jan','feb','mar','apr','may','jun','jul','now'];
|
||||
|
||||
function initChart() {
|
||||
const isDark = matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
const gridColor = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.06)';
|
||||
const textColor = isDark ? 'rgba(255,255,255,0.45)' : 'rgba(0,0,0,0.45)';
|
||||
const zeroColor = isDark ? 'rgba(255,255,255,0.2)' : 'rgba(0,0,0,0.15)';
|
||||
|
||||
new Chart(document.getElementById('eqChart'), {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
type: 'line',
|
||||
label: 'cumulative',
|
||||
data: cumul,
|
||||
borderColor: '#1D9E75',
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#1D9E75',
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6,
|
||||
tension: 0.35,
|
||||
fill: { target: 'origin', above: 'rgba(29,158,117,0.08)', below: 'rgba(226,75,74,0.08)' },
|
||||
yAxisID: 'y',
|
||||
order: 1
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
label: 'monthly',
|
||||
data: monthly,
|
||||
backgroundColor: monthly.map(v => v >= 0 ? 'rgba(29,158,117,0.25)' : 'rgba(226,75,74,0.25)'),
|
||||
borderColor: monthly.map(v => v >= 0 ? '#1D9E75' : '#E24B4A'),
|
||||
borderWidth: 1,
|
||||
borderRadius: 3,
|
||||
yAxisID: 'y',
|
||||
order: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: isDark ? '#2C2C2A' : '#fff',
|
||||
borderColor: isDark ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.1)',
|
||||
borderWidth: 1,
|
||||
titleColor: textColor,
|
||||
bodyColor: isDark ? 'rgba(255,255,255,0.8)' : 'rgba(0,0,0,0.8)',
|
||||
titleFont: { family: "'IBM Plex Mono', monospace", size: 11 },
|
||||
bodyFont: { family: "'IBM Plex Mono', monospace", size: 12 },
|
||||
padding: 10,
|
||||
callbacks: {
|
||||
label: ctx => {
|
||||
const sign = ctx.parsed.y >= 0 ? '+' : '';
|
||||
return ` ${ctx.dataset.label}: ${sign}${ctx.parsed.y.toFixed(2)}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
grid: { display: false },
|
||||
border: { display: false },
|
||||
ticks: { color: textColor, font: { family: "'IBM Plex Mono', monospace", size: 11 }, autoSkip: false }
|
||||
},
|
||||
y: {
|
||||
grid: {
|
||||
color: ctx => ctx.tick.value === 0 ? zeroColor : gridColor,
|
||||
lineWidth: ctx => ctx.tick.value === 0 ? 1 : 0.5
|
||||
},
|
||||
border: { display: false },
|
||||
ticks: {
|
||||
color: textColor,
|
||||
font: { family: "'IBM Plex Mono', monospace", size: 11 },
|
||||
callback: v => (v >= 0 ? '+' : '') + v.toFixed(1) + '%'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof Chart !== 'undefined') {
|
||||
initChart();
|
||||
} else {
|
||||
var s = document.createElement('script');
|
||||
s.src = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.js';
|
||||
s.onload = initChart;
|
||||
document.head.appendChild(s);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 1.5rem; padding: 20px;">
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/minecraft.webp" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">Minecraft</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/Tombraider.png" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">Rise of the Tomb Raider</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/planetzoo.webp" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">Planet Zoo</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/gtav.webp" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">GTA V</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/factorio.jpg" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">Factorio</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-direction: column; align-items: center; gap: 0.5rem;">
|
||||
<img src="/static/gamesbanners/detroitbecomehuman.jpg" height="100" width="190" style="object-fit: cover; border-radius: 8px;">
|
||||
<p style="margin: 0; text-align: center;">Detroit: Become Human</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,20 @@
|
||||
<div class="row">
|
||||
<div class="toggle-wrap" role="switch" aria-checked="true" tabindex="0" id="toggle"
|
||||
aria-label="Toggle day/night mode">
|
||||
<div class="track day" id="track">
|
||||
<div class="stars">
|
||||
<div class="star"></div>
|
||||
<div class="star"></div>
|
||||
<div class="star"></div>
|
||||
<div class="star"></div>
|
||||
<div class="star"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="knob day" id="knob">
|
||||
<div class="sun-rays" id="rays"></div>
|
||||
<div class="moon-crater" style="width:4px;height:4px;top:3px;left:5px;"></div>
|
||||
<div class="moon-crater" style="width:3px;height:3px;top:9px;left:9px;"></div>
|
||||
<div class="moon-crater" style="width:2px;height:2px;top:4px;left:11px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
After Width: | Height: | Size: 6.8 MiB |
|
After Width: | Height: | Size: 331 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 734 KiB |
|
After Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 727 KiB |
|
After Width: | Height: | Size: 373 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 7.6 MiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M11 8.5L9.8 9L2.35172 12.3856C2.13752 12.4829 2 12.6965 2 12.9318V13.0682C2 13.3035 2.13752 13.5171 2.35172 13.6144L11.1724 17.6238C11.6982 17.8628 12.3018 17.8628 12.8276 17.6238L21.6483 13.6144C21.8625 13.5171 22 13.3035 22 13.0682V12.9318C22 12.6965 21.8625 12.4829 21.6483 12.3856L14.2 9L13 8.5" stroke="#000000" stroke-width="1.5"></path><path d="M22 13V17.112C22 17.3482 21.8615 17.5623 21.6462 17.6592L12.8207 21.6307C12.2988 21.8655 11.7012 21.8655 11.1793 21.6307L2.35378 17.6592C2.13847 17.5623 2 17.3482 2 17.112V13" stroke="#000000" stroke-width="1.5"></path><path d="M12 8C10.3431 8 9 6.65685 9 5C9 3.34315 10.3431 2 12 2C13.6569 2 15 3.34315 15 5C15 6.65685 13.6569 8 12 8Z" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11 8V13C11 13.5523 11.4477 14 12 14V14C12.5523 14 13 13.5523 13 13V8" stroke="#000000" stroke-width="1.5"></path><path d="M16 13H17" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" viewBox="0 0 24 24" stroke-width="1.5" fill="none" xmlns="http://www.w3.org/2000/svg" color="#000000"><path d="M17.5 19.1245C15.9791 20.3003 14.0712 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 17.5 15 17.5 15 14C15 12 15 8 15 8" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 12V10.5C15 9.11929 13.6569 8 12 8C10.3431 8 9 9.11929 9 10.5V12" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 12V13.5C15 14.8807 13.6569 16 12 16C10.3431 16 9 14.8807 9 13.5V12" stroke="#000000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
|
After Width: | Height: | Size: 780 B |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#aa00ff"><path d="M3 11.5066C3 16.7497 7.25034 21 12.4934 21C16.2209 21 19.4466 18.8518 21 15.7259C12.4934 15.7259 8.27411 11.5066 8.27411 3C5.14821 4.55344 3 7.77915 3 11.5066Z" stroke="#aa00ff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
|
After Width: | Height: | Size: 438 B |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 632 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><svg width="24px" height="24px" stroke-width="1.5" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" color="#ffff00"><path d="M12 18C15.3137 18 18 15.3137 18 12C18 8.68629 15.3137 6 12 6C8.68629 6 6 8.68629 6 12C6 15.3137 8.68629 18 12 18Z" stroke="#ffd043" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M22 12L23 12" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 2V1" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 23V22" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M20 20L19 19" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M20 4L19 5" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M4 20L5 19" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M4 4L5 5" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M1 12L2 12" stroke="#ffff00" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 131 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 544 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 142 KiB |
|
After Width: | Height: | Size: 537 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 533 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 798 KiB |
|
After Width: | Height: | Size: 122 KiB |
@@ -0,0 +1,656 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Serif:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Rubik:ital,wght@0,300..900;1,300..900&display=swap');
|
||||
|
||||
.ibm-plex-serif-thin {
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 100;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.ibm-plex-serif-extralight {
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 200;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.ibm-plex-serif-light {
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.ibm-plex-serif-regular {
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.ibm-plex-serif-medium {
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.theme-root.night {
|
||||
--bg: #1c1c1c;
|
||||
--surface: #252525;
|
||||
--surface2: #2a2a2a;
|
||||
--border: #363636;
|
||||
--border2: #ccc7be;
|
||||
--text: #ffffff;
|
||||
--muted: #d9d9d9;
|
||||
--dim: #b0aa9f;
|
||||
--navback: rgba(28, 28, 28, 0.8);
|
||||
--accent: #5389ff;
|
||||
--accent2:#1447b8;
|
||||
--green:#0eaf74;
|
||||
--amber:#ffd78e;
|
||||
--accent-dim: rgba(93, 167, 202, 0.12);
|
||||
--accent-border: rgba(93, 102, 202, 0.25);
|
||||
|
||||
}
|
||||
|
||||
.theme-root {
|
||||
--bg:#f7f6f3;
|
||||
--surface:#ffffff;
|
||||
--surface2:#f0eeea;
|
||||
--border:#e2ddd6;
|
||||
--border2:#ccc7be;
|
||||
--text:#1a1917;
|
||||
--muted:#6b6560;
|
||||
--dim:#b0aa9f;
|
||||
--accent:#1a56db;
|
||||
--accent2:#1447b8;
|
||||
--green:#0a7c52;
|
||||
--amber:#92620a;
|
||||
--navback: rgba(247,246,243,0.8)
|
||||
--ff-mono:'IBM Plex Mono',monospace;
|
||||
--ff-sans:'IBM Plex Sans',sans-serif;
|
||||
--accent-dim: rgba(93, 167, 202, 0.12);
|
||||
--accent-border: rgba(93, 102, 202, 0.25);
|
||||
|
||||
}
|
||||
|
||||
*{box-sizing:border-box;margin:0;padding:0;}
|
||||
html{font-size:15px;scroll-behavior:smooth;}
|
||||
body{
|
||||
background:var(--bg);
|
||||
color:var(--text);
|
||||
font-family:var(--ff-sans);
|
||||
font-weight:300;
|
||||
line-height:1.7;
|
||||
min-height:100vh;
|
||||
}
|
||||
|
||||
/* subtle dot grid */
|
||||
body::before{
|
||||
content:'';
|
||||
position:fixed;inset:0;
|
||||
background-image:radial-gradient(circle, var(--border) 1px, transparent 1px);
|
||||
background-size:24px 24px;
|
||||
pointer-events:none;z-index:0;
|
||||
}
|
||||
|
||||
nav{
|
||||
display: flex;
|
||||
background: var(--navback);
|
||||
backdrop-filter:blur(10px);
|
||||
border-bottom:1px solid var(--border);
|
||||
padding:0 clamp(1rem,5vw,3rem);
|
||||
height:52px;
|
||||
}
|
||||
.nav-logo{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;font-weight:500;
|
||||
color:var(--accent);text-decoration:none;
|
||||
display:flex;align-items:center;gap:6px;
|
||||
}
|
||||
.nav-logo::before{content:'>';color:var(--green);font-weight:500;}
|
||||
.nav-logo.sub::before{content:'/';color:var(--green);font-weight:500;}
|
||||
.nav-logo.back::before{content:'<-';color:var(--green);font-weight:500;}
|
||||
|
||||
|
||||
.nav-links{display:flex;gap:0.2rem;align-items:center;}
|
||||
.nav-links a{
|
||||
font-family:var(--ff-mono);font-size:0.72rem;
|
||||
color:var(--muted);text-decoration:none;
|
||||
padding:0.3rem 0.7rem;border-radius:4px;
|
||||
transition:color 0.15s,background 0.15s;
|
||||
}
|
||||
.nav-links a:hover{color:var(--text);background:var(--surface2);}
|
||||
.nav-links .cta{color:var(--accent);border:1px solid var(--accent);margin-left:0.4rem;}
|
||||
.nav-links .cta:hover{background:var(--accent2);color:#fff;}
|
||||
|
||||
.shell{
|
||||
position:relative;z-index:1;
|
||||
display:grid;
|
||||
grid-template-columns:220px 1fr;
|
||||
min-height:calc(100vh - 52px);
|
||||
max-width:1100px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
|
||||
aside{
|
||||
border-right:1px solid var(--border);
|
||||
padding:2.5rem 1.5rem;
|
||||
display:flex;flex-direction:column;gap:1rem;
|
||||
position:sticky;top:52px;height:calc(100vh - 52px);
|
||||
overflow-y:auto;
|
||||
background:var(--surface);
|
||||
}
|
||||
.sidebar-label{
|
||||
font-family:var(--ff-mono);font-size:0.65rem;
|
||||
letter-spacing:0.2em;text-transform:uppercase;
|
||||
color:var(--dim);margin-bottom:0.8rem;
|
||||
}
|
||||
.sidebar-links{display:flex;flex-direction:column;gap:2px;}
|
||||
.sidebar-links a{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;color:var(--muted);
|
||||
text-decoration:none;padding:0.3rem 0.6rem;border-radius:4px;
|
||||
transition:color 0.15s,background 0.15s;
|
||||
display:flex;align-items:center;gap:6px;
|
||||
}
|
||||
.sidebar-links a::before{content:'//';color:var(--dim);font-size:0.65rem;}
|
||||
.sidebar-links.back a::before{content:'<-';color:var(--dim);font-size:0.65rem;}
|
||||
.sidebar-links a:hover{color:var(--text);background:var(--surface2);}
|
||||
.skill-list{display:flex;flex-direction:column;gap:4px;}
|
||||
.skill-item{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;
|
||||
color:var(--muted);padding:0.3rem 0.6rem;
|
||||
border-left:2px solid var(--border2);
|
||||
transition:border-color 0.15s,color 0.15s;cursor:default;
|
||||
}
|
||||
.skill-item:hover{border-color:var(--accent);color:var(--text);}
|
||||
|
||||
main{
|
||||
padding:3rem clamp(1.5rem,4vw,3rem);
|
||||
display:flex;flex-direction:column;gap:4rem;
|
||||
}
|
||||
|
||||
.hero-eyebrow{
|
||||
font-family:var(--ff-mono);font-size:0.72rem;color:var(--green);
|
||||
letter-spacing:0.18em;margin-bottom:1rem;
|
||||
display:flex;align-items:center;gap:8px;
|
||||
}
|
||||
.hero-eyebrow::before{content:'';display:block;width:20px;height:1px;background:var(--green);}
|
||||
h1{
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 300;
|
||||
font-style: normal;
|
||||
font-size: 4rem;
|
||||
letter-spacing:-0.02em;line-height:1.1;
|
||||
}
|
||||
h1 span{color:var(--accent);}
|
||||
.hero-bio{
|
||||
margin-top:1.2rem;font-size:0.9rem;color:var(--muted);
|
||||
max-width:560px;line-height:1.8;
|
||||
}
|
||||
.hero-meta{
|
||||
margin-top:1.5rem;display:flex;gap:1.5rem;flex-wrap:wrap;
|
||||
font-family:var(--ff-mono);font-size:0.72rem;color:var(--dim);
|
||||
}
|
||||
.hero-meta span{display:flex;align-items:center;gap:5px;}
|
||||
|
||||
@keyframes pulse{0%,100%{opacity:1;}50%{opacity:0.35;}}
|
||||
|
||||
.section-head{display:flex;align-items:center;gap:10px;margin-bottom:1.8rem;}
|
||||
.section-head h2{
|
||||
font-family:var(--ff-mono);font-size:0.72rem;font-weight:400;
|
||||
letter-spacing:0.25em;text-transform:uppercase;color:var(--muted);
|
||||
white-space:nowrap;
|
||||
}
|
||||
.section-head::after{content:'';flex:1;height:1px;background:var(--border);}
|
||||
|
||||
.exp-grid{display:flex;flex-direction:column;gap:8px;}
|
||||
.exp-card{
|
||||
background:var(--surface);border:1px solid var(--border);
|
||||
border-radius:6px;padding:1.4rem 1.5rem;
|
||||
transition:border-color 0.2s,box-shadow 0.2s;
|
||||
max-width: 95%;
|
||||
}
|
||||
.exp-card:hover{border-color:var(--border2);box-shadow:0 2px 12px rgba(0,0,0,0.06);}
|
||||
|
||||
.exp-header{
|
||||
display:flex;align-items:baseline;justify-content:space-between;
|
||||
margin-bottom:0.6rem;flex-wrap:wrap;gap:0.5rem;
|
||||
}
|
||||
|
||||
.exp-card.link:hover{
|
||||
border-color:var(--accent);
|
||||
}
|
||||
|
||||
.exp-flex {
|
||||
display: block;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
@media (min-width: 800px) {
|
||||
.exp-flex {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.exp-title{
|
||||
font-family: "IBM Plex Serif", serif;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
color:var(--text);}
|
||||
|
||||
|
||||
.exp-tag{
|
||||
font-family:var(--ff-mono);font-size:0.65rem;
|
||||
letter-spacing:0.1em;text-transform:uppercase;
|
||||
background:rgba(26,86,219,0.07);color:var(--accent);
|
||||
border:1px solid rgba(26,86,219,0.2);
|
||||
padding:2px 8px;border-radius:3px;
|
||||
}
|
||||
|
||||
.exp-tag.green{
|
||||
color: green;
|
||||
background:rgba(61, 219, 26, 0.07);
|
||||
border:1px solid rgba(38, 103, 13, 0.2);
|
||||
}
|
||||
|
||||
.exp-tag.red{
|
||||
color: red;
|
||||
background:rgba(219, 26, 26, 0.07);
|
||||
border:1px solid rgba(219, 26, 26, 0.2);
|
||||
}
|
||||
|
||||
|
||||
.exp-body{font-size:0.85rem;color:var(--muted);line-height:1.8;}
|
||||
|
||||
.edu-grid{display:flex;flex-direction:column;gap:8px; max-width: 95%;}
|
||||
.edu-card{
|
||||
display:grid;grid-template-columns:80px 1fr;gap:1.2rem;
|
||||
background:var(--surface);border:1px solid var(--border);
|
||||
border-radius:6px;padding:1.2rem 1.4rem;
|
||||
transition:border-color 0.2s,box-shadow 0.2s;
|
||||
}
|
||||
.edu-card:hover{border-color:var(--border2);box-shadow:0 2px 12px rgba(0,0,0,0.06);}
|
||||
.edu-year{font-family:var(--ff-mono);font-size:0.7rem;color:var(--green);padding-top:2px;line-height:1.6;}
|
||||
.edu-degree{font-size:0.9rem;font-weight:500;color:var(--text);}
|
||||
.edu-inst{font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);margin-top:2px;}
|
||||
.edu-detail{font-size:0.82rem;color:var(--dim);margin-top:4px;}
|
||||
|
||||
.lang-row{display:flex;gap:0.6rem;flex-wrap:wrap;}
|
||||
.lang-badge{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;color:var(--text);
|
||||
background:var(--surface);border:1px solid var(--border);
|
||||
padding:0.35rem 0.9rem;border-radius:4px;
|
||||
}
|
||||
|
||||
.interests-row{display:flex;gap:0.6rem;flex-wrap:wrap;}
|
||||
.interest-chip{
|
||||
font-family:var(--ff-mono);font-size:0.75rem;color:var(--muted);
|
||||
background:var(--surface);border:1px solid var(--border);
|
||||
padding:0.3rem 0.75rem;border-radius:4px;
|
||||
}
|
||||
.interest-chip::before{content:'# ';color:var(--dim);}
|
||||
|
||||
.opinion-grid{display:flex;flex-wrap:wrap;gap:0.6rem;}
|
||||
.opinion-link{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;
|
||||
color:var(--muted);text-decoration:none;
|
||||
background:var(--surface);border:1px solid var(--border);
|
||||
padding:0.4rem 0.9rem;border-radius:4px;
|
||||
display:flex;align-items:center;gap:6px;
|
||||
transition:border-color 0.2s,color 0.2s,box-shadow 0.2s;
|
||||
}
|
||||
.opinion-link::before{content:'→';color:var(--dim);font-size:0.75rem;}
|
||||
.opinion-link:hover{border-color:var(--accent2);color:var(--text);box-shadow:0 2px 8px rgba(0,0,0,0.06);}
|
||||
|
||||
footer{
|
||||
position:relative;z-index:1;
|
||||
border-top:1px solid var(--border);
|
||||
padding:1.4rem clamp(1rem,5vw,3rem);
|
||||
display:flex;align-items:center;justify-content:space-between;
|
||||
flex-wrap:wrap;gap:0.5rem;
|
||||
background:var(--surface);
|
||||
height: 100px;
|
||||
}
|
||||
.footer-contact a{font-family:var(--ff-mono);font-size:0.78rem;color:var(--accent);text-decoration:none;}
|
||||
.footer-contact a:hover{text-decoration:underline;}
|
||||
.footer-copy{font-family:var(--ff-mono);font-size:0.7rem;color:var(--dim);}
|
||||
|
||||
@keyframes fadeUp{from{opacity:0;transform:translateY(10px);}to{opacity:1;transform:translateY(0);}}
|
||||
.hero{animation:fadeUp 0.6s ease both;}
|
||||
.exp-card:nth-child(1){animation:fadeUp 0.5s 0.05s ease both;}
|
||||
.exp-card:nth-child(2){animation:fadeUp 0.5s 0.12s ease both;}
|
||||
.edu-card:nth-child(1){animation:fadeUp 0.5s 0.08s ease both;}
|
||||
.edu-card:nth-child(2){animation:fadeUp 0.5s 0.15s ease both;}
|
||||
|
||||
@media(max-width:700px){
|
||||
.shell{grid-template-columns:1fr;}
|
||||
aside{display:none;}
|
||||
.edu-card{grid-template-columns:1fr;gap:0.3rem;}
|
||||
}
|
||||
|
||||
.edu-tag {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
font-family: 'DM Mono', monospace;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
padding: 3px 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.tag-dropped {
|
||||
background: var(--surface2);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
|
||||
.toplist-ranked li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
counter-increment: rank;
|
||||
}
|
||||
|
||||
.toplist-ranked {
|
||||
counter-reset: rank;
|
||||
}
|
||||
|
||||
.toplist-ranked li::before {
|
||||
content: counter(rank);
|
||||
font-family: 'DM Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
min-width: 16px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.toplist-ranked li img {
|
||||
width: 42px;
|
||||
height: 62px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.toplist-toggle {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.poster-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
max-width: 90%;
|
||||
}
|
||||
|
||||
.poster-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 120px;
|
||||
}
|
||||
|
||||
.poster-item img {
|
||||
width: 120px;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.poster-item p {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.game-grid {
|
||||
justify-content: center;
|
||||
gap: 1.5rem;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.game-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-family: var(--ff-mono);
|
||||
font-size: clamp(1rem, 4vw, 2rem);
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.page-title::before {
|
||||
content: '> ';
|
||||
color: var(--green);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.page-sub {
|
||||
font-family: var(--ff-mono);
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0.08em;
|
||||
padding-left: 1.1rem;
|
||||
}
|
||||
|
||||
|
||||
.posterhover:hover img {
|
||||
transform: scale(1.07);
|
||||
}
|
||||
|
||||
|
||||
#rain-canvas {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
z-index: 999;
|
||||
opacity: 0;
|
||||
transition: opacity 0.8s ease;
|
||||
}
|
||||
|
||||
#dragon-grid {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
z-index: 999;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
|
||||
.outlink {font-family:var(--ff-mono);font-size:0.78rem;color:var(--accent);text-decoration:none;}
|
||||
.outlink:hover{text-decoration:underline;}
|
||||
|
||||
a{
|
||||
font-family:var(--ff-mono);font-size:0.78rem;color:var(--muted);
|
||||
text-decoration:none;padding:0.3rem 0.6rem;border-radius:4px;
|
||||
transition:color 0.15s,background 0.15s;
|
||||
display:flex;align-items:center;gap:6px;
|
||||
}
|
||||
|
||||
|
||||
.rabbit-svg path { fill: gray; transition: fill 0.3s ease; }
|
||||
.rabbit-svg:hover path { fill: #7F77DD; }
|
||||
|
||||
|
||||
|
||||
|
||||
.scene {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 2rem 1rem;
|
||||
gap: 1rem;
|
||||
font-family: var(--font-sans);
|
||||
}
|
||||
|
||||
.label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.03em;
|
||||
transition: color 0.4s ease;
|
||||
user-select: none;
|
||||
}
|
||||
.label.day-label { color: #B8860B; }
|
||||
.label.night-label { color: #9090d0; }
|
||||
.state-label {
|
||||
font-size: 11px;
|
||||
color: var(--color-text-secondary);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.copy-block {
|
||||
background: var(--color-background-secondary);
|
||||
border: 0.5px solid var(--color-border-tertiary);
|
||||
border-radius: var(--border-radius-lg);
|
||||
padding: 1rem 1.25rem;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
}
|
||||
.copy-block p { margin: 0 0 8px; font-size: 13px; color: var(--color-text-secondary); }
|
||||
.copy-block code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--color-text-primary);
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.copy-btn {
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
padding: 4px 10px;
|
||||
border-radius: var(--border-radius-md);
|
||||
cursor: pointer;
|
||||
border: 0.5px solid var(--color-border-secondary);
|
||||
background: transparent;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
.copy-btn:hover { background: var(--color-background-secondary); }
|
||||
|
||||
.Hitchhiker {
|
||||
max-width: 480px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
display: none;
|
||||
}
|
||||
.matrix-hidden {
|
||||
display: none;
|
||||
color: green;
|
||||
font-family: var(--ff-mono);
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.08em;
|
||||
padding-left: 1.1rem;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
.dot--available { background: var(--green); }
|
||||
.dot--busy { background: red; }
|
||||
|
||||
/* styles2.css */
|
||||
.overlay {
|
||||
position: fixed;
|
||||
inset: 0; /* top/right/bottom/left: 0 */
|
||||
background: rgba(0, 0, 0, 0.6); /* semi-transparent dark */
|
||||
z-index: 100;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
button {
|
||||
font-family:var(--ff-mono);font-size:0.65rem;
|
||||
letter-spacing:0.1em;text-transform:uppercase;
|
||||
background:rgba(26,86,219,0.07);color:var(--accent);
|
||||
border:1px solid rgba(26,86,219,0.2);
|
||||
padding:2px 8px;border-radius:3px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border:1px solid rgb(26, 87, 219);
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
section .cta {
|
||||
margin-top: 3rem;
|
||||
padding: 2rem;
|
||||
border: 0.5px solid var(--accent-border);
|
||||
border-radius: 8px;
|
||||
background: var(--accent-dim);
|
||||
}
|
||||
|
||||
.cta-hed {
|
||||
font-family: var(--mono);
|
||||
font-size: 13px;
|
||||
color: var(--accent);
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.cta-body {
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
margin-bottom: 1.25rem;
|
||||
line-height: 1.65;
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
display: inline-block;
|
||||
font-family: var(--mono);
|
||||
font-size: 12px;
|
||||
color: var(--bg);
|
||||
background: var(--accent);
|
||||
border: none;
|
||||
padding: 9px 20px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.cta-btn:hover { opacity: 0.85; }
|
||||
.available {
|
||||
background:rgba(65, 219, 26, 0.07);color:var(--green);
|
||||
border:1px solid var(--green);
|
||||
padding:2px 8px;border-radius:3px;
|
||||
}
|
||||
|
||||
.busy {
|
||||
background:rgba(219, 26, 26, 0.07);color:var(--red);
|
||||
border:1px solid var(--red);
|
||||
padding:2px 8px;border-radius:3px;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
= Top 5 Movies
|
||||
1. Blade Runner 2049
|
||||
2. A cure of Wellness
|
||||
3. Fight Club
|
||||
4. Dracula: A Love Tale
|
||||
5. Girl with a Dragon Tattoo
|
||||
|
||||
|
||||
= Movies
|
||||
- Tenet
|
||||
- The Outrun
|
||||
- Hail Mary
|
||||
- Day Break
|
||||
- l, Tonya (2018)
|
||||
- Caddo lake (2024)
|
||||
- The Shawshank Redemption
|
||||
- Fight Club
|
||||
- Perl
|
||||
- A Cure of Wellness, (2016)
|
||||
- green book
|
||||
- spirited away
|
||||
- Saltburn
|
||||
- Dracula: A Love Tale (2025)
|
||||
- Frankenstein (2015)
|
||||
- The Fall Guy
|
||||
- The Notebook
|
||||
- Primal Fear
|
||||
- blade runner 2049
|
||||
- girl with a dragon tatto
|
||||
- Wicked
|
||||
- Matrix
|
||||
- Book of Eli
|
||||
- Pulp Fiction
|
||||
- Midsommer
|
||||
- Living
|
||||
- Moneyball
|
||||
- Legally Blonde (2001)
|
||||
- Dune
|
||||
- Internal sunshine
|
||||
|
||||
|
||||
== Danish
|
||||
- A royal affiar
|
||||
- Adams æbler
|
||||
|
||||
= Top 5 Shows
|
||||
1. Chernobyl
|
||||
2. Succesion
|
||||
3. Breaking Bad
|
||||
4. Avatar, the last air bender
|
||||
5. Adolescence
|
||||
|
||||
= Shows
|
||||
- succesion
|
||||
- Breaking bad
|
||||
- Adolescence (2025)
|
||||
- The penguin
|
||||
- Community
|
||||
- Avatar, the last air bender
|
||||
- Foundation
|
||||
- Lessons in Chemistry
|
||||
- Hanna
|
||||
- You
|
||||
- killing eve
|
||||
- ozark
|
||||
- Chernobyl
|
||||
- The wire
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 710 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 929 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 939 KiB |
|
After Width: | Height: | Size: 624 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
@@ -0,0 +1,742 @@
|
||||
function cureClick() {
|
||||
if (document.getElementById('cure-canvas')) return;
|
||||
if (document.getElementById('lightning-canvas')) stopLightning();
|
||||
if (document.getElementById('typewriter-canvas')) stopTypewriter();
|
||||
if (document.getElementById('matrix-canvas')) stopMatrix();
|
||||
if (document.getElementById('rain-canvas')) stopRain();
|
||||
|
||||
const el = document.getElementById('interest');
|
||||
el.textContent = "Actually... I'm feeling much better now!"
|
||||
startWater();
|
||||
isDay = false;
|
||||
makenight();
|
||||
setTimeout(() => {
|
||||
el.textContent = 'interest';
|
||||
stopWater();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
let cureCanvas, cureCtx, cureAnimFrame;
|
||||
|
||||
function startWater() {
|
||||
cureCanvas = document.createElement('canvas');
|
||||
cureCanvas.id = 'cure-canvas';
|
||||
cureCanvas.width = window.innerWidth;
|
||||
cureCanvas.height = window.innerHeight;
|
||||
Object.assign(cureCanvas.style, {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
pointerEvents: 'none',
|
||||
zIndex: -1, // behind everything
|
||||
opacity: '0',
|
||||
transition: 'opacity 1.4s ease'
|
||||
});
|
||||
document.body.appendChild(cureCanvas);
|
||||
cureCtx = cureCanvas.getContext('2d');
|
||||
requestAnimationFrame(() => { cureCanvas.style.opacity = '1'; });
|
||||
|
||||
const W = cureCanvas.width;
|
||||
const H = cureCanvas.height;
|
||||
|
||||
// waterline sits at 38% from the top
|
||||
const waterlineY = H * 0.38;
|
||||
|
||||
// floating particles below the surface
|
||||
const particles = Array.from({ length: 55 }, () => ({
|
||||
x: Math.random() * W,
|
||||
y: waterlineY + 20 + Math.random() * (H - waterlineY - 20),
|
||||
r: 1 + Math.random() * 2.5,
|
||||
speedX: (Math.random() - 0.5) * 0.3,
|
||||
speedY: -0.08 - Math.random() * 0.18, // slow upward drift
|
||||
opacity: 0.08 + Math.random() * 0.18
|
||||
}));
|
||||
|
||||
function drawFrame(ts) {
|
||||
cureCtx.clearRect(0, 0, W, H);
|
||||
|
||||
// ── sky above waterline (very subtle, mostly transparent) ──────────
|
||||
const sky = cureCtx.createLinearGradient(0, 0, 0, waterlineY);
|
||||
sky.addColorStop(0, 'rgba(190, 230, 255, 0.06)');
|
||||
sky.addColorStop(1, 'rgba(190, 230, 255, 0.01)');
|
||||
cureCtx.fillStyle = sky;
|
||||
cureCtx.fillRect(0, 0, W, waterlineY);
|
||||
|
||||
// ── water body below waterline ─────────────────────────────────────
|
||||
const water = cureCtx.createLinearGradient(0, waterlineY, 0, H);
|
||||
water.addColorStop(0, 'rgba(80, 190, 220, 0.28)');
|
||||
water.addColorStop(0.35, 'rgba(40, 140, 190, 0.22)');
|
||||
water.addColorStop(0.7, 'rgba(20, 90, 150, 0.20)');
|
||||
water.addColorStop(1, 'rgba(10, 50, 110, 0.22)');
|
||||
cureCtx.fillStyle = water;
|
||||
cureCtx.fillRect(0, waterlineY, W, H - waterlineY);
|
||||
|
||||
// ── caustic shimmer streaks ────────────────────────────────────────
|
||||
const numStreaks = 10;
|
||||
for (let i = 0; i < numStreaks; i++) {
|
||||
const phase = (ts * 0.0004 + i * 0.63) % 1;
|
||||
const x = (i / numStreaks) * W + Math.sin(ts * 0.0008 + i) * 60;
|
||||
const yTop = waterlineY + 10 + Math.sin(ts * 0.001 + i * 1.3) * 18;
|
||||
const yBot = yTop + 40 + Math.sin(ts * 0.0007 + i * 0.9) * 30;
|
||||
const streak = cureCtx.createLinearGradient(x, yTop, x + 6, yBot);
|
||||
streak.addColorStop(0, 'rgba(200, 245, 255, 0)');
|
||||
streak.addColorStop(0.4, `rgba(200, 245, 255, ${0.05 + 0.06 * Math.sin(ts * 0.002 + i)})`);
|
||||
streak.addColorStop(1, 'rgba(200, 245, 255, 0)');
|
||||
cureCtx.fillStyle = streak;
|
||||
cureCtx.fillRect(x, yTop, 5, yBot - yTop);
|
||||
}
|
||||
|
||||
// ── floating particles ─────────────────────────────────────────────
|
||||
particles.forEach(p => {
|
||||
p.x += p.speedX;
|
||||
p.y += p.speedY;
|
||||
// reset when reaching the surface
|
||||
if (p.y < waterlineY + 5) {
|
||||
p.y = H - 10;
|
||||
p.x = Math.random() * W;
|
||||
}
|
||||
if (p.x < 0) p.x = W;
|
||||
if (p.x > W) p.x = 0;
|
||||
|
||||
const depth = (p.y - waterlineY) / (H - waterlineY); // 0 = surface, 1 = bottom
|
||||
cureCtx.beginPath();
|
||||
cureCtx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
|
||||
cureCtx.fillStyle = `rgba(180, 240, 255, ${p.opacity * (0.4 + 0.6 * (1 - depth))})`;
|
||||
cureCtx.fill();
|
||||
});
|
||||
|
||||
// ── animated surface waves (2 layers, different speeds & amplitudes) ─
|
||||
function drawWaveLine(amp, freq, speed, lineW, alpha) {
|
||||
cureCtx.beginPath();
|
||||
for (let x = 0; x <= W; x += 3) {
|
||||
const y = waterlineY
|
||||
+ Math.sin(x * freq + ts * speed) * amp
|
||||
+ Math.sin(x * freq * 1.7 + ts * speed * 0.6) * (amp * 0.4);
|
||||
x === 0 ? cureCtx.moveTo(x, y) : cureCtx.lineTo(x, y);
|
||||
}
|
||||
cureCtx.strokeStyle = `rgba(210, 245, 255, ${alpha})`;
|
||||
cureCtx.lineWidth = lineW;
|
||||
cureCtx.stroke();
|
||||
}
|
||||
|
||||
// primary wave
|
||||
drawWaveLine(7, 0.012, 0.0018, 2.0, 0.70);
|
||||
// secondary wave (slightly offset phase)
|
||||
drawWaveLine(4, 0.018, 0.0024, 1.2, 0.40);
|
||||
// micro ripple
|
||||
drawWaveLine(2, 0.030, 0.0032, 0.7, 0.25);
|
||||
|
||||
// ── foam/highlight on the crest ────────────────────────────────────
|
||||
cureCtx.beginPath();
|
||||
for (let x = 0; x <= W; x += 3) {
|
||||
const y = waterlineY
|
||||
+ Math.sin(x * 0.012 + ts * 0.0018) * 7
|
||||
+ Math.sin(x * 0.020 + ts * 0.0024) * 3;
|
||||
x === 0 ? cureCtx.moveTo(x, y - 1) : cureCtx.lineTo(x, y - 1);
|
||||
}
|
||||
cureCtx.strokeStyle = 'rgba(255, 255, 255, 0.18)';
|
||||
cureCtx.lineWidth = 1;
|
||||
cureCtx.stroke();
|
||||
|
||||
if (cureCanvas) cureAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
cureAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function stopWater() {
|
||||
if (!cureCanvas) return;
|
||||
cureCanvas.style.opacity = '0';
|
||||
cancelAnimationFrame(cureAnimFrame);
|
||||
setTimeout(() => {
|
||||
cureCanvas.remove();
|
||||
cureCanvas = null;
|
||||
}, 1400);
|
||||
}
|
||||
|
||||
function draculaClick() {
|
||||
if (document.getElementById('lightning-canvas')) return;
|
||||
|
||||
if (document.getElementById('cure-canvas')) stopWater();
|
||||
if (document.getElementById('typewriter-canvas')) stopTypewriter();
|
||||
if (document.getElementById('matrix-canvas')) stopMatrix();
|
||||
if (document.getElementById('rain-canvas')) stopRain();
|
||||
|
||||
const el = document.getElementById('interest');
|
||||
el.textContent = 'You have your whole life ahead of you. And I only offer death.';
|
||||
startLightning();
|
||||
isDay = false;
|
||||
makenight();
|
||||
setTimeout(() => {
|
||||
el.textContent = 'interest';
|
||||
stopLightning();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
let lightningCanvas, lightningCtx, lightningInterval, lightningFlashTimeout;
|
||||
|
||||
function startLightning() {
|
||||
lightningCanvas = document.createElement('canvas');
|
||||
lightningCanvas.id = 'lightning-canvas';
|
||||
lightningCanvas.width = window.innerWidth;
|
||||
lightningCanvas.height = window.innerHeight;
|
||||
Object.assign(lightningCanvas.style, {
|
||||
position: 'fixed', top: 0, left: 0,
|
||||
pointerEvents: 'none', zIndex: 9999,
|
||||
opacity: '0', transition: 'opacity 0.3s ease'
|
||||
});
|
||||
document.body.appendChild(lightningCanvas);
|
||||
lightningCtx = lightningCanvas.getContext('2d');
|
||||
requestAnimationFrame(() => { lightningCanvas.style.opacity = '1'; });
|
||||
|
||||
function drawBolt(x1, y1, x2, y2, roughness, depth) {
|
||||
if (depth === 0) {
|
||||
lightningCtx.beginPath();
|
||||
lightningCtx.moveTo(x1, y1);
|
||||
lightningCtx.lineTo(x2, y2);
|
||||
lightningCtx.stroke();
|
||||
return;
|
||||
}
|
||||
const mx = (x1 + x2) / 2 + (Math.random() - 0.5) * roughness;
|
||||
const my = (y1 + y2) / 2 + (Math.random() - 0.5) * roughness;
|
||||
drawBolt(x1, y1, mx, my, roughness / 2, depth - 1);
|
||||
drawBolt(mx, my, x2, y2, roughness / 2, depth - 1);
|
||||
// random fork branch
|
||||
if (depth > 2 && Math.random() < 0.4) {
|
||||
const fx = mx + (Math.random() - 0.3) * roughness * 2;
|
||||
const fy = my + Math.random() * roughness * 2;
|
||||
lightningCtx.save();
|
||||
lightningCtx.globalAlpha *= 0.5;
|
||||
drawBolt(mx, my, fx, fy, roughness / 2, depth - 2);
|
||||
lightningCtx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
function flashStrike() {
|
||||
lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height);
|
||||
|
||||
const numBolts = 1 + Math.floor(Math.random() * 3);
|
||||
for (let i = 0; i < numBolts; i++) {
|
||||
const startX = lightningCanvas.width * (0.1 + Math.random() * 0.8);
|
||||
const endX = startX + (Math.random() - 0.5) * 300;
|
||||
const color = Math.random() < 0.6
|
||||
? `rgba(220, 180, 255, ${0.6 + Math.random() * 0.4})` // purple
|
||||
: `rgba(255, 255, 255, ${0.7 + Math.random() * 0.3})`; // white
|
||||
|
||||
lightningCtx.strokeStyle = color;
|
||||
lightningCtx.lineWidth = 0.5 + Math.random() * 2;
|
||||
lightningCtx.shadowColor = color;
|
||||
lightningCtx.shadowBlur = 18 + Math.random() * 30;
|
||||
lightningCtx.globalAlpha = 1;
|
||||
|
||||
drawBolt(startX, 0, endX, lightningCanvas.height * (0.4 + Math.random() * 0.5), 120, 7);
|
||||
}
|
||||
|
||||
// ambient flash on the body
|
||||
document.body.style.transition = 'background 0.05s';
|
||||
document.body.style.background = `rgba(80, 0, 120, 0.08)`;
|
||||
setTimeout(() => { document.body.style.background = ''; }, 80);
|
||||
|
||||
// fade the bolt out
|
||||
lightningFlashTimeout = setTimeout(() => {
|
||||
lightningCtx.clearRect(0, 0, lightningCanvas.width, lightningCanvas.height);
|
||||
}, 80 + Math.random() * 120);
|
||||
}
|
||||
|
||||
// irregular strike cadence — like real lightning
|
||||
function scheduleStrike() {
|
||||
flashStrike();
|
||||
const delay = 600 + Math.random() * 2200;
|
||||
lightningInterval = setTimeout(scheduleStrike, delay);
|
||||
}
|
||||
scheduleStrike();
|
||||
}
|
||||
|
||||
function stopLightning() {
|
||||
if (!lightningCanvas) return;
|
||||
lightningCanvas.style.opacity = '0';
|
||||
clearTimeout(lightningInterval);
|
||||
clearTimeout(lightningFlashTimeout);
|
||||
setTimeout(() => {
|
||||
lightningCanvas.remove();
|
||||
lightningCanvas = null;
|
||||
}, 800);
|
||||
}
|
||||
|
||||
|
||||
function dragonTypewriterClick() {
|
||||
if (document.getElementById('typewriter-canvas')) return;
|
||||
if (document.getElementById('lightning-canvas')) stopLightning();
|
||||
if (document.getElementById('cure-canvas')) stopWater();
|
||||
if (document.getElementById('matrix-canvas')) stopMatrix();
|
||||
if (document.getElementById('rain-canvas')) stopRain();
|
||||
|
||||
const el = document.getElementById('interest');
|
||||
el.textContent = 'case file 36609...';
|
||||
startTypewriter();
|
||||
isDay = false;
|
||||
makenight();
|
||||
setTimeout(() => {
|
||||
el.textContent = 'interest';
|
||||
stopTypewriter();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
let twCanvas, twCtx, twAnimFrame;
|
||||
|
||||
function startTypewriter() {
|
||||
twCanvas = document.createElement('canvas');
|
||||
twCanvas.id = 'typewriter-canvas';
|
||||
twCanvas.width = window.innerWidth;
|
||||
twCanvas.height = window.innerHeight;
|
||||
Object.assign(twCanvas.style, {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
pointerEvents: 'none',
|
||||
zIndex: -1,
|
||||
opacity: '0',
|
||||
transition: 'opacity 1.2s ease'
|
||||
});
|
||||
document.body.appendChild(twCanvas);
|
||||
twCtx = twCanvas.getContext('2d');
|
||||
requestAnimationFrame(() => { twCanvas.style.opacity = '1'; });
|
||||
|
||||
const W = twCanvas.width;
|
||||
const H = twCanvas.height;
|
||||
|
||||
// ── case file fragments ──────────────────────────────────────────────
|
||||
const fragments = [
|
||||
'SUBJECT: HARRIET VANGER',
|
||||
'CASE NO. 36609-74',
|
||||
'CLASSIFICATION: MISSING / PRESUMED',
|
||||
'████████████ DECEASED ████████████',
|
||||
'LAST SEEN: HEDEBY ISLAND',
|
||||
'NOVEMBER 1, 1966',
|
||||
'INVESTIGATING OFFICER: ██████████',
|
||||
'FINANCIAL IRREGULARITIES NOTED',
|
||||
'SEE ATTACHMENT ██-C',
|
||||
'WENNERSTROM GROUP — CLASSIFIED',
|
||||
'DO NOT DISTRIBUTE',
|
||||
'SOURCE: CONFIDENTIAL',
|
||||
'SALANDER, L. — AUTHORIZED',
|
||||
'BLOMKVIST, M. — AUTHORIZED',
|
||||
'ALL OTHERS: ACCESS DENIED',
|
||||
'████████████████████████████',
|
||||
'MOTIVE: UNKNOWN',
|
||||
'SUSPECT LIST: REDACTED',
|
||||
'EVIDENCE SEALED BY ORDER OF',
|
||||
'████████ COURT ████████',
|
||||
'TRAUMA INDICATED / SEE REPORT 7',
|
||||
'LISBETH — CLEARANCE LEVEL 3',
|
||||
'THE DRAGON TATTOO',
|
||||
'——————————————————————',
|
||||
'WHERE IS SHE.',
|
||||
'WHO KNOWS.',
|
||||
'FILE CLOSED: ██/██/████',
|
||||
];
|
||||
|
||||
// glitch chars pool
|
||||
const glitchChars = '!@#$%&*?|\\/<>[]{}~^░▒▓█▄▀■□▪▫';
|
||||
function randGlitch() {
|
||||
return glitchChars[Math.floor(Math.random() * glitchChars.length)];
|
||||
}
|
||||
|
||||
// ── active lines on screen ───────────────────────────────────────────
|
||||
const lines = [];
|
||||
let fragmentPool = [...fragments];
|
||||
|
||||
function pickFragment() {
|
||||
if (!fragmentPool.length) fragmentPool = [...fragments];
|
||||
const i = Math.floor(Math.random() * fragmentPool.length);
|
||||
return fragmentPool.splice(i, 1)[0];
|
||||
}
|
||||
|
||||
function spawnLine() {
|
||||
const text = pickFragment();
|
||||
const fontSize = 11 + Math.floor(Math.random() * 9);
|
||||
const x = 40 + Math.random() * (W * 0.55);
|
||||
const y = 60 + Math.random() * (H - 120);
|
||||
const isRedact = text.includes('█') || Math.random() < 0.12;
|
||||
const speed = 28 + Math.random() * 22; // chars per second
|
||||
|
||||
lines.push({
|
||||
text,
|
||||
x, y,
|
||||
fontSize,
|
||||
typed: 0, // chars revealed so far (float)
|
||||
speed,
|
||||
glitching: false,
|
||||
glitchTimer: 0,
|
||||
glitchStr: '',
|
||||
opacity: 0.72 + Math.random() * 0.26,
|
||||
isRedact,
|
||||
done: false,
|
||||
holdTimer: 0,
|
||||
fadeOut: false,
|
||||
fadeAlpha: 1,
|
||||
stamp: Math.random() < 0.08, // rare CLASSIFIED/DENIED stamp
|
||||
});
|
||||
}
|
||||
|
||||
// spawn cadence
|
||||
spawnLine();
|
||||
const spawnTimer = setInterval(() => {
|
||||
if (lines.length < 14) spawnLine();
|
||||
}, 600);
|
||||
twCanvas._spawnTimer = spawnTimer;
|
||||
|
||||
let lastTs = null;
|
||||
|
||||
function drawFrame(ts) {
|
||||
if (!lastTs) lastTs = ts;
|
||||
const dt = (ts - lastTs) / 1000;
|
||||
lastTs = ts;
|
||||
|
||||
twCtx.clearRect(0, 0, W, H);
|
||||
|
||||
// faint scanline overlay — gives it that CRT/photocopy feel
|
||||
for (let sy = 0; sy < H; sy += 3) {
|
||||
twCtx.fillStyle = 'rgba(0,0,0,0.04)';
|
||||
twCtx.fillRect(0, sy, W, 1);
|
||||
}
|
||||
|
||||
lines.forEach((line, idx) => {
|
||||
twCtx.font = `${line.fontSize}px "Courier New", Courier, monospace`;
|
||||
|
||||
if (line.fadeOut) {
|
||||
line.fadeAlpha -= dt * 0.6;
|
||||
if (line.fadeAlpha <= 0) {
|
||||
lines.splice(idx, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const alpha = line.opacity * (line.fadeOut ? line.fadeAlpha : 1);
|
||||
|
||||
if (!line.done) {
|
||||
line.typed += line.speed * dt;
|
||||
if (line.typed >= line.text.length) {
|
||||
line.typed = line.text.length;
|
||||
line.done = true;
|
||||
}
|
||||
|
||||
// random mid-type glitch burst
|
||||
if (!line.glitching && Math.random() < 0.004) {
|
||||
line.glitching = true;
|
||||
line.glitchTimer = 0.18 + Math.random() * 0.22;
|
||||
line.glitchStr = Array.from(
|
||||
{ length: 3 + Math.floor(Math.random() * 5) },
|
||||
randGlitch
|
||||
).join('');
|
||||
}
|
||||
} else {
|
||||
// after typing finishes, hold then fade
|
||||
line.holdTimer += dt;
|
||||
if (line.holdTimer > 3.5 + Math.random() * 3) {
|
||||
line.fadeOut = true;
|
||||
// spawn a replacement
|
||||
setTimeout(spawnLine, 200 + Math.random() * 600);
|
||||
}
|
||||
|
||||
// occasional re-glitch on finished lines
|
||||
if (!line.glitching && Math.random() < 0.003) {
|
||||
line.glitching = true;
|
||||
line.glitchTimer = 0.1 + Math.random() * 0.15;
|
||||
line.glitchStr = Array.from(
|
||||
{ length: 2 + Math.floor(Math.random() * 4) },
|
||||
randGlitch
|
||||
).join('');
|
||||
}
|
||||
}
|
||||
|
||||
// resolve glitch burst
|
||||
if (line.glitching) {
|
||||
line.glitchTimer -= dt;
|
||||
if (line.glitchTimer <= 0) {
|
||||
line.glitching = false;
|
||||
line.glitchStr = '';
|
||||
}
|
||||
}
|
||||
|
||||
// ── build visible string ───────────────────────────────────────
|
||||
const visible = line.text.slice(0, Math.floor(line.typed));
|
||||
let display = visible;
|
||||
|
||||
// blinking cursor at the type head
|
||||
if (!line.done) {
|
||||
const blink = Math.floor(ts / 530) % 2 === 0;
|
||||
display += blink ? '▌' : ' ';
|
||||
}
|
||||
|
||||
// append glitch burst after cursor
|
||||
if (line.glitching) display += line.glitchStr;
|
||||
|
||||
// ── color choice ───────────────────────────────────────────────
|
||||
// redacted blocks stay white-ish; normal text is the faded amber/grey
|
||||
const isRedactLine = line.text.includes('█');
|
||||
if (isRedactLine) {
|
||||
twCtx.fillStyle = `rgba(220, 215, 200, ${alpha})`;
|
||||
} else if (line.stamp) {
|
||||
twCtx.fillStyle = `rgba(180, 30, 30, ${alpha * 0.85})`;
|
||||
} else {
|
||||
// slight warm/cool variation per line
|
||||
const warm = Math.random() < 0.5;
|
||||
twCtx.fillStyle = warm
|
||||
? `rgba(210, 200, 180, ${alpha})`
|
||||
: `rgba(185, 200, 195, ${alpha})`;
|
||||
}
|
||||
|
||||
twCtx.fillText(display, line.x, line.y);
|
||||
|
||||
// ── underline for "stamp" lines ────────────────────────────────
|
||||
if (line.stamp && line.done) {
|
||||
const mw = twCtx.measureText(line.text).width;
|
||||
twCtx.strokeStyle = `rgba(180, 30, 30, ${alpha * 0.6})`;
|
||||
twCtx.lineWidth = 1;
|
||||
twCtx.beginPath();
|
||||
twCtx.moveTo(line.x, line.y + 3);
|
||||
twCtx.lineTo(line.x + mw, line.y + 3);
|
||||
twCtx.stroke();
|
||||
}
|
||||
});
|
||||
|
||||
if (twCanvas) twAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
twAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function stopTypewriter() {
|
||||
if (!twCanvas) return;
|
||||
twCanvas.style.opacity = '0';
|
||||
clearInterval(twCanvas._spawnTimer);
|
||||
cancelAnimationFrame(twAnimFrame);
|
||||
setTimeout(() => {
|
||||
twCanvas.remove();
|
||||
twCanvas = null;
|
||||
}, 1200);
|
||||
}
|
||||
|
||||
function matrixClick() {
|
||||
if (document.getElementById('matrix-canvas')) return;
|
||||
|
||||
if (document.getElementById('cure-canvas')) stopWater();
|
||||
if (document.getElementById('lightning-canvas')) stopLightning();
|
||||
if (document.getElementById('typewriter-canvas')) stopTypewriter();
|
||||
if (document.getElementById('rain-canvas')) stopRain();
|
||||
|
||||
const el = document.getElementById('interest');
|
||||
el.textContent = 'wake up, neo...';
|
||||
startMatrix();
|
||||
isDay = false;
|
||||
makenight();
|
||||
setTimeout(() => {
|
||||
el.textContent = 'interest';
|
||||
stopMatrix();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
let matrixCanvas, matrixCtx, matrixAnimFrame, matrixStopping = false;
|
||||
|
||||
function startMatrix() {
|
||||
matrixCanvas = document.createElement('canvas');
|
||||
matrixCanvas.id = 'matrix-canvas';
|
||||
matrixCanvas.width = window.innerWidth;
|
||||
matrixCanvas.height = window.innerHeight;
|
||||
Object.assign(matrixCanvas.style, {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
pointerEvents: 'none',
|
||||
zIndex: -1,
|
||||
opacity: '0',
|
||||
transition: 'opacity 1.2s ease'
|
||||
});
|
||||
document.body.appendChild(matrixCanvas);
|
||||
matrixCtx = matrixCanvas.getContext('2d');
|
||||
requestAnimationFrame(() => { matrixCanvas.style.opacity = '1'; });
|
||||
|
||||
const W = matrixCanvas.width;
|
||||
const H = matrixCanvas.height;
|
||||
|
||||
const FONT_SIZE = 16;
|
||||
const COLS = Math.floor(W / FONT_SIZE);
|
||||
|
||||
// katakana unicode block ァ–ン (U+30A1–U+30F6)
|
||||
const KATA_START = 0x30A1;
|
||||
const KATA_END = 0x30F6;
|
||||
function randKata() {
|
||||
return String.fromCharCode(
|
||||
KATA_START + Math.floor(Math.random() * (KATA_END - KATA_START + 1))
|
||||
);
|
||||
}
|
||||
|
||||
// each column: how far down it has dripped (in rows)
|
||||
// start negative so columns begin off-screen at staggered times
|
||||
const drops = Array.from({ length: COLS }, () => ({
|
||||
y: -(Math.random() * 50), // staggered start above top
|
||||
speed: 0.1 + Math.random() * 0.2,
|
||||
glyphs: [] // trail of { char, age }
|
||||
}));
|
||||
|
||||
const TRAIL_LEN = 14; // how many glyphs in the tail
|
||||
|
||||
function drawFrame() {
|
||||
// dim the previous frame — this creates the fade trail
|
||||
matrixCtx.fillStyle = 'rgba(0, 0, 0, 0.10)';
|
||||
matrixCtx.fillRect(0, 0, W, H);
|
||||
|
||||
matrixCtx.font = `${FONT_SIZE}px monospace`;
|
||||
|
||||
drops.forEach((drop, col) => {
|
||||
const x = col * FONT_SIZE;
|
||||
|
||||
// advance drop
|
||||
drop.y += drop.speed;
|
||||
|
||||
// push a new glyph at the head
|
||||
if (drop.y >= 0) {
|
||||
drop.glyphs.unshift({ char: randKata(), age: 0 });
|
||||
if (drop.glyphs.length > TRAIL_LEN) drop.glyphs.pop();
|
||||
}
|
||||
|
||||
// age each glyph and draw
|
||||
drop.glyphs.forEach((g, i) => {
|
||||
g.age++;
|
||||
// randomly mutate the character occasionally
|
||||
if (Math.random() < 0.04) g.char = randKata();
|
||||
|
||||
const headY = Math.floor(drop.y) - i;
|
||||
if (headY < 0 || headY * FONT_SIZE > H) return;
|
||||
|
||||
const t = i / TRAIL_LEN; // 0 = head, 1 = tail
|
||||
|
||||
if (i === 0) {
|
||||
// head glyph — bright white flash
|
||||
matrixCtx.fillStyle = `rgba(200, 255, 220, 0.98)`;
|
||||
} else if (i < 3) {
|
||||
// near-head — bright green
|
||||
matrixCtx.fillStyle = `rgba(100, 255, 140, ${1 - t * 0.3})`;
|
||||
} else {
|
||||
// tail — fade to dark green
|
||||
const alpha = Math.max(0, 1 - t * 1.1);
|
||||
const green = Math.floor(180 - t * 120);
|
||||
matrixCtx.fillStyle = `rgba(0, ${green}, 60, ${alpha})`;
|
||||
}
|
||||
|
||||
matrixCtx.fillText(g.char, x, headY * FONT_SIZE);
|
||||
});
|
||||
|
||||
// reset column when it scrolls fully off the bottom
|
||||
// reset column when it scrolls fully off the bottom
|
||||
if (drop.y * FONT_SIZE > H + TRAIL_LEN * FONT_SIZE) {
|
||||
if (matrixStopping) {
|
||||
// don't recycle — park it off-screen so the trail dies
|
||||
drop.y = -(TRAIL_LEN * 10);
|
||||
drop.glyphs = [];
|
||||
} else {
|
||||
drop.y = -(Math.random() * 30);
|
||||
drop.speed = 0.3 + Math.random() * 0.7;
|
||||
drop.glyphs = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (matrixCanvas) matrixAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
matrixAnimFrame = requestAnimationFrame(drawFrame);
|
||||
}
|
||||
|
||||
function stopMatrix() {
|
||||
if (!matrixCanvas) return;
|
||||
|
||||
// signal: no new heads, let existing trails die out
|
||||
matrixStopping = true;
|
||||
|
||||
// after trails have had time to fade, pull the canvas
|
||||
setTimeout(() => {
|
||||
cancelAnimationFrame(matrixAnimFrame);
|
||||
matrixCanvas.style.opacity = '0';
|
||||
matrixCanvas.style.transition = 'opacity 1.2s ease';
|
||||
setTimeout(() => {
|
||||
matrixCanvas.remove();
|
||||
matrixCanvas = null;
|
||||
matrixStopping = false;
|
||||
}, 1200);
|
||||
}, 1800);
|
||||
}
|
||||
|
||||
|
||||
function bladerunnerClick() {
|
||||
if (document.getElementById('rain-canvas')) return; // already running, do nothing
|
||||
|
||||
if (document.getElementById('cure-canvas')) stopWater();
|
||||
if (document.getElementById('matrix-canvas')) stopMatrix();
|
||||
if (document.getElementById('typewriter-canvas')) stopTypewriter();
|
||||
if (document.getElementById('lightning-canvas')) stopLightning();
|
||||
|
||||
|
||||
const el = document.getElementById('interest');
|
||||
el.textContent = 'tears in rain';
|
||||
|
||||
startRain();
|
||||
|
||||
isDay = false
|
||||
makenight();
|
||||
|
||||
setTimeout(() => {
|
||||
el.textContent = "interest";
|
||||
stopRain();
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
let rainCanvas, rainCtx, rainInterval;
|
||||
|
||||
function startRain() {
|
||||
rainCanvas = document.createElement('canvas');
|
||||
rainCanvas.id = 'rain-canvas';
|
||||
rainCanvas.width = window.innerWidth;
|
||||
rainCanvas.height = window.innerHeight;
|
||||
document.body.appendChild(rainCanvas);
|
||||
rainCtx = rainCanvas.getContext('2d');
|
||||
|
||||
requestAnimationFrame(() => { rainCanvas.style.opacity = '1'; });
|
||||
|
||||
const drops = Array.from({ length: 120 }, () => ({
|
||||
x: Math.random() * rainCanvas.width,
|
||||
y: Math.random() * rainCanvas.height,
|
||||
speed: 8 + Math.random() * 7,
|
||||
length: 12 + Math.random() * 20,
|
||||
opacity: 0.2 + Math.random() * 0.4
|
||||
}));
|
||||
|
||||
rainInterval = setInterval(() => {
|
||||
rainCtx.clearRect(0, 0, rainCanvas.width, rainCanvas.height);
|
||||
drops.forEach(drop => {
|
||||
rainCtx.beginPath();
|
||||
rainCtx.moveTo(drop.x, drop.y);
|
||||
rainCtx.lineTo(drop.x - 2, drop.y + drop.length);
|
||||
rainCtx.strokeStyle = `rgba(174, 214, 241, ${drop.opacity})`;
|
||||
rainCtx.lineWidth = 3;
|
||||
rainCtx.stroke();
|
||||
|
||||
drop.y += drop.speed;
|
||||
if (drop.y > rainCanvas.height) {
|
||||
drop.y = -drop.length;
|
||||
drop.x = Math.random() * rainCanvas.width;
|
||||
}
|
||||
});
|
||||
}, 30);
|
||||
}
|
||||
|
||||
function stopRain() {
|
||||
if (!rainCanvas) return;
|
||||
rainCanvas.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
clearInterval(rainInterval);
|
||||
rainCanvas.remove();
|
||||
rainCanvas = null;
|
||||
}, 800);
|
||||
}
|
||||