Update website themes and blog sections
Homelab Main / deploy (push) Successful in 1m24s
Details
Homelab Main / deploy (push) Successful in 1m24s
Details
This commit is contained in:
parent
73d512f13d
commit
285fbe5700
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Architecture Simulator</title>
|
<title>Architecture Simulator</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 04</p><h1>Interactive System Architecture Simulator</h1><p>Click-driven load, failure, and self-healing simulation for a tiny web stack.</p></section>
|
<section class="hero"><p class="kicker">Demo 04</p><h1>Interactive System Architecture Simulator</h1><p>Click-driven load, failure, and self-healing simulation for a tiny web stack.</p></section>
|
||||||
|
|
@ -16,5 +16,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="architecture-simulator.js"></script>
|
<script src="architecture-simulator.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Local Log and JSON Toolbelt</title>
|
<title>Local Log and JSON Toolbelt</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 03</p><h1>Local Log and JSON Toolbelt</h1><p>Format JSON, decode JWT payloads, parse URLs, and filter logs without sending data to a backend.</p></section>
|
<section class="hero"><p class="kicker">Demo 03</p><h1>Local Log and JSON Toolbelt</h1><p>Format JSON, decode JWT payloads, parse URLs, and filter logs without sending data to a backend.</p></section>
|
||||||
|
|
@ -17,5 +17,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="dev-toolbelt.js"></script>
|
<script src="dev-toolbelt.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Homelab Demo Artifacts</title>
|
<title>Homelab Demo Artifacts</title>
|
||||||
<link rel="stylesheet" href="shared.css">
|
<link rel="stylesheet" href="shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline">
|
<nav class="topline">
|
||||||
<a href="/demos.php">Back to website</a>
|
<a href="/demos.php">Back to website</a>
|
||||||
|
|
@ -29,5 +29,6 @@
|
||||||
<a class="catalog-card" href="model-drift/"><span>Demo 08</span><h2>Model Drift Simulator</h2><p>MLOps drift and retraining playground.</p></a>
|
<a class="catalog-card" href="model-drift/"><span>Demo 08</span><h2>Model Drift Simulator</h2><p>MLOps drift and retraining playground.</p></a>
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
<script src="theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Client-Side Media Cruncher</title>
|
<title>Client-Side Media Cruncher</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
|
|
@ -28,5 +28,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="media-cruncher.js"></script>
|
<script src="media-cruncher.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Model Drift Simulator</title>
|
<title>Model Drift Simulator</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 08</p><h1>Model Drift and Performance Simulator</h1><p>MLOps playground: create drift, corrupt inputs, and retrain back to a healthier curve.</p></section>
|
<section class="hero"><p class="kicker">Demo 08</p><h1>Model Drift and Performance Simulator</h1><p>MLOps playground: create drift, corrupt inputs, and retrain back to a healthier curve.</p></section>
|
||||||
|
|
@ -16,5 +16,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="model-drift.js"></script>
|
<script src="model-drift.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>How Is My Internet, Really?</title>
|
<title>How Is My Internet, Really?</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 02</p><h1>How Is My Internet, Really?</h1><p>Latency, jitter, and stability on a live Canvas graph. This version uses same-origin probes; a WebSocket echo endpoint is the production-grade path.</p></section>
|
<section class="hero"><p class="kicker">Demo 02</p><h1>How Is My Internet, Really?</h1><p>Latency, jitter, and stability on a live Canvas graph. This version uses same-origin probes; a WebSocket echo endpoint is the production-grade path.</p></section>
|
||||||
|
|
@ -16,5 +16,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="network-quality.js"></script>
|
<script src="network-quality.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Privacy-First Object Redactor</title>
|
<title>Privacy-First Object Redactor</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 06</p><h1>Privacy-First Object Redactor</h1><p>Drop an image, blur sensitive regions locally, and download the result. The future production path is onnxruntime-web plus a quantized detector in a worker.</p></section>
|
<section class="hero"><p class="kicker">Demo 06</p><h1>Privacy-First Object Redactor</h1><p>Drop an image, blur sensitive regions locally, and download the result. The future production path is onnxruntime-web plus a quantized detector in a worker.</p></section>
|
||||||
|
|
@ -17,5 +17,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="privacy-redactor.js"></script>
|
<script src="privacy-redactor.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<title>Local Sentiment Sandbox</title>
|
<title>Local Sentiment Sandbox</title>
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 07</p><h1>Local Sentiment and Text Analytics</h1><p>Prototype local sentiment, keywords, and summary. Production path: Transformers.js with a small ONNX model.</p></section>
|
<section class="hero"><p class="kicker">Demo 07</p><h1>Local Sentiment and Text Analytics</h1><p>Prototype local sentiment, keywords, and summary. Production path: Transformers.js with a small ONNX model.</p></section>
|
||||||
|
|
@ -16,5 +16,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="sentiment-sandbox.js"></script>
|
<script src="sentiment-sandbox.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,18 @@ body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.theme-dark {
|
||||||
|
background: #030603;
|
||||||
|
color: #a8ffb0;
|
||||||
|
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.theme-light {
|
||||||
|
background: #fff;
|
||||||
|
color: #111;
|
||||||
|
font-family: "Brush Script MT", "Segoe Script", "Snell Roundhand", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
@ -242,12 +254,132 @@ pre {
|
||||||
color: #102a43;
|
color: #102a43;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site-theme-toolbar {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option {
|
||||||
|
min-height: 32px;
|
||||||
|
border: 1px solid #9fb3c8;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 900;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option[data-site-theme="dark"] {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option[data-site-theme="light"] {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
font-family: "Brush Script MT", "Segoe Script", "Snell Roundhand", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option.is-active {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .topline a,
|
||||||
|
.theme-dark .kicker,
|
||||||
|
.theme-dark h1,
|
||||||
|
.theme-dark .catalog-card h2,
|
||||||
|
.theme-dark label,
|
||||||
|
.theme-dark .stats strong {
|
||||||
|
color: #c7ffd0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .hero p,
|
||||||
|
.theme-dark .note,
|
||||||
|
.theme-dark .catalog-card p,
|
||||||
|
.theme-dark .result-row span,
|
||||||
|
.theme-dark .stats span {
|
||||||
|
color: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .hero,
|
||||||
|
.theme-dark .panel,
|
||||||
|
.theme-dark .catalog-card,
|
||||||
|
.theme-dark .drop-zone,
|
||||||
|
.theme-dark .result-row,
|
||||||
|
.theme-dark .stats span {
|
||||||
|
background: #061106;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
box-shadow: 0 0 18px rgba(168, 255, 176, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark select,
|
||||||
|
.theme-dark input,
|
||||||
|
.theme-dark textarea {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark canvas {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .topline a,
|
||||||
|
.theme-light .kicker,
|
||||||
|
.theme-light h1,
|
||||||
|
.theme-light .catalog-card h2,
|
||||||
|
.theme-light label,
|
||||||
|
.theme-light .stats strong,
|
||||||
|
.theme-light .hero p,
|
||||||
|
.theme-light .note,
|
||||||
|
.theme-light .catalog-card p,
|
||||||
|
.theme-light .result-row span,
|
||||||
|
.theme-light .stats span {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .hero,
|
||||||
|
.theme-light .panel,
|
||||||
|
.theme-light .catalog-card,
|
||||||
|
.theme-light .drop-zone,
|
||||||
|
.theme-light .result-row,
|
||||||
|
.theme-light .stats span {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light select,
|
||||||
|
.theme-light input,
|
||||||
|
.theme-light textarea {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light canvas {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.topline {
|
.topline {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.site-theme-toolbar {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.result-row {
|
.result-row {
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
const demoThemeButtons = [];
|
||||||
|
const demoThemeStorageKey = 'site-theme';
|
||||||
|
|
||||||
|
function normalizeDemoTheme(theme) {
|
||||||
|
return theme === 'light' ? 'light' : 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoredDemoTheme() {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem(demoThemeStorageKey);
|
||||||
|
} catch (_error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeDemoTheme(theme) {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(demoThemeStorageKey, theme);
|
||||||
|
} catch (_error) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDemoTheme(theme) {
|
||||||
|
const nextTheme = normalizeDemoTheme(theme);
|
||||||
|
document.body.classList.toggle('theme-light', nextTheme === 'light');
|
||||||
|
document.body.classList.toggle('theme-dark', nextTheme === 'dark');
|
||||||
|
|
||||||
|
demoThemeButtons.forEach((button) => {
|
||||||
|
const active = button.dataset.siteTheme === nextTheme;
|
||||||
|
button.classList.toggle('is-active', active);
|
||||||
|
button.setAttribute('aria-pressed', active ? 'true' : 'false');
|
||||||
|
});
|
||||||
|
|
||||||
|
storeDemoTheme(nextTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
function installDemoThemeToolbar() {
|
||||||
|
const nav = document.querySelector('.topline');
|
||||||
|
if (!nav) return;
|
||||||
|
|
||||||
|
const toolbar = document.createElement('div');
|
||||||
|
toolbar.className = 'site-theme-toolbar';
|
||||||
|
toolbar.setAttribute('aria-label', 'Theme');
|
||||||
|
toolbar.innerHTML = '<span>Theme</span><button type="button" class="site-theme-option" data-site-theme="dark">Dark</button><span class="site-theme-separator" aria-hidden="true">|</span><button type="button" class="site-theme-option" data-site-theme="light">Light</button>';
|
||||||
|
nav.appendChild(toolbar);
|
||||||
|
|
||||||
|
demoThemeButtons.push(...toolbar.querySelectorAll('[data-site-theme]'));
|
||||||
|
demoThemeButtons.forEach((button) => {
|
||||||
|
button.addEventListener('click', () => setDemoTheme(button.dataset.siteTheme));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
installDemoThemeToolbar();
|
||||||
|
setDemoTheme(getStoredDemoTheme() || 'dark');
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="stylesheet" href="../shared.css">
|
<link rel="stylesheet" href="../shared.css">
|
||||||
<link rel="manifest" href="manifest.webmanifest">
|
<link rel="manifest" href="manifest.webmanifest">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
<nav class="topline"><a href="../">All demos</a><a href="/demos.php">Website catalog</a></nav>
|
||||||
<section class="hero"><p class="kicker">Demo 05</p><h1>Offline Traveler Converter</h1><p>Timezone, currency, and GB/GiB conversion in an installable PWA shell.</p></section>
|
<section class="hero"><p class="kicker">Demo 05</p><h1>Offline Traveler Converter</h1><p>Timezone, currency, and GB/GiB conversion in an installable PWA shell.</p></section>
|
||||||
|
|
@ -22,5 +22,6 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
<script src="traveler-tools.js"></script>
|
<script src="traveler-tools.js"></script>
|
||||||
|
<script src="../theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,10 @@ $stackSourceLinks = [
|
||||||
['label' => 'HAProxy template', 'path' => 'bootstrap/edge/templates/haproxy.cfg.tftpl'],
|
['label' => 'HAProxy template', 'path' => 'bootstrap/edge/templates/haproxy.cfg.tftpl'],
|
||||||
],
|
],
|
||||||
'blog_stack_7' => [
|
'blog_stack_7' => [
|
||||||
['label' => 'cv-theme.js', 'path' => 'apps/website/cv-theme.js'],
|
['label' => 'theme script', 'path' => 'apps/website/cv-theme.js'],
|
||||||
['label' => 'cv.php', 'path' => 'apps/website/cv.php'],
|
['label' => 'theme toolbar', 'path' => 'apps/website/partials/theme_toolbar.php'],
|
||||||
['label' => 'styles.css', 'path' => 'apps/website/styles.css'],
|
['label' => 'website CSS', 'path' => 'apps/website/styles.css'],
|
||||||
|
['label' => 'demo theme script', 'path' => 'apps/demos-static/public/theme.js'],
|
||||||
],
|
],
|
||||||
'blog_stack_8' => [
|
'blog_stack_8' => [
|
||||||
['label' => 'media-cruncher.js', 'path' => 'apps/demos-static/public/media-cruncher/media-cruncher.js'],
|
['label' => 'media-cruncher.js', 'path' => 'apps/demos-static/public/media-cruncher/media-cruncher.js'],
|
||||||
|
|
@ -156,7 +157,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<title><?php echo $text['blog_title']; ?> - <?php echo $text['name']; ?></title>
|
<title><?php echo $text['blog_title']; ?> - <?php echo $text['name']; ?></title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
|
|
||||||
<nav class="top-nav">
|
<nav class="top-nav">
|
||||||
<div class="nav-left">Juvenal Diaz</div>
|
<div class="nav-left">Juvenal Diaz</div>
|
||||||
|
|
@ -188,6 +189,8 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<?php require __DIR__ . '/partials/theme_toolbar.php'; ?>
|
||||||
|
|
||||||
<main class="blog-page">
|
<main class="blog-page">
|
||||||
<section class="blog-hero">
|
<section class="blog-hero">
|
||||||
<p class="blog-kicker"
|
<p class="blog-kicker"
|
||||||
|
|
@ -227,7 +230,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<div class="diagram-shell" aria-label="Professional homelab architecture diagram">
|
<div class="diagram-shell" aria-label="Professional homelab architecture diagram">
|
||||||
<svg class="homelab-map" viewBox="0 0 1120 960" role="img" aria-labelledby="homelab-map-title homelab-map-desc">
|
<svg class="homelab-map" viewBox="0 0 1120 960" role="img" aria-labelledby="homelab-map-title homelab-map-desc">
|
||||||
<title id="homelab-map-title">Homelab architecture map</title>
|
<title id="homelab-map-title">Homelab architecture map</title>
|
||||||
<desc id="homelab-map-desc">Git push enters Gitea, Gitea Actions validates and builds app images, OpenTofu manages the cluster and provisioning layers, Debian serves PXE and preseed content, Pimox builds Debian VM templates and worker clones on NVMe storage, OpenWrt can run as an opt-in firewall VM, Argo CD syncs manifests, and the OCI edge routes traffic into Kubernetes services.</desc>
|
<desc id="homelab-map-desc">Git push enters Gitea, Gitea Actions validates and builds app images, OpenTofu manages cluster and provisioning layers, Debian keeps the control plane and PXE services, Pimox app workers run Argo CD, Kyverno, and app workloads on NVMe-backed VMs, OpenWrt can run as an opt-in firewall VM, and the OCI edge routes traffic into Kubernetes services.</desc>
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="map-arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
<marker id="map-arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
|
||||||
<path d="M2,2 L10,6 L2,10 Z" fill="#2b6cb0"></path>
|
<path d="M2,2 L10,6 L2,10 Z" fill="#2b6cb0"></path>
|
||||||
|
|
@ -238,10 +241,10 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<text class="diagram-zone-title" x="46" y="74">Source, validation, and images</text>
|
<text class="diagram-zone-title" x="46" y="74">Source, validation, and images</text>
|
||||||
|
|
||||||
<rect class="diagram-zone diagram-zone-platform" x="382" y="40" width="340" height="880" rx="12"></rect>
|
<rect class="diagram-zone diagram-zone-platform" x="382" y="40" width="340" height="880" rx="12"></rect>
|
||||||
<text class="diagram-zone-title" x="404" y="74">Debian node 192.168.100.68</text>
|
<text class="diagram-zone-title" x="404" y="74">Control plane and provisioning</text>
|
||||||
|
|
||||||
<rect class="diagram-zone diagram-zone-runtime" x="770" y="40" width="326" height="880" rx="12"></rect>
|
<rect class="diagram-zone diagram-zone-runtime" x="770" y="40" width="326" height="880" rx="12"></rect>
|
||||||
<text class="diagram-zone-title" x="792" y="74">Edge access and workloads</text>
|
<text class="diagram-zone-title" x="792" y="74">Workers, edge, and workloads</text>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-blue" transform="translate(54 100)">
|
<g class="diagram-node node-accent-blue" transform="translate(54 100)">
|
||||||
<rect width="240" height="74" rx="8"></rect>
|
<rect width="240" height="74" rx="8"></rect>
|
||||||
|
|
@ -260,7 +263,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<rect width="240" height="82" rx="8"></rect>
|
<rect width="240" height="82" rx="8"></rect>
|
||||||
<text x="18" y="27">Gitea Actions runner</text>
|
<text x="18" y="27">Gitea Actions runner</text>
|
||||||
<text class="diagram-small" x="18" y="50">Debian hosted runner</text>
|
<text class="diagram-small" x="18" y="50">Debian hosted runner</text>
|
||||||
<text class="diagram-small" x="18" y="68">runs app-only deploys</text>
|
<text class="diagram-small" x="18" y="68">runs validated deploys</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-red" transform="translate(54 430)">
|
<g class="diagram-node node-accent-red" transform="translate(54 430)">
|
||||||
|
|
@ -294,8 +297,8 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<g class="diagram-node node-accent-blue" transform="translate(412 326)">
|
<g class="diagram-node node-accent-blue" transform="translate(412 326)">
|
||||||
<rect width="280" height="82" rx="8"></rect>
|
<rect width="280" height="82" rx="8"></rect>
|
||||||
<text x="18" y="27">kubeadm control plane</text>
|
<text x="18" y="27">kubeadm control plane</text>
|
||||||
<text class="diagram-small" x="18" y="50">API server, scheduler, controller</text>
|
<text class="diagram-small" x="18" y="50">API server and control loops</text>
|
||||||
<text class="diagram-small" x="18" y="68">Calico pod networking</text>
|
<text class="diagram-small" x="18" y="68">workloads pushed to app workers</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-teal" transform="translate(412 438)">
|
<g class="diagram-node node-accent-teal" transform="translate(412 438)">
|
||||||
|
|
@ -307,16 +310,16 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
|
|
||||||
<g class="diagram-node node-accent-green" transform="translate(412 550)">
|
<g class="diagram-node node-accent-green" transform="translate(412 550)">
|
||||||
<rect width="280" height="82" rx="8"></rect>
|
<rect width="280" height="82" rx="8"></rect>
|
||||||
<text x="18" y="27">Argo CD</text>
|
<text x="18" y="27">GitOps + policy controllers</text>
|
||||||
<text class="diagram-small" x="18" y="50">registry and monitoring</text>
|
<text class="diagram-small" x="18" y="50">Argo CD and Kyverno</text>
|
||||||
<text class="diagram-small" x="18" y="68">website and demos-static apps</text>
|
<text class="diagram-small" x="18" y="68">pinned to app workers</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-purple" transform="translate(412 662)">
|
<g class="diagram-node node-accent-purple" transform="translate(412 662)">
|
||||||
<rect width="280" height="82" rx="8"></rect>
|
<rect width="280" height="82" rx="8"></rect>
|
||||||
<text x="18" y="27">Monitoring stack</text>
|
<text x="18" y="27">Monitoring stack</text>
|
||||||
<text class="diagram-small" x="18" y="50">Prometheus, Grafana, Loki</text>
|
<text class="diagram-small" x="18" y="50">Prometheus, Grafana, Loki</text>
|
||||||
<text class="diagram-small" x="18" y="68">Mimir, Promtail, exporters</text>
|
<text class="diagram-small" x="18" y="68">Promtail, node-exporter, KSM</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-orange" transform="translate(412 774)">
|
<g class="diagram-node node-accent-orange" transform="translate(412 774)">
|
||||||
|
|
@ -344,17 +347,17 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<g class="diagram-node node-accent-green" transform="translate(800 330)">
|
<g class="diagram-node node-accent-green" transform="translate(800 330)">
|
||||||
<rect width="258" height="96" rx="8"></rect>
|
<rect width="258" height="96" rx="8"></rect>
|
||||||
<text x="18" y="28">Raspberry Pi 192.168.100.89</text>
|
<text x="18" y="28">Raspberry Pi 192.168.100.89</text>
|
||||||
<text class="diagram-small" x="18" y="52">arm64 Kubernetes worker</text>
|
<text class="diagram-small" x="18" y="52">external Gitea Docker service</text>
|
||||||
<text class="diagram-small" x="18" y="70">external Gitea Docker service</text>
|
<text class="diagram-small" x="18" y="70">optional edge-app worker</text>
|
||||||
<text class="diagram-small" x="18" y="88">website and demos pods</text>
|
<text class="diagram-small" x="18" y="88">repo home and backup source</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<g class="diagram-node node-accent-red" transform="translate(800 466)">
|
<g class="diagram-node node-accent-red" transform="translate(800 466)">
|
||||||
<rect width="258" height="112" rx="8"></rect>
|
<rect width="258" height="112" rx="8"></rect>
|
||||||
<text x="18" y="28">Orange Pi 5 Plus Pimox</text>
|
<text x="18" y="28">Orange Pi 5 Plus Pimox</text>
|
||||||
<text class="diagram-small" x="18" y="52">VM 9000 template on local</text>
|
<text class="diagram-small" x="18" y="52">pimox-worker app nodes</text>
|
||||||
<text class="diagram-small" x="18" y="70">workers on nvme_thin_pool</text>
|
<text class="diagram-small" x="18" y="70">workers on nvme_thin_pool</text>
|
||||||
<text class="diagram-small" x="18" y="88">OVMF, virtio-scsi, qemu agent</text>
|
<text class="diagram-small" x="18" y="88">Argo CD, Kyverno, apps</text>
|
||||||
<text class="diagram-small" x="18" y="106">idempotent qm automation</text>
|
<text class="diagram-small" x="18" y="106">idempotent qm automation</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
|
|
@ -371,7 +374,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<rect width="258" height="82" rx="8"></rect>
|
<rect width="258" height="82" rx="8"></rect>
|
||||||
<text x="18" y="27">Local registry :30500</text>
|
<text x="18" y="27">Local registry :30500</text>
|
||||||
<text class="diagram-small" x="18" y="50">php-website and demos-static</text>
|
<text class="diagram-small" x="18" y="50">php-website and demos-static</text>
|
||||||
<text class="diagram-small" x="18" y="68">pulled by arm64 workloads</text>
|
<text class="diagram-small" x="18" y="68">pulled by app workers</text>
|
||||||
</g>
|
</g>
|
||||||
|
|
||||||
<path class="diagram-link" d="M174 174 L174 208"></path>
|
<path class="diagram-link" d="M174 174 L174 208"></path>
|
||||||
|
|
@ -404,7 +407,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<text class="diagram-link-label" x="566" y="320">join path</text>
|
<text class="diagram-link-label" x="566" y="320">join path</text>
|
||||||
<text class="diagram-link-label" x="710" y="494">Pimox template</text>
|
<text class="diagram-link-label" x="710" y="494">Pimox template</text>
|
||||||
<text class="diagram-link-label" x="710" y="626">firewall VM</text>
|
<text class="diagram-link-label" x="710" y="626">firewall VM</text>
|
||||||
<text class="diagram-link-label" x="710" y="420">sync apps</text>
|
<text class="diagram-link-label" x="710" y="420">policy + GitOps</text>
|
||||||
<text class="diagram-link-label" x="934" y="212">secure tunnel</text>
|
<text class="diagram-link-label" x="934" y="212">secure tunnel</text>
|
||||||
<text class="diagram-link-label" x="934" y="322">service traffic</text>
|
<text class="diagram-link-label" x="934" y="322">service traffic</text>
|
||||||
<text class="diagram-link-label" x="946" y="754">image pulls</text>
|
<text class="diagram-link-label" x="946" y="754">image pulls</text>
|
||||||
|
|
@ -822,6 +825,7 @@ function renderStackSourceLinks(string $stackKey, array $sourceLinks, string $so
|
||||||
<script>
|
<script>
|
||||||
const OTHER_PAGES = ['/index.php', '/cv.php', '/homelab-tree.php'];
|
const OTHER_PAGES = ['/index.php', '/cv.php', '/homelab-tree.php'];
|
||||||
</script>
|
</script>
|
||||||
|
<script src="cv-theme.js"></script>
|
||||||
<?php require_once __DIR__ . '/partials/translation_ui.php'; ?>
|
<?php require_once __DIR__ . '/partials/translation_ui.php'; ?>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,48 @@
|
||||||
const cvThemeButtons = [...document.querySelectorAll('[data-cv-theme]')];
|
const siteThemeButtons = [...document.querySelectorAll('[data-site-theme]')];
|
||||||
const cvPortrait = document.getElementById('cv-portrait-orbit');
|
const cvPortrait = document.getElementById('cv-portrait-orbit');
|
||||||
const cvThemeStorageKey = 'cv-theme';
|
const siteThemeStorageKey = 'site-theme';
|
||||||
|
const legacyCvThemeStorageKey = 'cv-theme';
|
||||||
|
|
||||||
function getStoredCvTheme() {
|
function normalizeSiteTheme(theme) {
|
||||||
|
return theme === 'light' || theme === 'fancy' ? 'light' : 'dark';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStoredSiteTheme() {
|
||||||
try {
|
try {
|
||||||
return localStorage.getItem(cvThemeStorageKey);
|
return localStorage.getItem(siteThemeStorageKey) || localStorage.getItem(legacyCvThemeStorageKey);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeCvTheme(theme) {
|
function storeSiteTheme(theme) {
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(cvThemeStorageKey, theme);
|
localStorage.setItem(siteThemeStorageKey, theme);
|
||||||
|
localStorage.removeItem(legacyCvThemeStorageKey);
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setCvTheme(theme) {
|
function setSiteTheme(theme) {
|
||||||
const nextTheme = theme === 'fancy' ? 'fancy' : 'elegant';
|
const nextTheme = normalizeSiteTheme(theme);
|
||||||
document.body.classList.toggle('cv-fancy', nextTheme === 'fancy');
|
document.body.classList.toggle('theme-light', nextTheme === 'light');
|
||||||
document.body.classList.toggle('cv-elegant', nextTheme === 'elegant');
|
document.body.classList.toggle('theme-dark', nextTheme === 'dark');
|
||||||
|
|
||||||
cvThemeButtons.forEach((button) => {
|
siteThemeButtons.forEach((button) => {
|
||||||
const active = button.dataset.cvTheme === nextTheme;
|
const active = button.dataset.siteTheme === nextTheme;
|
||||||
button.classList.toggle('is-active', active);
|
button.classList.toggle('is-active', active);
|
||||||
button.setAttribute('aria-pressed', active ? 'true' : 'false');
|
button.setAttribute('aria-pressed', active ? 'true' : 'false');
|
||||||
});
|
});
|
||||||
|
|
||||||
storeCvTheme(nextTheme);
|
storeSiteTheme(nextTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
cvThemeButtons.forEach((button) => {
|
siteThemeButtons.forEach((button) => {
|
||||||
button.addEventListener('click', () => setCvTheme(button.dataset.cvTheme));
|
button.addEventListener('click', () => setSiteTheme(button.dataset.siteTheme));
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener('pointermove', (event) => {
|
document.addEventListener('pointermove', (event) => {
|
||||||
if (!document.body.classList.contains('cv-fancy') || !cvPortrait) return;
|
if (!document.body.classList.contains('theme-light') || !cvPortrait) return;
|
||||||
|
|
||||||
const bounds = cvPortrait.getBoundingClientRect();
|
const bounds = cvPortrait.getBoundingClientRect();
|
||||||
const centerX = bounds.left + bounds.width / 2;
|
const centerX = bounds.left + bounds.width / 2;
|
||||||
|
|
@ -47,5 +53,4 @@ document.addEventListener('pointermove', (event) => {
|
||||||
cvPortrait.style.setProperty('--portrait-rotation', `${degrees + 8}deg`);
|
cvPortrait.style.setProperty('--portrait-rotation', `${degrees + 8}deg`);
|
||||||
});
|
});
|
||||||
|
|
||||||
setCvTheme(getStoredCvTheme() || 'elegant');
|
setSiteTheme(getStoredSiteTheme() || 'dark');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<title>CV - <?php echo $text['name']; ?></title>
|
<title>CV - <?php echo $text['name']; ?></title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="cv-page cv-elegant">
|
<body class="cv-page theme-dark">
|
||||||
|
|
||||||
<nav class="top-nav">
|
<nav class="top-nav">
|
||||||
<div class="nav-left">Juvenal Diaz</div>
|
<div class="nav-left">Juvenal Diaz</div>
|
||||||
|
|
@ -38,22 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="cv-theme-toolbar" aria-label="<?php echo htmlspecialchars($en['cv_theme_label']); ?>">
|
<?php require __DIR__ . '/partials/theme_toolbar.php'; ?>
|
||||||
<span data-translate data-key="cv_theme_label"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_label']); ?>">
|
|
||||||
<?php echo $text['cv_theme_label']; ?>
|
|
||||||
</span>
|
|
||||||
<button type="button" class="cv-theme-option is-active" data-cv-theme="elegant"
|
|
||||||
data-translate data-key="cv_theme_elegant"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_elegant']); ?>">
|
|
||||||
<?php echo $text['cv_theme_elegant']; ?>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="cv-theme-option" data-cv-theme="fancy"
|
|
||||||
data-translate data-key="cv_theme_fancy"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_fancy']); ?>">
|
|
||||||
<?php echo $text['cv_theme_fancy']; ?>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container cv-container">
|
<div class="container cv-container">
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ $demoCards = [
|
||||||
<title><?php echo $text['demos_title']; ?> - <?php echo $text['name']; ?></title>
|
<title><?php echo $text['demos_title']; ?> - <?php echo $text['name']; ?></title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="theme-dark">
|
||||||
|
|
||||||
<nav class="top-nav">
|
<nav class="top-nav">
|
||||||
<div class="nav-left">Juvenal Diaz</div>
|
<div class="nav-left">Juvenal Diaz</div>
|
||||||
|
|
@ -99,6 +99,8 @@ $demoCards = [
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<?php require __DIR__ . '/partials/theme_toolbar.php'; ?>
|
||||||
|
|
||||||
<main class="demos-page">
|
<main class="demos-page">
|
||||||
<section class="demos-hero">
|
<section class="demos-hero">
|
||||||
<p class="blog-kicker"
|
<p class="blog-kicker"
|
||||||
|
|
@ -151,5 +153,6 @@ $demoCards = [
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script src="cv-theme.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<title><?php echo $text['name']; ?></title>
|
<title><?php echo $text['name']; ?></title>
|
||||||
<link rel="stylesheet" href="styles.css">
|
<link rel="stylesheet" href="styles.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="home-page cv-elegant">
|
<body class="home-page theme-dark">
|
||||||
|
|
||||||
<nav class="top-nav">
|
<nav class="top-nav">
|
||||||
<div class="nav-left">Juvenal Diaz</div>
|
<div class="nav-left">Juvenal Diaz</div>
|
||||||
|
|
@ -38,22 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="cv-theme-toolbar" aria-label="<?php echo htmlspecialchars($en['cv_theme_label']); ?>">
|
<?php require __DIR__ . '/partials/theme_toolbar.php'; ?>
|
||||||
<span data-translate data-key="cv_theme_label"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_label']); ?>">
|
|
||||||
<?php echo $text['cv_theme_label']; ?>
|
|
||||||
</span>
|
|
||||||
<button type="button" class="cv-theme-option is-active" data-cv-theme="elegant"
|
|
||||||
data-translate data-key="cv_theme_elegant"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_elegant']); ?>">
|
|
||||||
<?php echo $text['cv_theme_elegant']; ?>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="cv-theme-option" data-cv-theme="fancy"
|
|
||||||
data-translate data-key="cv_theme_fancy"
|
|
||||||
data-en="<?php echo htmlspecialchars($en['cv_theme_fancy']); ?>">
|
|
||||||
<?php echo $text['cv_theme_fancy']; ?>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<section class="hero">
|
<section class="hero">
|
||||||
<div class="hero-text">
|
<div class="hero-text">
|
||||||
|
|
|
||||||
|
|
@ -18,9 +18,12 @@ return [
|
||||||
|
|
||||||
'cv_summary_title' => 'Professional Summary',
|
'cv_summary_title' => 'Professional Summary',
|
||||||
'cv_summary' => 'IT Professional with 12+ years of experience, specializing in Linux but also proficient in team management (local and global teams) and user satisfaction. My greatest strength is a sense of urgency which enables me to tackle issues in the most fast and efficient way, always focusing on continuous improvement and service excellence. I also enjoy learning new technologies as required.',
|
'cv_summary' => 'IT Professional with 12+ years of experience, specializing in Linux but also proficient in team management (local and global teams) and user satisfaction. My greatest strength is a sense of urgency which enables me to tackle issues in the most fast and efficient way, always focusing on continuous improvement and service excellence. I also enjoy learning new technologies as required.',
|
||||||
'cv_theme_label' => 'CV theme',
|
'site_theme_label' => 'Theme',
|
||||||
'cv_theme_elegant' => 'Elegant',
|
'site_theme_dark' => 'Dark',
|
||||||
'cv_theme_fancy' => 'Fancy',
|
'site_theme_light' => 'Light',
|
||||||
|
'cv_theme_label' => 'Theme',
|
||||||
|
'cv_theme_elegant' => 'Dark',
|
||||||
|
'cv_theme_fancy' => 'Light',
|
||||||
'cv_orbit_text' => 'Reliability, Linux, Kubernetes, automation, and just enough drama to keep the resume awake.',
|
'cv_orbit_text' => 'Reliability, Linux, Kubernetes, automation, and just enough drama to keep the resume awake.',
|
||||||
|
|
||||||
'cv_employment_title' => 'Employment History / Activities',
|
'cv_employment_title' => 'Employment History / Activities',
|
||||||
|
|
@ -55,7 +58,7 @@ return [
|
||||||
|
|
||||||
'blog_kicker' => 'Homelab field notes',
|
'blog_kicker' => 'Homelab field notes',
|
||||||
'blog_title' => 'I accidentally built a tiny CI/CD platform',
|
'blog_title' => 'I accidentally built a tiny CI/CD platform',
|
||||||
'blog_subtitle' => 'A casual conversation about how a Debian box, a Raspberry Pi, an Orange Pi 5 Plus running Pimox, an OCI edge host, a Debian 13 VM template, and a small OpenWrt firewall plan became a repeatable Kubernetes delivery path.',
|
'blog_subtitle' => 'A casual conversation about how a Debian control plane, Pimox app workers, external Gitea, a local registry, Kyverno policy, Argo CD, and a small static demo shelf became a repeatable Kubernetes delivery path.',
|
||||||
'blog_speaker_question' => 'Future me, judging',
|
'blog_speaker_question' => 'Future me, judging',
|
||||||
'blog_speaker_answer' => 'Me, holding coffee',
|
'blog_speaker_answer' => 'Me, holding coffee',
|
||||||
'blog_q1' => 'Be honest: why build all this instead of just running a couple containers like a normal person?',
|
'blog_q1' => 'Be honest: why build all this instead of just running a couple containers like a normal person?',
|
||||||
|
|
@ -63,29 +66,29 @@ return [
|
||||||
'blog_q2' => 'Why kubeadm? Were managed clusters too emotionally stable?',
|
'blog_q2' => 'Why kubeadm? Were managed clusters too emotionally stable?',
|
||||||
'blog_a2' => 'Pretty much. kubeadm keeps the cluster close to the metal, which is a polite way of saying I get to see every sharp edge. The Debian node runs the control plane, the Raspberry Pi joins as an arm64 worker, and Pimox on an Orange Pi 5 Plus now gives me a repeatable way to add Debian 13 arm64 VM workers. Suddenly networking, storage, container runtimes, certs, and node recovery are not mysterious cloud magic. They are my problem.',
|
'blog_a2' => 'Pretty much. kubeadm keeps the cluster close to the metal, which is a polite way of saying I get to see every sharp edge. The Debian node runs the control plane, the Raspberry Pi joins as an arm64 worker, and Pimox on an Orange Pi 5 Plus now gives me a repeatable way to add Debian 13 arm64 VM workers. Suddenly networking, storage, container runtimes, certs, and node recovery are not mysterious cloud magic. They are my problem.',
|
||||||
'blog_q3' => 'So where is the CI/CD part hiding?',
|
'blog_q3' => 'So where is the CI/CD part hiding?',
|
||||||
'blog_a3' => 'It is small, but it is real. OpenTofu brings up the cluster, platform, apps, and edge layers. Argo CD watches Git and keeps the cluster honest. Docker Buildx builds the PHP website for linux/arm64, pushes it to the local registry, and then the workload rolls forward. No enterprise dashboard fireworks, just a clean loop that says: Git changed, image built, cluster updated, nobody had to kubectl-edit anything at 2 AM.',
|
'blog_a3' => 'It is small, but it is real. OpenTofu brings up the cluster, platform, apps, and edge layers. Argo CD watches Git from the app workers, Kyverno keeps policy pressure on the workloads, Docker Buildx builds linux/arm64 images, and the local registry feeds the cluster. No enterprise dashboard fireworks, just a clean loop: Git changed, image built, cluster updated, nobody had to kubectl-edit anything at 2 AM.',
|
||||||
'blog_q4' => 'Why run your own registry and Gitea? Was the simple option unavailable?',
|
'blog_q4' => 'Why run your own registry and Gitea? Was the simple option unavailable?',
|
||||||
'blog_a4' => 'The simple option was very available, which is why I heroically ignored it. The registry means experiments do not need to go to a public image repo, and external Gitea gives the lab its own Git service without making Kubernetes responsible for its own source of truth. Together they make the setup feel less like "some containers under the stairs" and more like a tiny platform with opinions, responsibilities, and occasionally dramatic storage needs.',
|
'blog_a4' => 'The simple option was very available, which is why I heroically ignored it. The registry means experiments do not need to go to a public image repo, and external Gitea gives the lab its own Git service without making Kubernetes responsible for its own source of truth. Together they make the setup feel less like "some containers under the stairs" and more like a tiny platform with opinions, responsibilities, and occasionally dramatic storage needs.',
|
||||||
'blog_q5' => 'What actually hurt the most?',
|
'blog_q5' => 'What actually hurt the most?',
|
||||||
'blog_a5' => 'Storage. Always storage. Kubernetes, Docker, retained volumes, and build caches can fill a small root disk with the quiet confidence of a bad decision. Moving OpenEBS local volumes and Docker data to the external SSD turned the lab from "why is everything on fire?" into "okay, this is usable now." Growth, allegedly.',
|
'blog_a5' => 'Storage. Always storage. Kubernetes, Docker, retained volumes, and build caches can fill a small root disk with the quiet confidence of a bad decision. Moving OpenEBS local volumes and Docker data to the external SSD turned the lab from "why is everything on fire?" into "okay, this is usable now." Growth, allegedly.',
|
||||||
'blog_q6' => 'And now the website has demos and a weirdly expressive CV?',
|
'blog_q6' => 'So the platform controllers finally moved off the control plane?',
|
||||||
'blog_a6' => 'Correct. The CV now has an Elegant mode for terminal-green seriousness and a Fancy mode where my face follows the cursor like it has opinions. The Demos page is now a catalog that links to a separate demos-static artifact, because apparently the natural next step after building a platform is learning not to shove every toy into the same image.',
|
'blog_a6' => 'Yes. Argo CD and Kyverno now target the homelab.dev/node-role=app workers, including Kyverno hook jobs, so the Debian node can stay focused on control-plane duties. That one change made the lab feel less like everything was balanced on the first machine that happened to boot.',
|
||||||
'blog_q7' => 'Can the current cluster actually handle all that, or are we about to smoke the Pi?',
|
'blog_q7' => 'Can the current cluster actually handle all that, or are we about to smoke the Pi?',
|
||||||
'blog_a7' => 'The Pi survives because the demos are intentionally local-first and now ship as a separate static artifact. The website pod stays a portfolio shell, the demos-static pod serves static bundles, and the user browser does the expensive work. If I later ship real ONNX object detection, Transformers.js, or full video transcoding models, those must lazy-load in the browser or move to a beefier node. The Raspberry Pi is brave, but it is not a GPU wearing a tiny hat.',
|
'blog_a7' => 'The Pi survives because the demos are intentionally local-first and now ship as a separate static artifact. The website pod stays a portfolio shell, the demos-static pod serves static bundles, and the user browser does the expensive work. If I later ship real ONNX object detection, Transformers.js, or full video transcoding models, those must lazy-load in the browser or move to a beefier node. The Raspberry Pi is brave, but it is not a GPU wearing a tiny hat.',
|
||||||
'blog_q8' => 'So the lab can now build its own worker nodes?',
|
'blog_q8' => 'So the lab can now build its own worker nodes?',
|
||||||
'blog_a8' => 'Yes, and now with fewer crossed fingers. Debian runs a provisioning layer with dnsmasq, nginx, PXE boot files, GRUB, and a Debian 13 arm64 preseed. OpenTofu talks to Pimox through qm, creates VM 9000 on local storage, boots it from the network, installs the OS, runs golden-node prep, disables swap, verifies cgroups, installs containerd and kubeadm tooling, then seals it as a template. Worker clones are idempotent by VMID and now land on nvme_thin_pool, so local storage stays reserved for the template.',
|
'blog_a8' => 'Yes, and now with fewer crossed fingers. Debian runs a provisioning layer with dnsmasq, nginx, PXE boot files, GRUB, and a Debian 13 arm64 preseed. OpenTofu talks to Pimox through qm, creates VM 9000 on local storage, boots it from the network, installs the OS, runs golden-node prep, disables swap, verifies cgroups, installs containerd and kubeadm tooling, then seals it as a template. Worker clones are idempotent by VMID and now land on nvme_thin_pool, so local storage stays reserved for the template.',
|
||||||
'blog_q9' => 'And OpenWrt is joining the story too?',
|
'blog_q9' => 'And OpenWrt is joining the story too?',
|
||||||
'blog_a9' => 'Only as a simple firewall, not as a networking science project. The pipeline can create an opt-in OpenWrt ARM SystemReady VM, attach vmbr0 as WAN and vmbr1 as LAN, and configure the LAN side without rewriting Orange Pi host networking. DHCP stays optional, and VLANs wait until there is a managed switch and a local test window.',
|
'blog_a9' => 'Only as a simple firewall, not as a networking science project. The pipeline can create an opt-in OpenWrt ARM SystemReady VM, attach vmbr0 as WAN and vmbr1 as LAN, and configure the LAN side without rewriting Orange Pi host networking. DHCP stays optional, and VLANs wait until there is a managed switch and a local test window.',
|
||||||
'blog_q10' => 'What changed on the observability side?',
|
'blog_q10' => 'What changed on the observability and scheduling side?',
|
||||||
'blog_a10' => 'Monitoring moved from "someday" to "running." The platform now has Prometheus, Grafana, Loki, Mimir, Promtail, node-exporter, and kube-state-metrics. The next useful step is not installing more dashboards; it is choosing the few alerts that would actually wake me up for the right reasons.',
|
'blog_a10' => 'Monitoring moved from "someday" to "running," and scheduling moved from "whatever fits" to explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics give the lab useful signals; the next useful step is choosing the few alerts that would actually wake me up for the right reasons.',
|
||||||
'blog_stack_title' => 'Technologies and why they are here',
|
'blog_stack_title' => 'Technologies and why they are here',
|
||||||
'blog_stack_1' => 'Debian Linux is the steady adult in the room: control plane host, deployment workstation, PXE/preseed server, and the place where OpenTofu, Docker, kubeadm, and the scripts do their thing.',
|
'blog_stack_1' => 'Debian Linux is the steady adult in the room: control plane host, deployment workstation, PXE/preseed server, and the place where OpenTofu, Docker, kubeadm, and the scripts do their thing.',
|
||||||
'blog_stack_2' => 'Raspberry Pi adds the current arm64 worker, while Pimox on the Orange Pi 5 Plus gives the lab a VM-based expansion path. The template stays on local storage and Kubernetes worker clones go to nvme_thin_pool.',
|
'blog_stack_2' => 'Raspberry Pi keeps the external Gitea service close to the lab, while Pimox on the Orange Pi 5 Plus provides the app-worker pool. The Debian template stays on local storage and Kubernetes worker clones go to nvme_thin_pool.',
|
||||||
'blog_stack_3' => 'OpenTofu makes the cluster, platform, apps, edge, and provisioning layers repeatable, because "I swear I remember the command" is not a disaster recovery strategy.',
|
'blog_stack_3' => 'OpenTofu makes the cluster, platform, apps, edge, and provisioning layers repeatable, because "I swear I remember the command" is not a disaster recovery strategy.',
|
||||||
'blog_stack_4' => 'Calico handles pod networking, and OpenEBS hostpath storage keeps the important data around after rebuilds, because deleting everything by accident is only funny once.',
|
'blog_stack_4' => 'Calico handles pod networking, and OpenEBS hostpath storage keeps the important data around after rebuilds, because deleting everything by accident is only funny once.',
|
||||||
'blog_stack_5' => 'Argo CD is the GitOps referee: manifests live in Git, the cluster follows along, and manual drift gets side-eyed back into place.',
|
'blog_stack_5' => 'Argo CD is the GitOps referee and now runs on app workers: manifests live in Git, the cluster follows along, and manual drift gets side-eyed back into place.',
|
||||||
'blog_stack_6' => 'The OCI edge host runs nginx, HAProxy, Varnish, and Squid so TLS, routing, and caching stay outside the home network while Tailscale sneaks the traffic back to the worker node.',
|
'blog_stack_6' => 'The OCI edge host runs nginx, HAProxy, Varnish, and Squid so TLS, routing, and caching stay outside the home network while Tailscale sneaks the traffic back to the worker node.',
|
||||||
'blog_stack_7' => 'The CV theme toggle is plain CSS and JavaScript, which is all it needs: one mode for console nostalgia, one mode for cursor-following nonsense with manners.',
|
'blog_stack_7' => 'The shared theme toggle is plain CSS and JavaScript: dark mode uses an old-console green-on-black treatment, while light mode switches to black cursive on white across the website and demo catalog.',
|
||||||
'blog_stack_8' => 'The first demo keeps files in the browser. Image crunching uses native Canvas APIs today, while the fast serious path for video conversion is Rust compiled to WebAssembly with a TypeScript UI.',
|
'blog_stack_8' => 'The first demo keeps files in the browser. Image crunching uses native Canvas APIs today, while the fast serious path for video conversion is Rust compiled to WebAssembly with a TypeScript UI.',
|
||||||
'blog_stack_9' => 'The newer demos cover network jitter graphs, local JSON/JWT/log tools, an architecture simulator, an offline traveler converter, a redactor prototype, sentiment analysis, and model-drift simulation.',
|
'blog_stack_9' => 'The newer demos cover network jitter graphs, local JSON/JWT/log tools, an architecture simulator, an offline traveler converter, a redactor prototype, sentiment analysis, and model-drift simulation.',
|
||||||
'blog_stack_10' => 'The heavier ML demos are designed as client-side Wasm/ONNX/Transformers.js candidates, not server-side jobs. That keeps the homelab app boring to operate, which is secretly the whole point.',
|
'blog_stack_10' => 'The heavier ML demos are designed as client-side Wasm/ONNX/Transformers.js candidates, not server-side jobs. That keeps the homelab app boring to operate, which is secretly the whole point.',
|
||||||
|
|
@ -94,15 +97,15 @@ return [
|
||||||
'blog_stack_13' => 'The golden image bakes in Kubernetes prerequisites: swap disabled, cgroup boot options checked, kernel modules loaded, containerd configured for systemd cgroups, kubeadm/kubelet/kubectl installed, and qemu-guest-agent enabled.',
|
'blog_stack_13' => 'The golden image bakes in Kubernetes prerequisites: swap disabled, cgroup boot options checked, kernel modules loaded, containerd configured for systemd cgroups, kubeadm/kubelet/kubectl installed, and qemu-guest-agent enabled.',
|
||||||
'blog_stack_14' => 'Worker clone automation is count-based and idempotent: LAB_PIMOX_WORKER_COUNT=1 means ensure VM 9010 exists, not create a fresh worker on every run. New workers are refused if the target storage is local.',
|
'blog_stack_14' => 'Worker clone automation is count-based and idempotent: LAB_PIMOX_WORKER_COUNT=1 means ensure VM 9010 exists, not create a fresh worker on every run. New workers are refused if the target storage is local.',
|
||||||
'blog_stack_15' => 'OpenWrt is handled separately from the Debian golden-node template. The lab downloads the upstream ARM SystemReady EFI image, imports it as VM 9050 on nvme_thin_pool, and keeps it disabled unless LAB_OPENWRT_VM=true is set.',
|
'blog_stack_15' => 'OpenWrt is handled separately from the Debian golden-node template. The lab downloads the upstream ARM SystemReady EFI image, imports it as VM 9050 on nvme_thin_pool, and keeps it disabled unless LAB_OPENWRT_VM=true is set.',
|
||||||
'blog_stack_16' => 'The monitoring layer now includes Prometheus Stack, Grafana, Mimir, Loki, Promtail, node-exporter, and kube-state-metrics, giving the lab metrics and logs without putting heavy traffic analysis on the firewall.',
|
'blog_stack_16' => 'The monitoring layer now includes Prometheus Stack, Grafana, Loki, Promtail, node-exporter, and kube-state-metrics, with placement guardrails so platform add-ons stop defaulting to the control plane.',
|
||||||
'blog_arch_kicker' => 'Architecture map',
|
'blog_arch_kicker' => 'Architecture map',
|
||||||
'blog_arch_title' => 'The homelab, end to end',
|
'blog_arch_title' => 'The homelab, end to end',
|
||||||
'blog_arch_intro' => 'The current delivery path starts with a push to Gitea, runs local validation, builds arm64 images, syncs the validated commit into the GitOps mirror, and lets Argo CD reconcile the Kubernetes workloads. The infrastructure path stays manual through lab.sh, including the PXE/Pimox template builder, NVMe-backed worker clones, and the opt-in OpenWrt firewall VM, while the OCI edge routes public traffic back through the private path.',
|
'blog_arch_intro' => 'The current delivery path starts with a push to Gitea, runs local validation, builds arm64 images, syncs the validated commit into the GitOps mirror, and lets Argo CD reconcile from the app workers. The infrastructure path stays manual through lab.sh, including the PXE/Pimox template builder, NVMe-backed worker clones, Kyverno policy placement, and the opt-in OpenWrt firewall VM, while the OCI edge routes public traffic back through the private path.',
|
||||||
'blog_arch_caption' => 'The diagram is intentionally operational: it shows the app delivery loop, image flow, provisioning path, storage boundary, monitoring layer, OpenWrt firewall option, and public traffic path without hiding the practical bits that make a small lab behave like a platform.',
|
'blog_arch_caption' => 'The diagram is intentionally operational: it shows the app delivery loop, image flow, provisioning path, worker-placement boundary, monitoring layer, OpenWrt firewall option, and public traffic path without hiding the practical bits that make a small lab behave like a platform.',
|
||||||
'blog_arch_fun_link' => 'Open the Christmas-tree version',
|
'blog_arch_fun_link' => 'Open the Christmas-tree version',
|
||||||
'blog_activity_kicker' => 'Recent activity log',
|
'blog_activity_kicker' => 'Recent activity log',
|
||||||
'blog_activity_title' => 'What changed since the first build',
|
'blog_activity_title' => 'What changed since the first build',
|
||||||
'blog_activity_intro' => 'The lab moved from a working Kubernetes experiment into a more complete self-hosted delivery system. The latest work focused on trust, repeatability, VM-based expansion, and making deploys match the exact commit that passed validation.',
|
'blog_activity_intro' => 'The lab moved from a working Kubernetes experiment into a more complete self-hosted delivery system. The latest work focused on trust, repeatability, VM-based expansion, controller placement, and making deploys match the exact commit that passed validation.',
|
||||||
'blog_activity_1' => 'Moved Gitea out of Kubernetes and onto the Raspberry Pi as the local Git service, while keeping the public /git/ route through the edge stack.',
|
'blog_activity_1' => 'Moved Gitea out of Kubernetes and onto the Raspberry Pi as the local Git service, while keeping the public /git/ route through the edge stack.',
|
||||||
'blog_activity_2' => 'Installed and validated a Debian-hosted Gitea Actions runner so pushes to main can build, scan, and deploy without depending on a laptop session.',
|
'blog_activity_2' => 'Installed and validated a Debian-hosted Gitea Actions runner so pushes to main can build, scan, and deploy without depending on a laptop session.',
|
||||||
'blog_activity_3' => 'Added a custom checkout flow for the /git/ subpath and kept a persistent Debian checkout for the deployment scripts.',
|
'blog_activity_3' => 'Added a custom checkout flow for the /git/ subpath and kept a persistent Debian checkout for the deployment scripts.',
|
||||||
|
|
@ -115,7 +118,7 @@ return [
|
||||||
'blog_activity_10' => 'Built the Debian 13 arm64 Pimox template end to end with PXE, preseed, qemu-guest-agent discovery, cgroup validation, swap disabled, and a final seal step.',
|
'blog_activity_10' => 'Built the Debian 13 arm64 Pimox template end to end with PXE, preseed, qemu-guest-agent discovery, cgroup validation, swap disabled, and a final seal step.',
|
||||||
'blog_activity_11' => 'Added NVMe-backed Pimox worker clone automation so VM 9000 stays on local storage while worker nodes are created on nvme_thin_pool.',
|
'blog_activity_11' => 'Added NVMe-backed Pimox worker clone automation so VM 9000 stays on local storage while worker nodes are created on nvme_thin_pool.',
|
||||||
'blog_activity_12' => 'Added an opt-in OpenWrt VM path for a simple firewall between vmbr0 and vmbr1, with guardrails that avoid Orange Pi host networking changes.',
|
'blog_activity_12' => 'Added an opt-in OpenWrt VM path for a simple firewall between vmbr0 and vmbr1, with guardrails that avoid Orange Pi host networking changes.',
|
||||||
'blog_activity_13' => 'Installed the monitoring stack with Grafana, Prometheus, Mimir, Loki, Promtail, node-exporter, and kube-state-metrics.',
|
'blog_activity_13' => 'Installed the monitoring stack and moved platform add-ons such as Argo CD, Kyverno, and prometheus-stack work toward app-worker placement instead of treating the control plane as spare capacity.',
|
||||||
'blog_todo_kicker' => 'Improvement backlog',
|
'blog_todo_kicker' => 'Improvement backlog',
|
||||||
'blog_todo_title' => 'Todo list for the next homelab pass',
|
'blog_todo_title' => 'Todo list for the next homelab pass',
|
||||||
'blog_todo_intro' => 'These are improvement proposals, not chores for the sake of chores. Each item either reduces rebuild risk, tightens supply-chain hygiene, or makes the platform easier to operate when something fails.',
|
'blog_todo_intro' => 'These are improvement proposals, not chores for the sake of chores. Each item either reduces rebuild risk, tightens supply-chain hygiene, or makes the platform easier to operate when something fails.',
|
||||||
|
|
@ -125,7 +128,7 @@ return [
|
||||||
'blog_todo_4' => 'Replace mutable latest image references with immutable tags or digest pins for website and demo workloads.',
|
'blog_todo_4' => 'Replace mutable latest image references with immutable tags or digest pins for website and demo workloads.',
|
||||||
'blog_todo_5' => 'Generate SBOMs and sign images so the local registry can prove what it is serving.',
|
'blog_todo_5' => 'Generate SBOMs and sign images so the local registry can prove what it is serving.',
|
||||||
'blog_todo_6' => 'Add Renovate or Dependabot-style dependency updates for base images, Helm charts, and GitHub/Gitea Actions.',
|
'blog_todo_6' => 'Add Renovate or Dependabot-style dependency updates for base images, Helm charts, and GitHub/Gitea Actions.',
|
||||||
'blog_todo_7' => 'Enforce baseline Kubernetes policy with Kyverno or Gatekeeper: non-root, read-only roots, resource requests, and allowed registries.',
|
'blog_todo_7' => 'Expand Kyverno baseline policy coverage: non-root, read-only roots, resource requests, allowed registries, and documented exceptions for platform components.',
|
||||||
'blog_todo_8' => 'Turn the installed observability stack into useful operations views: a few high-signal dashboards, alerts for node health, storage pressure, certificate expiry, and failed app syncs.',
|
'blog_todo_8' => 'Turn the installed observability stack into useful operations views: a few high-signal dashboards, alerts for node health, storage pressure, certificate expiry, and failed app syncs.',
|
||||||
'blog_todo_9' => 'Schedule backup restore drills for external Gitea and OpenEBS volumes, then write the exact restore runbook.',
|
'blog_todo_9' => 'Schedule backup restore drills for external Gitea and OpenEBS volumes, then write the exact restore runbook.',
|
||||||
'blog_todo_10' => 'Tighten TLS, SSH, and token rotation around the OCI edge, Gitea, registry, and runner credentials.',
|
'blog_todo_10' => 'Tighten TLS, SSH, and token rotation around the OCI edge, Gitea, registry, and runner credentials.',
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,12 @@ return [
|
||||||
|
|
||||||
'cv_summary_title' => 'Notequitl Tlahcuilolli',
|
'cv_summary_title' => 'Notequitl Tlahcuilolli',
|
||||||
'cv_summary' => 'Tlapixqui āmantēcayōtl inic matlactli omome xihuitl, motemachtia Linux ihuan quimatia tlatecpanaliztli (ipan altepetl ihuan tlalpan) ihuan tlahtoa tlacame. Nohueyitequitl ic tlaneltoquiliztli niquixehua tlaneltoquiliztli inic achi ic niquichihua, moch ica tlapatlaliztli ihuan tlatequipanoliztli. Nixpampa nimati āmantēcayōtl yancuic quenin monequi.',
|
'cv_summary' => 'Tlapixqui āmantēcayōtl inic matlactli omome xihuitl, motemachtia Linux ihuan quimatia tlatecpanaliztli (ipan altepetl ihuan tlalpan) ihuan tlahtoa tlacame. Nohueyitequitl ic tlaneltoquiliztli niquixehua tlaneltoquiliztli inic achi ic niquichihua, moch ica tlapatlaliztli ihuan tlatequipanoliztli. Nixpampa nimati āmantēcayōtl yancuic quenin monequi.',
|
||||||
'cv_theme_label' => 'CV theme',
|
'site_theme_label' => 'Theme',
|
||||||
'cv_theme_elegant' => 'Elegant',
|
'site_theme_dark' => 'Dark',
|
||||||
'cv_theme_fancy' => 'Fancy',
|
'site_theme_light' => 'Light',
|
||||||
|
'cv_theme_label' => 'Theme',
|
||||||
|
'cv_theme_elegant' => 'Dark',
|
||||||
|
'cv_theme_fancy' => 'Light',
|
||||||
'cv_orbit_text' => 'Reliability, Linux, Kubernetes, automation, ihuan achi drama inic resume nemi.',
|
'cv_orbit_text' => 'Reliability, Linux, Kubernetes, automation, ihuan achi drama inic resume nemi.',
|
||||||
|
|
||||||
'cv_employment_title' => 'Notequitl Tlahcuilolli / Tlatequipanoliztli',
|
'cv_employment_title' => 'Notequitl Tlahcuilolli / Tlatequipanoliztli',
|
||||||
|
|
@ -72,24 +75,24 @@ return [
|
||||||
'blog_a4' => 'Registry amo monequi nicpush nochi experiment ipan public repo. Gitea nemi fuera Kubernetes ipan Raspberry Pi ihuan quimaca lab se Git service. In ome quichihua ce tepiton production platform.',
|
'blog_a4' => 'Registry amo monequi nicpush nochi experiment ipan public repo. Gitea nemi fuera Kubernetes ipan Raspberry Pi ihuan quimaca lab se Git service. In ome quichihua ce tepiton production platform.',
|
||||||
'blog_q5' => 'Tlein achi ohui omomachtih?',
|
'blog_q5' => 'Tlein achi ohui omomachtih?',
|
||||||
'blog_a5' => 'Storage. Kubernetes, Docker, retained volumes, ihuan build cache huel quitemitia root disk. OpenEBS ihuan Docker data omoyecpan ipan external SSD, ic system achi yec nemi.',
|
'blog_a5' => 'Storage. Kubernetes, Docker, retained volumes, ihuan build cache huel quitemitia root disk. OpenEBS ihuan Docker data omoyecpan ipan external SSD, ic system achi yec nemi.',
|
||||||
'blog_q6' => 'Ihuan axcan website quipia demos ihuan CV occeppa?',
|
'blog_q6' => 'Axcan Argo CD ihuan Kyverno quisa fuera control plane?',
|
||||||
'blog_a6' => 'Quena. CV quipia Elegant mode para console green ihuan Fancy mode canin noxayac quitta cursor. Demos page axcan catalog ihuan demos-static artifact.',
|
'blog_a6' => 'Quena. Argo CD ihuan Kyverno axcan yahui ipan homelab.dev/node-role=app workers, ihuan Debian control plane mocahua para API, scheduler, ihuan controller tequitl.',
|
||||||
'blog_q7' => 'Cluster huel quipias nochi demos?',
|
'blog_q7' => 'Cluster huel quipias nochi demos?',
|
||||||
'blog_a7' => 'Quena, pampa demos cateh local-first ihuan separate static artifact. Website pod zan shell, demos-static pod quimaca bundles, browser quichihua tequitl. Real ONNX, Transformers.js, o video transcoding monequi lazy-load o occe node hueyi.',
|
'blog_a7' => 'Quena, pampa demos cateh local-first ihuan separate static artifact. Website pod zan shell, demos-static pod quimaca bundles, browser quichihua tequitl. Real ONNX, Transformers.js, o video transcoding monequi lazy-load o occe node hueyi.',
|
||||||
'blog_q8' => 'Axcan lab huel quichihua worker nodes?',
|
'blog_q8' => 'Axcan lab huel quichihua worker nodes?',
|
||||||
'blog_a8' => 'Quena. Debian quipia provisioning layer: dnsmasq, nginx, PXE, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox ika qm, quichihua VM 9000 ipan local storage, quiboota network, quinstala OS, quichihua golden-node prep, quitzacua swap, quitta cgroups, quinstala containerd ihuan kubeadm tools, ihuan quicuepa template. Worker clones yahui ipan nvme_thin_pool.',
|
'blog_a8' => 'Quena. Debian quipia provisioning layer: dnsmasq, nginx, PXE, GRUB, ihuan Debian 13 arm64 preseed. OpenTofu notza Pimox ika qm, quichihua VM 9000 ipan local storage, quiboota network, quinstala OS, quichihua golden-node prep, quitzacua swap, quitta cgroups, quinstala containerd ihuan kubeadm tools, ihuan quicuepa template. Worker clones yahui ipan nvme_thin_pool.',
|
||||||
'blog_q9' => 'Ihuan OpenWrt no quiza nican?',
|
'blog_q9' => 'Ihuan OpenWrt no quiza nican?',
|
||||||
'blog_a9' => 'Zan simple firewall. Pipeline huel quichihua OpenWrt ARM SystemReady VM, quipia vmbr0 quen WAN ihuan vmbr1 quen LAN, ihuan amo quipatla Orange Pi host networking. DHCP optional, VLANs mostla quema onca managed switch ihuan local test.',
|
'blog_a9' => 'Zan simple firewall. Pipeline huel quichihua OpenWrt ARM SystemReady VM, quipia vmbr0 quen WAN ihuan vmbr1 quen LAN, ihuan amo quipatla Orange Pi host networking. DHCP optional, VLANs mostla quema onca managed switch ihuan local test.',
|
||||||
'blog_q10' => 'Tlein mopatla ipan observability?',
|
'blog_q10' => 'Tlein mopatla ipan observability ihuan scheduling?',
|
||||||
'blog_a10' => 'Monitoring axcan nemi: Prometheus, Grafana, Loki, Mimir, Promtail, node-exporter, ihuan kube-state-metrics. Oc monequi achi cuali dashboards ihuan alerts.',
|
'blog_a10' => 'Monitoring axcan nemi, ihuan scheduling quipia explicit worker placement. Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics quimaca signals para lab.',
|
||||||
'blog_stack_title' => 'Tlamantli ihuan tleica nemi nican',
|
'blog_stack_title' => 'Tlamantli ihuan tleica nemi nican',
|
||||||
'blog_stack_1' => 'Debian Linux quimaca control-plane host, deployment workstation, PXE/preseed server, ihuan canin nemi OpenTofu, Docker, kubeadm, ihuan scripts.',
|
'blog_stack_1' => 'Debian Linux quimaca control-plane host, deployment workstation, PXE/preseed server, ihuan canin nemi OpenTofu, Docker, kubeadm, ihuan scripts.',
|
||||||
'blog_stack_2' => 'Raspberry Pi quimaca axcan arm64 worker; Pimox ipan Orange Pi 5 Plus quimaca VM expansion path. Template mocahua ipan local storage ihuan worker clones yahui ipan nvme_thin_pool.',
|
'blog_stack_2' => 'Raspberry Pi quimaca axcan arm64 worker; Pimox ipan Orange Pi 5 Plus quimaca VM expansion path. Template mocahua ipan local storage ihuan worker clones yahui ipan nvme_thin_pool.',
|
||||||
'blog_stack_3' => 'OpenTofu quichihua cluster, platform, apps, edge, ihuan provisioning configuration reproducible.',
|
'blog_stack_3' => 'OpenTofu quichihua cluster, platform, apps, edge, ihuan provisioning configuration reproducible.',
|
||||||
'blog_stack_4' => 'Calico quimati pod networking; OpenEBS hostpath storage quipia data ipan cluster rebuilds.',
|
'blog_stack_4' => 'Calico quimati pod networking; OpenEBS hostpath storage quipia data ipan cluster rebuilds.',
|
||||||
'blog_stack_5' => 'Argo CD quimaca GitOps control loop: manifests cateh ipan Git ihuan cluster moyecpana.',
|
'blog_stack_5' => 'Argo CD quimaca GitOps control loop ihuan axcan nemi ipan app workers: manifests cateh ipan Git ihuan cluster moyecpana.',
|
||||||
'blog_stack_6' => 'OCI edge host quipia nginx, HAProxy, Varnish, ihuan Squid para TLS, routing, ihuan cache, ihuan Tailscale quihuica traffic ipan worker node.',
|
'blog_stack_6' => 'OCI edge host quipia nginx, HAProxy, Varnish, ihuan Squid para TLS, routing, ihuan cache, ihuan Tailscale quihuica traffic ipan worker node.',
|
||||||
'blog_stack_7' => 'CV theme toggle quipia CSS ihuan JavaScript: ce mode console, occe mode cursor-following.',
|
'blog_stack_7' => 'Shared theme toggle quipia CSS ihuan JavaScript: dark mode console green-on-black, light mode cursive black-on-white ipan website ihuan demos.',
|
||||||
'blog_stack_8' => 'Demo achto quipia files ipan browser. Image crunching quimati Canvas; video conversion quinequi Rust WebAssembly ihuan TypeScript UI.',
|
'blog_stack_8' => 'Demo achto quipia files ipan browser. Image crunching quimati Canvas; video conversion quinequi Rust WebAssembly ihuan TypeScript UI.',
|
||||||
'blog_stack_9' => 'Yancuic demos quipia network jitter graphs, local JSON/JWT/log tools, architecture simulator, offline traveler converter, redactor, sentiment analysis, ihuan model drift simulation.',
|
'blog_stack_9' => 'Yancuic demos quipia network jitter graphs, local JSON/JWT/log tools, architecture simulator, offline traveler converter, redactor, sentiment analysis, ihuan model drift simulation.',
|
||||||
'blog_stack_10' => 'ML demos monequi client-side Wasm/ONNX/Transformers.js, ahmo server-side jobs.',
|
'blog_stack_10' => 'ML demos monequi client-side Wasm/ONNX/Transformers.js, ahmo server-side jobs.',
|
||||||
|
|
@ -98,7 +101,7 @@ return [
|
||||||
'blog_stack_13' => 'Golden image quipia Kubernetes prereqs: swap disabled, cgroup boot options checked, kernel modules, containerd systemd cgroups, kubeadm/kubelet/kubectl, ihuan qemu-guest-agent.',
|
'blog_stack_13' => 'Golden image quipia Kubernetes prereqs: swap disabled, cgroup boot options checked, kernel modules, containerd systemd cgroups, kubeadm/kubelet/kubectl, ihuan qemu-guest-agent.',
|
||||||
'blog_stack_14' => 'Worker clone automation count-based ihuan idempotent: LAB_PIMOX_WORKER_COUNT=1 quinequi VM 9010 ma onca, ahmo yancuic VM nochipa. Local storage amo quiselia worker clones.',
|
'blog_stack_14' => 'Worker clone automation count-based ihuan idempotent: LAB_PIMOX_WORKER_COUNT=1 quinequi VM 9010 ma onca, ahmo yancuic VM nochipa. Local storage amo quiselia worker clones.',
|
||||||
'blog_stack_15' => 'OpenWrt amo quiza ipan Debian golden-node template. Lab quitemoa ARM SystemReady EFI image, quimporta VM 9050 ipan nvme_thin_pool, ihuan zan quisa quema LAB_OPENWRT_VM=true.',
|
'blog_stack_15' => 'OpenWrt amo quiza ipan Debian golden-node template. Lab quitemoa ARM SystemReady EFI image, quimporta VM 9050 ipan nvme_thin_pool, ihuan zan quisa quema LAB_OPENWRT_VM=true.',
|
||||||
'blog_stack_16' => 'Monitoring layer quipia Prometheus Stack, Grafana, Mimir, Loki, Promtail, node-exporter, ihuan kube-state-metrics.',
|
'blog_stack_16' => 'Monitoring layer quipia Prometheus Stack, Grafana, Loki, Promtail, node-exporter, ihuan kube-state-metrics, ika worker placement guardrails.',
|
||||||
|
|
||||||
'demos_kicker' => 'Tepiton tools ipan browser',
|
'demos_kicker' => 'Tepiton tools ipan browser',
|
||||||
'demos_title' => 'Demo Apps',
|
'demos_title' => 'Demo Apps',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
<div class="site-theme-toolbar" aria-label="<?php echo htmlspecialchars($en['site_theme_label']); ?>">
|
||||||
|
<span data-translate data-key="site_theme_label"
|
||||||
|
data-en="<?php echo htmlspecialchars($en['site_theme_label']); ?>">
|
||||||
|
<?php echo $text['site_theme_label']; ?>
|
||||||
|
</span>
|
||||||
|
<button type="button" class="site-theme-option is-active" data-site-theme="dark"
|
||||||
|
data-translate data-key="site_theme_dark"
|
||||||
|
data-en="<?php echo htmlspecialchars($en['site_theme_dark']); ?>">
|
||||||
|
<?php echo $text['site_theme_dark']; ?>
|
||||||
|
</button>
|
||||||
|
<span class="site-theme-separator" aria-hidden="true">|</span>
|
||||||
|
<button type="button" class="site-theme-option" data-site-theme="light"
|
||||||
|
data-translate data-key="site_theme_light"
|
||||||
|
data-en="<?php echo htmlspecialchars($en['site_theme_light']); ?>">
|
||||||
|
<?php echo $text['site_theme_light']; ?>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
@ -223,12 +223,12 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-page .top-nav,
|
.home-page .top-nav,
|
||||||
.home-page .cv-theme-toolbar {
|
.home-page .site-theme-toolbar {
|
||||||
max-width: 1080px;
|
max-width: 1080px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-page .top-nav,
|
.cv-page .top-nav,
|
||||||
.cv-page .cv-theme-toolbar {
|
.cv-page .site-theme-toolbar {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1057,7 +1057,7 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-theme-toolbar {
|
.site-theme-toolbar {
|
||||||
max-width: 900px;
|
max-width: 900px;
|
||||||
margin: 0 auto 18px;
|
margin: 0 auto 18px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -1067,7 +1067,7 @@ body {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-theme-option {
|
.site-theme-option {
|
||||||
border: 1px solid #9fb3c8;
|
border: 1px solid #9fb3c8;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #102a43;
|
color: #102a43;
|
||||||
|
|
@ -1078,7 +1078,7 @@ body {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-theme-option.is-active {
|
.site-theme-option.is-active {
|
||||||
background: #004085;
|
background: #004085;
|
||||||
border-color: #004085;
|
border-color: #004085;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
@ -1107,177 +1107,189 @@ body {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.cv-page.cv-elegant,
|
body.cv-page.theme-dark,
|
||||||
body.home-page.cv-elegant {
|
body.home-page.theme-dark {
|
||||||
background: #030603;
|
background: #030603;
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .top-nav,
|
.theme-dark .top-nav,
|
||||||
.cv-elegant .cv-theme-toolbar {
|
.theme-dark .site-theme-toolbar {
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .nav-left,
|
.theme-dark .nav-left,
|
||||||
.cv-elegant .nav-right a {
|
.theme-dark .nav-right a {
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-theme-option {
|
.theme-dark .site-theme-option {
|
||||||
background: #061106;
|
background: #061106;
|
||||||
border-color: #2b7a38;
|
border-color: #2b7a38;
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-theme-option.is-active {
|
.theme-dark .site-theme-option.is-active {
|
||||||
background: #143b18;
|
background: #143b18;
|
||||||
border-color: #a8ffb0;
|
border-color: #a8ffb0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-container {
|
.theme-dark .cv-container {
|
||||||
background: #061106;
|
background: #061106;
|
||||||
border: 1px solid #2b7a38;
|
border: 1px solid #2b7a38;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-shadow: 0 0 18px rgba(168, 255, 176, 0.12);
|
box-shadow: 0 0 18px rgba(168, 255, 176, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-container h1,
|
.theme-dark .cv-container h1,
|
||||||
.cv-elegant .cv-container h2,
|
.theme-dark .cv-container h2,
|
||||||
.cv-elegant .cv-container strong {
|
.theme-dark .cv-container strong {
|
||||||
color: #c7ffd0;
|
color: #c7ffd0;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-container p {
|
.theme-dark .cv-container p {
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant.home-page .hero {
|
.theme-dark.home-page .hero {
|
||||||
background: #061106;
|
background: #061106;
|
||||||
border: 1px solid #2b7a38;
|
border: 1px solid #2b7a38;
|
||||||
box-shadow: 0 0 18px rgba(168, 255, 176, 0.12);
|
box-shadow: 0 0 18px rgba(168, 255, 176, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant.home-page .hero-text h1,
|
.theme-dark.home-page .hero-text h1,
|
||||||
.cv-elegant.home-page .cta a {
|
.theme-dark.home-page .cta a {
|
||||||
color: #c7ffd0;
|
color: #c7ffd0;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant.home-page .bio-intro,
|
.theme-dark.home-page .bio-intro,
|
||||||
.cv-elegant.home-page .bio-story,
|
.theme-dark.home-page .bio-story,
|
||||||
.cv-elegant.home-page .cta {
|
.theme-dark.home-page .cta {
|
||||||
color: #a8ffb0;
|
color: #a8ffb0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant.home-page .hero-image img {
|
.theme-dark.home-page .hero-image img {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 2px solid #a8ffb0;
|
border: 2px solid #a8ffb0;
|
||||||
filter: saturate(0.8) contrast(1.08);
|
filter: saturate(0.8) contrast(1.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-header-text {
|
.theme-dark .cv-header-text {
|
||||||
min-height: 160px;
|
min-height: 160px;
|
||||||
padding-right: 190px;
|
padding-right: 190px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-portrait-orbit {
|
.theme-dark .cv-portrait-orbit {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 30px;
|
top: 30px;
|
||||||
right: 30px;
|
right: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-portrait-img {
|
.theme-dark .cv-portrait-img {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 2px solid #a8ffb0;
|
border: 2px solid #a8ffb0;
|
||||||
filter: saturate(0.8) contrast(1.08);
|
filter: saturate(0.8) contrast(1.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
body.cv-page.cv-fancy,
|
body.theme-dark {
|
||||||
body.home-page.cv-fancy {
|
background: #030603;
|
||||||
background: #f7f0e8;
|
color: #a8ffb0;
|
||||||
color: #37251f;
|
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||||
font-family: Georgia, "Times New Roman", serif;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .top-nav {
|
body.theme-light {
|
||||||
|
background: #fff;
|
||||||
|
color: #111;
|
||||||
|
font-family: "Brush Script MT", "Segoe Script", "Snell Roundhand", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.cv-page.theme-light,
|
||||||
|
body.home-page.theme-light {
|
||||||
|
background: #fff;
|
||||||
|
color: #111;
|
||||||
|
font-family: "Brush Script MT", "Segoe Script", "Snell Roundhand", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .top-nav {
|
||||||
color: #5f3f35;
|
color: #5f3f35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .nav-left,
|
.theme-light .nav-left,
|
||||||
.cv-fancy .nav-right a {
|
.theme-light .nav-right a {
|
||||||
color: #6f3d53;
|
color: #6f3d53;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-theme-option {
|
.theme-light .site-theme-option {
|
||||||
border-color: #c29aac;
|
border-color: #c29aac;
|
||||||
background: #fffaf7;
|
background: #fffaf7;
|
||||||
color: #6f3d53;
|
color: #6f3d53;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-theme-option.is-active {
|
.theme-light .site-theme-option.is-active {
|
||||||
background: #6f3d53;
|
background: #6f3d53;
|
||||||
border-color: #6f3d53;
|
border-color: #6f3d53;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-container {
|
.theme-light .cv-container {
|
||||||
background: #fffaf7;
|
background: #fffaf7;
|
||||||
border: 1px solid #ead5c6;
|
border: 1px solid #ead5c6;
|
||||||
box-shadow: 0 18px 40px rgba(95, 63, 53, 0.16);
|
box-shadow: 0 18px 40px rgba(95, 63, 53, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy.home-page .hero {
|
.theme-light.home-page .hero {
|
||||||
background: #fffaf7;
|
background: #fffaf7;
|
||||||
border: 1px solid #ead5c6;
|
border: 1px solid #ead5c6;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
box-shadow: 0 18px 40px rgba(95, 63, 53, 0.16);
|
box-shadow: 0 18px 40px rgba(95, 63, 53, 0.16);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy.home-page .hero-text h1 {
|
.theme-light.home-page .hero-text h1 {
|
||||||
color: #6f3d53;
|
color: #6f3d53;
|
||||||
font-family: "Brush Script MT", "Segoe Script", cursive;
|
font-family: "Brush Script MT", "Segoe Script", cursive;
|
||||||
font-size: 3.4rem;
|
font-size: 3.4rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy.home-page .bio-intro,
|
.theme-light.home-page .bio-intro,
|
||||||
.cv-fancy.home-page .bio-story,
|
.theme-light.home-page .bio-story,
|
||||||
.cv-fancy.home-page .cta {
|
.theme-light.home-page .cta {
|
||||||
color: #5f3f35;
|
color: #5f3f35;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy.home-page .cta a {
|
.theme-light.home-page .cta a {
|
||||||
color: #6f3d53;
|
color: #6f3d53;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy.home-page .hero-image img {
|
.theme-light.home-page .hero-image img {
|
||||||
border: 8px solid #fff;
|
border: 8px solid #fff;
|
||||||
box-shadow: 0 12px 24px rgba(95, 63, 53, 0.2);
|
box-shadow: 0 12px 24px rgba(95, 63, 53, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-header-text {
|
.theme-light .cv-header-text {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-header-text h1 {
|
.theme-light .cv-header-text h1 {
|
||||||
color: #6f3d53;
|
color: #6f3d53;
|
||||||
font-family: "Brush Script MT", "Segoe Script", cursive;
|
font-family: "Brush Script MT", "Segoe Script", cursive;
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-portrait-orbit {
|
.theme-light .cv-portrait-orbit {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 8px auto 28px;
|
margin: 8px auto 28px;
|
||||||
width: 260px;
|
width: 260px;
|
||||||
height: 260px;
|
height: 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-portrait-orbit svg {
|
.theme-light .cv-portrait-orbit svg {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
@ -1285,14 +1297,14 @@ body.home-page.cv-fancy {
|
||||||
animation: orbitText 24s linear infinite;
|
animation: orbitText 24s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-portrait-orbit text {
|
.theme-light .cv-portrait-orbit text {
|
||||||
fill: #6f3d53;
|
fill: #6f3d53;
|
||||||
font-family: "Brush Script MT", "Segoe Script", cursive;
|
font-family: "Brush Script MT", "Segoe Script", cursive;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
letter-spacing: 1px;
|
letter-spacing: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-portrait-img {
|
.theme-light .cv-portrait-img {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 45px;
|
inset: 45px;
|
||||||
width: 170px;
|
width: 170px;
|
||||||
|
|
@ -1548,8 +1560,231 @@ body.home-page.cv-fancy {
|
||||||
font-size: 38px;
|
font-size: 38px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.theme-light .top-nav,
|
||||||
|
body.theme-light .site-theme-toolbar,
|
||||||
|
body.theme-light .nav-left,
|
||||||
|
body.theme-light .nav-right a,
|
||||||
|
body.theme-light .blog-kicker,
|
||||||
|
body.theme-light .section-kicker,
|
||||||
|
body.theme-light .demo-label,
|
||||||
|
body.theme-light .source-links span,
|
||||||
|
body.theme-light .source-links a {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-separator {
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option[data-site-theme="dark"] {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
font-family: "Courier New", "Lucida Console", Monaco, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option[data-site-theme="light"] {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
font-family: "Brush Script MT", "Segoe Script", "Snell Roundhand", cursive;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-theme-option.is-active {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .site-theme-option[data-site-theme="dark"].is-active {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #a8ffb0;
|
||||||
|
color: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .site-theme-option[data-site-theme="light"].is-active {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .blog-hero,
|
||||||
|
.theme-dark .architecture-section,
|
||||||
|
.theme-dark .activity-log,
|
||||||
|
.theme-dark .homelab-todo,
|
||||||
|
.theme-dark .tech-notes,
|
||||||
|
.theme-dark .demo-card,
|
||||||
|
.theme-dark .message,
|
||||||
|
.theme-dark .todo-list li,
|
||||||
|
.theme-dark .activity-list li,
|
||||||
|
.theme-dark .visitor-idea-card,
|
||||||
|
.theme-dark .idea-status,
|
||||||
|
.theme-dark .source-links a {
|
||||||
|
background: #061106;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
box-shadow: 0 0 18px rgba(168, 255, 176, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .blog-hero,
|
||||||
|
.theme-dark .demos-hero {
|
||||||
|
border-color: #2b7a38;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .blog-hero h1,
|
||||||
|
.theme-dark .demos-hero h1,
|
||||||
|
.theme-dark .section-heading h1,
|
||||||
|
.theme-dark .section-heading h2,
|
||||||
|
.theme-dark .tech-notes h2,
|
||||||
|
.theme-dark .speaker,
|
||||||
|
.theme-dark .visitor-ideas h3,
|
||||||
|
.theme-dark .visitor-idea-list h4,
|
||||||
|
.theme-dark .demo-card h2,
|
||||||
|
.theme-dark .cv-header-text h1 {
|
||||||
|
color: #c7ffd0;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .blog-subtitle,
|
||||||
|
.theme-dark .section-heading p,
|
||||||
|
.theme-dark .message p,
|
||||||
|
.theme-dark .demos-hero p,
|
||||||
|
.theme-dark .demo-card p,
|
||||||
|
.theme-dark .diagram-caption,
|
||||||
|
.theme-dark .visitor-idea-card footer,
|
||||||
|
.theme-dark .idea-form label,
|
||||||
|
.theme-dark .source-links span {
|
||||||
|
color: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .idea-form input,
|
||||||
|
.theme-dark .idea-form textarea {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
color: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-shell {
|
||||||
|
background: #030603;
|
||||||
|
border-color: #2b7a38;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-zone {
|
||||||
|
fill: #061106;
|
||||||
|
stroke: #2b7a38;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-node rect {
|
||||||
|
fill: #030603;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-zone-title,
|
||||||
|
.theme-dark .diagram-node text,
|
||||||
|
.theme-dark .diagram-link-label {
|
||||||
|
fill: #c7ffd0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-node .diagram-small,
|
||||||
|
.theme-dark .diagram-small {
|
||||||
|
fill: #a8ffb0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dark .diagram-link {
|
||||||
|
stroke: #7cff9c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .container,
|
||||||
|
.theme-light.home-page .hero,
|
||||||
|
.theme-light .cv-container,
|
||||||
|
.theme-light .blog-hero,
|
||||||
|
.theme-light .architecture-section,
|
||||||
|
.theme-light .activity-log,
|
||||||
|
.theme-light .homelab-todo,
|
||||||
|
.theme-light .tech-notes,
|
||||||
|
.theme-light .demo-card,
|
||||||
|
.theme-light .message,
|
||||||
|
.theme-light .todo-list li,
|
||||||
|
.theme-light .activity-list li,
|
||||||
|
.theme-light .visitor-idea-card,
|
||||||
|
.theme-light .idea-status,
|
||||||
|
.theme-light .source-links a {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .blog-hero,
|
||||||
|
.theme-light .demos-hero {
|
||||||
|
border-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .blog-hero h1,
|
||||||
|
.theme-light .demos-hero h1,
|
||||||
|
.theme-light .section-heading h1,
|
||||||
|
.theme-light .section-heading h2,
|
||||||
|
.theme-light .tech-notes h2,
|
||||||
|
.theme-light .speaker,
|
||||||
|
.theme-light .visitor-ideas h3,
|
||||||
|
.theme-light .visitor-idea-list h4,
|
||||||
|
.theme-light .demo-card h2,
|
||||||
|
.theme-light .cv-header-text h1,
|
||||||
|
.theme-light.home-page .hero-text h1,
|
||||||
|
.theme-light.home-page .cta a {
|
||||||
|
color: #111;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .blog-subtitle,
|
||||||
|
.theme-light .section-heading p,
|
||||||
|
.theme-light .message p,
|
||||||
|
.theme-light .demos-hero p,
|
||||||
|
.theme-light .demo-card p,
|
||||||
|
.theme-light .diagram-caption,
|
||||||
|
.theme-light .visitor-idea-card footer,
|
||||||
|
.theme-light .idea-form label,
|
||||||
|
.theme-light .source-links span,
|
||||||
|
.theme-light.home-page .bio-intro,
|
||||||
|
.theme-light.home-page .bio-story,
|
||||||
|
.theme-light.home-page .cta {
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .idea-form input,
|
||||||
|
.theme-light .idea-form textarea {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .diagram-shell {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .diagram-zone {
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .diagram-node rect {
|
||||||
|
fill: #fff;
|
||||||
|
stroke: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .diagram-zone-title,
|
||||||
|
.theme-light .diagram-node text,
|
||||||
|
.theme-light .diagram-link-label,
|
||||||
|
.theme-light .diagram-node .diagram-small,
|
||||||
|
.theme-light .diagram-small {
|
||||||
|
fill: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-light .diagram-link {
|
||||||
|
stroke: #111;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.cv-theme-toolbar {
|
.site-theme-toolbar {
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
@ -1558,17 +1793,17 @@ body.home-page.cv-fancy {
|
||||||
width: min(100%, 320px);
|
width: min(100%, 320px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-header-text {
|
.theme-dark .cv-header-text {
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
padding-top: 178px;
|
padding-top: 178px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-elegant .cv-portrait-orbit {
|
.theme-dark .cv-portrait-orbit {
|
||||||
left: 30px;
|
left: 30px;
|
||||||
right: auto;
|
right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cv-fancy .cv-header-text h1 {
|
.theme-light .cv-header-text h1 {
|
||||||
font-size: 2.35rem;
|
font-size: 2.35rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue