redesign site as "case file" theme

- new paper/ink palette with single red accent, replacing catppuccin
- self-hosted IBM Plex Serif (display) + Source Serif 4 (body), drop Outfit
- rebuilt homepage masthead, numbered post index, navbar with active states
- switch syntax highlighting + utterances to gruvbox, synced via postMessage
- remove terminal/404 easter egg chrome, clean up remaining inline styles
- fix stray syntax.css link causing a 404
This commit is contained in:
2026-07-16 12:28:34 +03:00
parent 57462869ec
commit a51671d7e4
62 changed files with 2116 additions and 2806 deletions
+31 -22
View File
@@ -1,41 +1,50 @@
/* Outfit */
/* IBM Plex Serif — display/headings */
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-300.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-family: 'IBM Plex Serif';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-regular.woff2') format('woff2');
src: url('../fonts/ibm-plex-serif/ibm-plex-serif-v20-latin-regular.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-500.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-family: 'IBM Plex Serif';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-600.woff2') format('woff2');
src: url('../fonts/ibm-plex-serif/ibm-plex-serif-v20-latin-600.woff2') format('woff2');
}
@font-face {
font-family: 'Outfit';
font-family: 'IBM Plex Serif';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('../fonts/outfit/outfit-v15-latin-700.woff2') format('woff2');
src: url('../fonts/ibm-plex-serif/ibm-plex-serif-v20-latin-700.woff2') format('woff2');
}
/* JetBrains Mono (official release — full ligature support) */
/* Source Serif 4 — body copy */
@font-face {
font-family: 'Source Serif 4';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('../fonts/source-serif-4/source-serif-4-v14-latin-regular.woff2') format('woff2');
}
@font-face {
font-family: 'Source Serif 4';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url('../fonts/source-serif-4/source-serif-4-v14-latin-italic.woff2') format('woff2');
}
@font-face {
font-family: 'Source Serif 4';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url('../fonts/source-serif-4/source-serif-4-v14-latin-600.woff2') format('woff2');
}
/* JetBrains Mono (official release — full ligature support) — case-reference lines, tags, code */
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-108
View File
@@ -1,108 +0,0 @@
(function () {
'use strict';
var body = document.getElementById('term-body');
if (!body) return;
var path = window.location.pathname;
var promptUser = body.dataset.prompt || 'user@pwnbox';
var hostname = body.dataset.hostname || 'pwnbox';
var whoami = body.dataset.whoami || 'l0st_r3ader';
var links = [
{ href: body.dataset.linkHome, label: '~/home' },
{ href: body.dataset.linkPosts, label: '~/posts' },
{ href: body.dataset.linkTags, label: '~/tags' },
];
var steps = [
{ type: 'cmd', text: 'cd ' + path },
{ type: 'out-error', text: 'bash: cd: ' + path + ': No such file or directory' },
{ type: 'cmd', text: 'whoami' },
{ type: 'out', text: whoami },
{ type: 'cmd', text: 'ls ~/recovery/' },
{ type: 'ls' },
];
var CHAR_DELAY = 28;
var LINE_DELAY = 180;
function ps1() {
var s = document.createElement('span');
s.className = 'term-ps1';
s.textContent = promptUser + '@' + hostname + ':~$ ';
return s;
}
function appendCmdLine(text, callback) {
var row = document.createElement('div');
row.className = 'term-row';
row.appendChild(ps1());
var input = document.createElement('span');
input.className = 'term-input';
row.appendChild(input);
body.appendChild(row);
var i = 0;
function tick() {
if (i < text.length) {
input.textContent += text[i++];
setTimeout(tick, CHAR_DELAY);
} else {
setTimeout(callback, LINE_DELAY);
}
}
tick();
}
function appendOut(text, extraClass, callback) {
var div = document.createElement('div');
div.className = 'term-out' + (extraClass ? ' ' + extraClass : '');
div.textContent = text;
body.appendChild(div);
setTimeout(callback, LINE_DELAY);
}
function appendLs(callback) {
var div = document.createElement('div');
div.className = 'term-out-ls';
links.forEach(function (l) {
var a = document.createElement('a');
a.href = l.href;
a.textContent = l.label;
div.appendChild(a);
});
body.appendChild(div);
setTimeout(callback, LINE_DELAY);
}
function appendCursor() {
var row = document.createElement('div');
row.className = 'term-row';
row.appendChild(ps1());
var cur = document.createElement('span');
cur.className = 'term-cursor';
cur.setAttribute('aria-hidden', 'true');
row.appendChild(cur);
body.appendChild(row);
}
function runSteps(i) {
if (i >= steps.length) {
appendCursor();
return;
}
var step = steps[i];
var next = function () {
runSteps(i + 1);
};
if (step.type === 'cmd') appendCmdLine(step.text, next);
else if (step.type === 'out-error') appendOut(step.text, 'term-out-error', next);
else if (step.type === 'out') appendOut(step.text, 'term-out-muted', next);
else if (step.type === 'ls') appendLs(next);
else next();
}
runSteps(0);
})();
+25
View File
@@ -60,4 +60,29 @@ document.addEventListener('DOMContentLoaded', () => {
});
// Initial sync in case banner is already mounted
setTimeout(syncCCTheme, 100);
// Sync Utterances (comments iframe) theme with the site theme.
// The iframe only reads its initial `theme` attribute once, so later
// changes must go through postMessage per the utterances protocol.
const syncUtterancesTheme = () => {
const commentsEl = document.getElementById('comments');
const frame = document.querySelector('.utterances-frame');
if (!commentsEl || !frame) return;
const theme = htmlElement.classList.contains('dark')
? commentsEl.dataset.utterancesThemeDark
: commentsEl.dataset.utterancesThemeLight;
frame.contentWindow.postMessage({ type: 'set-theme', theme }, 'https://utteranc.es');
};
new MutationObserver(syncUtterancesTheme).observe(htmlElement, {
attributes: true,
attributeFilter: ['class'],
});
// The iframe loads asynchronously, so poll briefly until it's mounted.
let utterancesTries = 0;
const utterancesPoll = setInterval(() => {
if (document.querySelector('.utterances-frame') || ++utterancesTries > 20) {
clearInterval(utterancesPoll);
syncUtterancesTheme();
}
}, 250);
});