Files
the-other-dude/docs/website/blog/index.html
Jason Staack 5b78cd9869 docs(website): apply Warm Precision styling to all blog pages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 15:32:59 -05:00

290 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog — The Other Dude</title>
<meta name="description" content="Updates, insights, and the occasional rant about open source MikroTik fleet management, network operations, and building software with AI.">
<meta name="keywords" content="MikroTik blog, RouterOS fleet management, MSP blog, network management blog">
<meta name="author" content="The Other Dude">
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#eae7de">
<link rel="canonical" href="https://theotherdude.net/blog/">
<link rel="icon" href="../data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'><rect x='2' y='2' width='60' height='60' rx='8' fill='none' stroke='%238B1A1A' stroke-width='2'/><path d='M32 18 L46 32 L32 46 L18 32 Z' fill='%238B1A1A'/><path d='M32 19 L38 32 L32 45 L26 32 Z' fill='%232A9D8F'/><circle cx='32' cy='32' r='5' fill='%238B1A1A'/><circle cx='32' cy='32' r='2.5' fill='%232A9D8F'/></svg>">
<!-- Open Graph -->
<meta property="og:type" content="website">
<meta property="og:title" content="Blog — The Other Dude">
<meta property="og:description" content="Updates, insights, and the occasional rant about MikroTik fleet management, network operations, and building software with AI.">
<meta property="og:url" content="https://theotherdude.net/blog/">
<meta property="og:site_name" content="The Other Dude">
<!-- Fonts -->
<link rel="stylesheet" href="../style.css?v=3">
<style>
/* Warm Precision overrides */
:root {
--background: #eae7de;
--surface: #f6f4ec;
--elevated: #f0ede4;
--border: rgba(40,36,28,0.12);
--text-primary: #1a1810;
--text-secondary: #5e5a4e;
--text-muted: #8a8578;
--accent: #8a7a48;
}
body { background-color: #eae7de; color: #1a1810; }
.dark { /* prevent dark mode */ }
</style>
<style>
.blog-index {
max-width: 720px;
margin: 0 auto;
padding: 80px 24px 120px;
}
.blog-index h1 {
font-family: "Manrope", sans-serif;
font-weight: 700;
font-size: 2.5rem;
color: var(--text-primary);
margin-bottom: 8px;
}
.blog-index .blog-subtitle {
color: var(--text-muted);
font-size: 1.05rem;
margin-bottom: 48px;
}
.blog-list {
list-style: none;
padding: 0;
margin: 0;
}
.blog-list li {
border-bottom: 1px solid var(--border);
padding: 24px 0;
}
.blog-list li:first-child {
border-top: 1px solid var(--border);
}
.blog-list a {
text-decoration: none;
display: block;
}
.blog-list a:hover .blog-list-title {
color: var(--accent);
}
.blog-list-date {
color: var(--text-muted);
font-size: 13px;
margin-bottom: 4px;
}
.blog-list-title {
font-family: "Manrope", sans-serif;
font-weight: 600;
font-size: 1.25rem;
color: var(--text-primary);
margin-bottom: 6px;
transition: color 0.2s ease;
}
.blog-list-excerpt {
color: var(--text-secondary);
font-size: 0.95rem;
line-height: 1.6;
}
@media (max-width: 480px) {
.blog-index h1 { font-size: 1.8rem; }
.blog-index { padding: 60px 20px 80px; }
}
</style>
</head>
<body>
<nav class="site-nav site-nav--dark">
<div class="nav-inner container">
<a href="../index.html" class="nav-logo">
<svg class="nav-logo-mark" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="32" height="32" aria-label="The Other Dude logo">
<rect x="2" y="2" width="60" height="60" rx="8" fill="none" stroke="#8B1A1A" stroke-width="2"/>
<rect x="6" y="6" width="52" height="52" rx="5" fill="none" stroke="#F5E6C8" stroke-width="1.5"/>
<rect x="8" y="8" width="48" height="48" rx="4" fill="#8B1A1A" opacity="0.15"/>
<path d="M32 8 L56 32 L32 56 L8 32 Z" fill="none" stroke="#8B1A1A" stroke-width="2"/>
<path d="M32 13 L51 32 L32 51 L13 32 Z" fill="none" stroke="#F5E6C8" stroke-width="1.5"/>
<path d="M32 18 L46 32 L32 46 L18 32 Z" fill="#8B1A1A"/>
<path d="M32 19 L38 32 L32 45 L26 32 Z" fill="#2A9D8F"/>
<path d="M19 32 L32 26 L45 32 L32 38 Z" fill="#F5E6C8"/>
<circle cx="32" cy="32" r="5" fill="#8B1A1A"/>
<circle cx="32" cy="32" r="2.5" fill="#2A9D8F"/>
<path d="M10 10 L16 10 L10 16 Z" fill="#2A9D8F" opacity="0.7"/>
<path d="M54 10 L54 16 L48 10 Z" fill="#2A9D8F" opacity="0.7"/>
<path d="M10 54 L16 54 L10 48 Z" fill="#2A9D8F" opacity="0.7"/>
<path d="M54 54 L48 54 L54 48 Z" fill="#2A9D8F" opacity="0.7"/>
</svg>
<span>The Other Dude</span>
</a>
<div class="nav-links">
<a href="../docs.html" class="nav-link">Docs</a>
<a href="index.html" class="nav-link">Blog</a>
<a href="https://github.com/staack/the-other-dude" class="nav-link" rel="noopener">GitHub</a>
</div>
</div>
</nav>
<main>
<div class="blog-index">
<h1>Blog</h1>
<p class="blog-subtitle">Updates, insights, and the occasional rant about MikroTik fleet management.</p>
<ul class="blog-list">
<li>
<a href="500-devices-broke-the-api.html">
<div class="blog-list-date">March 21, 2026</div>
<div class="blog-list-title">500 Devices Broke the API</div>
<div class="blog-list-excerpt">The API container got OOM-killed under realistic load. Here's what happened, what was wrong, and how three config changes fixed it.</div>
</a>
</li>
<li>
<a href="not-posting-on-reddit.html">
<div class="blog-list-date">March 19, 2026</div>
<div class="blog-list-title">Why I'm Not Posting This on Reddit</div>
<div class="blog-list-excerpt">I built something and I'm not posting it on Reddit, Hacker News, or anywhere that runs on upvotes. Here's why building in public on forums is a trap.</div>
</a>
</li>
<li>
<a href="free-tier-cap.html">
<div class="blog-list-date">March 19, 2026</div>
<div class="blog-list-title">Free Is Now Capped at 250 Devices</div>
<div class="blog-list-excerpt">The free tier moved from "basically unlimited" to 250 devices. Here's why, what it means, and what hasn't changed.</div>
</a>
</li>
<li>
<a href="100-simulated-routers.html">
<div class="blog-list-date">March 18, 2026</div>
<div class="blog-list-title">Found a Bug Running 100 Simulated Routers</div>
<div class="blog-list-excerpt">A 100-device simulation exposed a NATS JetStream memory issue caused by message retention behavior. Here's what happened, why, and the fix.</div>
</a>
</li>
<li>
<a href="what-you-can-do-today.html">
<div class="blog-list-date">March 17, 2026</div>
<div class="blog-list-title">What You Can Do With It Today (And What You Can't)</div>
<div class="blog-list-excerpt">An honest look at what The Other Dude actually does today. No roadmap promises, no marketing. What works, what's rough, and what's missing entirely.</div>
</a>
</li>
<li>
<a href="not-stable-software.html">
<div class="blog-list-date">March 16, 2026</div>
<div class="blog-list-title">This Is Not Stable Software</div>
<div class="blog-list-excerpt">The Other Dude is under active development. Things break, APIs change, and that's the point. Here's why, and when stability starts mattering.</div>
</a>
</li>
<li>
<a href="why-this-exists.html">
<div class="blog-list-date">March 15, 2026</div>
<div class="blog-list-title">Why This Exists</div>
<div class="blog-list-excerpt">The story behind The Other Dude: two decades of wanting better MikroTik fleet management, AI making it possible, and why I probably won't answer your email.</div>
</a>
</li>
</ul>
</div>
</main>
<footer class="site-footer">
<div class="footer-inner container">
<div class="footer-brand">
<span class="footer-logo">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="24" height="24" aria-hidden="true" style="vertical-align: middle; margin-right: 8px;">
<rect x="2" y="2" width="60" height="60" rx="8" fill="none" stroke="#8B1A1A" stroke-width="2"/>
<rect x="6" y="6" width="52" height="52" rx="5" fill="none" stroke="#F5E6C8" stroke-width="1.5"/>
<rect x="8" y="8" width="48" height="48" rx="4" fill="#8B1A1A" opacity="0.15"/>
<path d="M32 18 L46 32 L32 46 L18 32 Z" fill="#8B1A1A"/>
<path d="M32 19 L38 32 L32 45 L26 32 Z" fill="#2A9D8F"/>
<path d="M19 32 L32 26 L45 32 L32 38 Z" fill="#F5E6C8"/>
<circle cx="32" cy="32" r="5" fill="#8B1A1A"/>
<circle cx="32" cy="32" r="2.5" fill="#2A9D8F"/>
</svg>
The Other Dude
</span>
<span class="footer-copy">&copy; 2026 The Other Dude. All rights reserved.</span>
</div>
<nav class="footer-links">
<a href="../docs.html">Docs</a>
<a href="index.html">Blog</a>
<a href="https://github.com/staack/the-other-dude" rel="noopener">GitHub</a>
<a href="mailto:license@theotherdude.net">Licensing</a>
</nav>
<p style="margin-top:12px;font-size:0.75em;color:#8a8578;text-align:center;">This site uses self-hosted, cookie-free analytics to measure page views and engagement. No personal data is collected or shared with third parties.</p>
</div>
</footer>
<script>
(function() {
var h = 'https://telemetry.theotherdude.net';
var p = location.pathname;
var t = document.title;
var r = document.referrer;
// Session page count via sessionStorage.
var sc = parseInt(sessionStorage.getItem('_tc_sc') || '0', 10) + 1;
sessionStorage.setItem('_tc_sc', sc);
// UTM params.
var sp = new URLSearchParams(location.search);
var us = sp.get('utm_source') || '';
var um = sp.get('utm_medium') || '';
var uc = sp.get('utm_campaign') || '';
// Pixel URL with all params.
var params = new URLSearchParams({
p: p, t: t, r: r,
sw: screen.width, sh: screen.height,
vw: innerWidth, vh: innerHeight,
tz: new Date().getTimezoneOffset(),
dpr: devicePixelRatio || 1,
touch: navigator.maxTouchPoints > 0 ? 1 : 0,
cd: screen.colorDepth,
plt: Math.round(performance.now()),
sc: sc
});
if (us) params.set('us', us);
if (um) params.set('um', um);
if (uc) params.set('uc', uc);
var ct = navigator.connection ? navigator.connection.effectiveType : '';
if (ct) params.set('ct', ct);
new Image().src = h + '/px?' + params.toString();
// Engagement tracking.
var startTime = performance.now();
var maxScroll = 0;
function getScrollDepth() {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
var docHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
var winHeight = innerHeight;
if (docHeight <= winHeight) return 100;
var pct = Math.round((scrollTop + winHeight) / docHeight * 100);
return Math.min(pct, 100);
}
window.addEventListener('scroll', function() {
var d = getScrollDepth();
if (d > maxScroll) maxScroll = d;
}, {passive: true});
// Send beacon on page hide.
function sendBeacon() {
var top = Math.round(performance.now() - startTime);
var data = new URLSearchParams({p: p, top: top, sd: maxScroll});
navigator.sendBeacon(h + '/px/beacon', data);
}
document.addEventListener('visibilitychange', function() {
if (document.visibilityState === 'hidden') sendBeacon();
});
window.addEventListener('pagehide', sendBeacon);
})();
</script>
</body>
</html>