<!DOCTYPE html>
<html lang="en" id="home">

<meta http-equiv="content-type" content="text/html;charset=utf-8" />

<head>
    <title>
        <%= blogPost.title %>
    </title>
    <meta name="description" content="<%= blogPost.description %>">
    <% const seo = { fullCanonicalUrl: 'https://oneuptime.com/blog/post/' + blogPost.fileName + '/view' }; %>
    <%- include('../head-basic') -%>
        <link rel="alternate" type="application/rss+xml" title="OneUptime Blog RSS Feed" href="/blog/rss.xml">
        <meta property="og:site_name" content="OneUptime | One Complete Observability platform.">
        <meta property="og:type" content="article">
        <meta property="og:title" content="<%= blogPost.title %>">
        <meta property="og:description" content="<%= blogPost.description %>">
        <meta property="og:url" content="<%= blogPost.blogUrl %>">
        <meta property="og:image" content="<%= blogPost.socialMediaImageUrl %>">
        <meta property="article:published_time" content="<%- blogPost.postDate -%>T00:00:00.000Z">
        <meta property="article:modified_time" content="<%- blogPost.postDate -%>T00:00:00.000Z">
        <meta property="article:publisher" content="https://www.facebook.com/OneUptime">
        <meta name="twitter:card" content="summary_large_image">
        <meta name="twitter:title" content="<%= blogPost.title %>">
        <meta name="twitter:description" content="<%= blogPost.description %>">
        <meta name="twitter:url" content="<%= blogPost.blogUrl %>">
        <meta name="twitter:image" content="<%= blogPost.socialMediaImageUrl %>">
        <meta name="twitter:label1" content="Written by">
        <meta name="twitter:data1" content="<%= blogPost.author.name %>">
        <meta name="twitter:site" content="@OneUptimeHQ">
        <meta property="og:image:width" content="1280">
        <meta property="og:image:height" content="720">
        <script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "Article",
    "publisher": {
        "@type": "Organization",
        "name": "OneUptime",
        "url": "https://oneuptime.com/",
        "logo": {
            "@type": "ImageObject",
            "url": "https://oneuptime.com/img/OneUptimePNG/1.png",
            "width": 60,
            "height": 60
        }
    },
    "author": {
        "@type": "Person",
        "name": "<%= blogPost.author.name %>",
        "url": "<%= blogPost.author.githubUrl %>",
        "sameAs": []
    },
    "headline": "<%= blogPost.title %>",
    "url": "<%= blogPost.blogUrl %>",
    "datePublished": "<%- blogPost.postDate -%>T00:00:00.000Z",
    "dateModified": "<%- blogPost.postDate -%>T00:00:00.000Z",
    "description": "<%- blogPost.description -%>",
    "mainEntityOfPage": "<%= blogPost.blogUrl %>"
}
    </script>

        <!-- Breadcrumb schema -->
        <script type="application/ld+json">
{
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://oneuptime.com/" },
        { "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://oneuptime.com/blog" },
        { "@type": "ListItem", "position": 3, "name": "<%= blogPost.title %>" }
    ]
}
        </script>

        <!-- Syntax highlighting with highlight.js - using VS Code dark theme -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/vs2015.min.css">
        <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
        <script>
            // Lazy-load only the language packs actually used on this page
            document.addEventListener('DOMContentLoaded', function(){
                var langMap = {
                    'dockerfile':'dockerfile','yaml':'yaml','yml':'yaml','bash':'bash','shell':'bash','sh':'bash',
                    'typescript':'typescript','ts':'typescript','go':'go','python':'python','py':'python',
                    'json':'json','ruby':'ruby','rb':'ruby','java':'java','css':'css','sql':'sql',
                    'rust':'rust','graphql':'graphql','javascript':'javascript','js':'javascript',
                    'xml':'xml','html':'xml','markup':'xml','cpp':'cpp','csharp':'csharp','php':'php',
                    'bind':'dns','dns':'dns','zone':'dns',
                    'conf':'apache','apache':'apache','nginx':'nginx',
                    'ini':'ini','toml':'ini',
                    'diff':'diff','http':'http','makefile':'makefile',
                    'c':'c','swift':'swift','kotlin':'kotlin','scala':'scala','lua':'lua',
                    'perl':'perl','r':'r','powershell':'powershell','ps':'powershell'
                };
                var needed = {};
                document.querySelectorAll('pre code[class*="language-"]').forEach(function(el){
                    var m = el.className.match(/language-(\w+)/);
                    if(m && langMap[m[1]]){ needed[langMap[m[1]]] = true; }
                    else if(m && m[1]){ el.className = el.className.replace(/language-\w+/, ''); }
                });
                var langs = Object.keys(needed);
                var loaded = 0;
                if(langs.length === 0){ hljs.highlightAll(); return; }
                langs.forEach(function(lang){
                    var s = document.createElement('script');
                    s.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/' + lang + '.min.js';
                    s.async = false;
                    s.onload = s.onerror = function(){
                        loaded++;
                        if(loaded >= langs.length){ hljs.highlightAll(); }
                    };
                    document.head.appendChild(s);
                });
            });
        </script>

        <style>
            body {
                background: #ffffff;
                color: #111827;
            }

            .reading-progress {
                position: fixed;
                top: 0;
                left: 0;
                width: 0;
                height: 3px;
                z-index: 60;
                background: linear-gradient(90deg, #3b82f6, #06b6d4, #10b981);
                transition: width 120ms ease-out;
                box-shadow: 0 0 10px rgba(59, 130, 246, 0.4), 0 0 20px rgba(6, 182, 212, 0.3);
            }

            .floating-share {
                position: absolute;
                left: -5rem;
                top: 0;
                display: flex;
                flex-direction: column;
                gap: 0.75rem;
            }

            .floating-share a, .floating-share button {
                width: 2.75rem;
                height: 2.75rem;
                border-radius: 9999px;
                border: 1px solid rgba(148, 163, 184, 0.5);
                display: inline-flex;
                align-items: center;
                justify-content: center;
                background: rgba(255, 255, 255, 0.9);
                box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06);
                transition: all 0.2s ease;
                cursor: pointer;
                color: inherit;
            }

            .floating-share a:hover, .floating-share button:hover {
                border-color: #3b82f6;
                color: #3b82f6;
                transform: translateY(-2px);
            }

            /* Code blocks styling */
            .blog-body pre {
                position: relative;
                font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', 'Courier New', monospace !important;
                font-size: 14px !important;
                line-height: 1.6 !important;
                font-weight: 400 !important;
                margin-top: 1.75rem !important;
                margin-bottom: 1.75rem !important;
                border: 1px solid rgba(15, 23, 42, 0.1) !important;
                border-radius: 0.75rem !important;
                box-shadow: 0 10px 30px rgba(15, 23, 42, 0.06) !important;
            }

            .blog-body pre code {
                font-family: inherit !important;
                font-size: inherit !important;
                line-height: inherit !important;
                font-weight: inherit !important;
            }

            /* Ensure highlight.js doesn't override our font settings */
            .blog-body .hljs {
                font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', 'Courier New', monospace !important;
                font-size: 14px !important;
                line-height: 1.6 !important;
                font-weight: 400 !important;
                padding: 1.25rem !important;
                padding-top: 3rem !important;
                border-radius: 0.75rem !important;
            }

            /* Language label */
            .code-block-header {
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                display: flex;
                align-items: center;
                justify-content: space-between;
                padding: 0.4rem 0.75rem;
                background: rgba(255, 255, 255, 0.06);
                border-bottom: 1px solid rgba(255, 255, 255, 0.08);
                border-radius: 0.75rem 0.75rem 0 0;
                z-index: 2;
            }

            .code-lang-label {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                font-size: 11px;
                font-weight: 500;
                text-transform: uppercase;
                letter-spacing: 0.05em;
                color: rgba(255, 255, 255, 0.5);
                user-select: none;
            }

            /* Copy button */
            .code-copy-btn {
                display: inline-flex;
                align-items: center;
                gap: 0.35rem;
                padding: 0.2rem 0.5rem;
                border: none;
                border-radius: 0.375rem;
                background: transparent;
                color: rgba(255, 255, 255, 0.5);
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                font-size: 11px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.15s ease;
            }

            .code-copy-btn:hover {
                background: rgba(255, 255, 255, 0.1);
                color: rgba(255, 255, 255, 0.85);
            }

            .code-copy-btn.copied {
                color: #4ade80;
            }

            .code-copy-btn svg {
                width: 14px;
                height: 14px;
            }

            /* Custom scrollbar for code blocks */
            .blog-body pre::-webkit-scrollbar,
            .blog-body pre code::-webkit-scrollbar {
                height: 6px;
            }

            .blog-body pre::-webkit-scrollbar-track,
            .blog-body pre code::-webkit-scrollbar-track {
                background: transparent;
            }

            .blog-body pre::-webkit-scrollbar-thumb,
            .blog-body pre code::-webkit-scrollbar-thumb {
                background: rgba(255, 255, 255, 0.15);
                border-radius: 3px;
            }

            .blog-body pre::-webkit-scrollbar-thumb:hover,
            .blog-body pre code::-webkit-scrollbar-thumb:hover {
                background: rgba(255, 255, 255, 0.3);
            }

            /* Inline code chips */
            .blog-body :not(pre) > code {
                font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Consolas', 'Courier New', monospace;
                font-size: 0.80em;
                padding: 0.15rem 0.4rem;
                border-radius: 0.375rem;
                background: rgba(59, 130, 246, 0.08);
                border: 1px solid rgba(59, 130, 246, 0.15);
                color: #2563eb;
                white-space: nowrap;
            }

            .blog-body :not(pre) > code strong {
                font-weight: 600;
            }

            /* Blockquotes */
            .blog-body blockquote {
                border-left: 3px solid #60a5fa;
                background: rgba(239, 246, 255, 0.6);
                padding: 1.25rem 1.5rem;
                border-radius: 0 0.75rem 0.75rem 0;
                font-style: normal;
                color: #1f2937;
            }

            .blog-body img {
                border-radius: 1rem;
                box-shadow: 0 20px 50px rgba(15, 23, 42, 0.08);
                cursor: zoom-in;
                transition: transform 0.2s;
            }

            .blog-body img:hover {
                transform: scale(1.01);
            }

            .blog-body h2,
            .blog-body h3,
            .blog-body h4 {
                scroll-margin-top: 7rem;
            }

            /* Links */
            .blog-body a:not(.no-underline) {
                text-decoration-color: rgba(59, 130, 246, 0.3);
                text-underline-offset: 3px;
            }

            .blog-body a:not(.no-underline):hover {
                text-decoration-color: rgba(59, 130, 246, 0.6);
            }

            @media (max-width: 1279px) {
                .floating-share {
                    display: none !important;
                }
            }

            /* Back to top button */
            .back-to-top {
                position: fixed;
                bottom: 2rem;
                left: 2rem;
                width: 3rem;
                height: 3rem;
                border-radius: 9999px;
                background: rgba(255, 255, 255, 0.95);
                border: 1px solid rgba(148, 163, 184, 0.3);
                box-shadow: 0 10px 40px rgba(15, 23, 42, 0.1);
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                opacity: 0;
                visibility: hidden;
                transform: translateY(10px);
                transition: all 0.3s ease;
                z-index: 50;
            }

            .back-to-top.visible {
                opacity: 1;
                visibility: visible;
                transform: translateY(0);
            }

            .back-to-top:hover {
                background: #fff;
                border-color: #3b82f6;
                transform: translateY(-3px);
                box-shadow: 0 15px 50px rgba(59, 130, 246, 0.15);
            }

            .back-to-top svg {
                width: 1.25rem;
                height: 1.25rem;
                color: #64748b;
                transition: color 0.2s;
            }

            .back-to-top:hover svg {
                color: #3b82f6;
            }

            /* Floating TOC */
            .floating-toc {
                position: absolute;
                top: 0;
                left: calc(100% + 2rem);
                width: 200px;
                max-height: 60vh;
                z-index: 40;
            }

            .floating-toc.is-fixed {
                position: fixed;
                top: 50%;
                left: calc(50% + 28rem);
                transform: translateY(-50%);
                width: 200px;
            }

            .floating-toc.is-bottom {
                position: absolute;
                top: auto;
                bottom: 0;
                left: calc(100% + 2rem);
                transform: none;
                width: 200px;
            }

            @media (min-width: 1280px) and (max-width: 1439px) {
                .floating-toc {
                    width: 180px;
                }
                .floating-toc.is-fixed {
                    left: calc(50% + 24rem);
                    width: 180px;
                }
                .floating-toc.is-bottom {
                    width: 180px;
                }
            }

            @media (min-width: 1440px) and (max-width: 1599px) {
                .floating-toc {
                    width: 190px;
                }
                .floating-toc.is-fixed {
                    left: calc(50% + 26rem);
                    width: 190px;
                }
                .floating-toc.is-bottom {
                    width: 190px;
                }
            }

            .floating-toc-inner {
                background: rgba(255, 255, 255, 0.95);
                backdrop-filter: blur(10px);
                border: 1px solid rgba(148, 163, 184, 0.3);
                border-radius: 1rem;
                padding: 1rem;
                box-shadow: 0 10px 40px rgba(15, 23, 42, 0.08);
            }

            .floating-toc-inner:hover {
                box-shadow: 0 15px 50px rgba(15, 23, 42, 0.12);
                border-color: rgba(59, 130, 246, 0.3);
            }

            /* TOC active state */
            #toc a.toc-active {
                color: #2563eb;
                font-weight: 500;
            }

            #toc a {
                position: relative;
                padding-left: 0.75rem;
                display: block;
                padding-top: 0.25rem;
                padding-bottom: 0.25rem;
                font-size: 0.8125rem;
                line-height: 1.4;
                color: #64748b;
                text-decoration: none;
                transition: color 0.2s;
            }

            #toc a:hover {
                color: #3b82f6;
            }

            #toc a::before {
                content: '';
                position: absolute;
                left: 0;
                top: 0.35rem;
                bottom: 0.35rem;
                width: 2px;
                background: transparent;
                border-radius: 1px;
                transition: background 0.2s;
            }

            #toc a.toc-active::before {
                background: #3b82f6;
            }

            #toc a.toc-h3 {
                padding-left: 1.25rem;
                font-size: 0.75rem;
                color: #94a3b8;
            }

            #toc a.toc-h3::before {
                left: 0.5rem;
            }

            /* Smooth scroll behavior */
            html {
                scroll-behavior: smooth;
            }

            /* Heading anchor links */
            .blog-body h2,
            .blog-body h3,
            .blog-body h4 {
                position: relative;
            }

            .heading-anchor {
                position: absolute;
                left: -1.5rem;
                top: 50%;
                transform: translateY(-50%);
                opacity: 0;
                color: #94a3b8;
                text-decoration: none;
                font-size: 0.9em;
                font-weight: 400;
                transition: opacity 0.2s, color 0.2s;
                line-height: 1;
            }

            .blog-body h2:hover .heading-anchor,
            .blog-body h3:hover .heading-anchor,
            .blog-body h4:hover .heading-anchor,
            .heading-anchor:focus {
                opacity: 1;
            }

            .heading-anchor:hover {
                color: #3b82f6;
            }

            /* Collapsible code blocks */
            .collapsible-code.collapsed {
                max-height: 400px;
                overflow: hidden;
            }

            .collapsible-code.collapsed::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                right: 0;
                height: 80px;
                background: linear-gradient(to bottom, rgba(30, 30, 30, 0), rgba(30, 30, 30, 0.95));
                border-radius: 0 0 0.75rem 0.75rem;
                pointer-events: none;
                z-index: 3;
            }

            .code-expand-btn {
                display: inline-flex;
                align-items: center;
                gap: 0.35rem;
                margin-top: -0.5rem;
                margin-bottom: 1.5rem;
                padding: 0.35rem 1rem;
                border: 1px solid rgba(148, 163, 184, 0.3);
                border-radius: 9999px;
                background: rgba(248, 250, 252, 1);
                color: rgba(100, 116, 139, 1);
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                font-size: 12px;
                font-weight: 500;
                cursor: pointer;
                transition: all 0.15s;
            }

            .code-expand-btn:hover {
                background: rgba(241, 245, 249, 1);
                border-color: rgba(148, 163, 184, 0.5);
                color: #334155;
            }

            /* Image lightbox */
            .blog-lightbox-overlay {
                position: fixed;
                inset: 0;
                z-index: 100;
                display: flex;
                align-items: center;
                justify-content: center;
                background: rgba(0, 0, 0, 0.85);
                backdrop-filter: blur(8px);
                opacity: 0;
                visibility: hidden;
                transition: opacity 0.25s, visibility 0.25s;
                cursor: zoom-out;
            }

            .blog-lightbox-overlay.active {
                opacity: 1;
                visibility: visible;
            }

            .blog-lightbox-overlay img {
                max-width: 90vw;
                max-height: 90vh;
                border-radius: 0.75rem;
                box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5);
                transform: scale(0.95);
                transition: transform 0.25s;
            }

            .blog-lightbox-overlay.active img {
                transform: scale(1);
            }

            .blog-lightbox-close {
                position: absolute;
                top: 1.5rem;
                right: 1.5rem;
                width: 2.5rem;
                height: 2.5rem;
                border-radius: 9999px;
                background: rgba(255, 255, 255, 0.1);
                border: 1px solid rgba(255, 255, 255, 0.2);
                color: #fff;
                display: flex;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: all 0.2s;
            }

            .blog-lightbox-close:hover {
                background: rgba(255, 255, 255, 0.2);
            }

            /* Time remaining badge */
            .time-remaining {
                position: fixed;
                bottom: 2rem;
                right: 2rem;
                z-index: 50;
                padding: 0.4rem 0.75rem;
                border-radius: 9999px;
                background: rgba(255, 255, 255, 0.95);
                border: 1px solid rgba(148, 163, 184, 0.3);
                box-shadow: 0 10px 40px rgba(15, 23, 42, 0.1);
                font-size: 12px;
                font-weight: 500;
                color: #64748b;
                opacity: 0;
                visibility: hidden;
                transform: translateY(10px);
                transition: all 0.3s ease;
                backdrop-filter: blur(8px);
            }

            .time-remaining.visible {
                opacity: 1;
                visibility: visible;
                transform: translateY(0);
            }

            /* Print styles */
            @media print {
                .reading-progress,
                .back-to-top,
                .floating-share,
                .floating-toc,
                .time-remaining,
                .code-block-header,
                .code-expand-btn,
                .blog-lightbox-overlay,
                nav,
                footer,
                .blog-cta-section {
                    display: none !important;
                }

                .blog-body pre {
                    border: 1px solid #ccc !important;
                    box-shadow: none !important;
                    max-height: none !important;
                    overflow: visible !important;
                    white-space: pre-wrap !important;
                    word-wrap: break-word !important;
                }

                .collapsible-code.collapsed {
                    max-height: none !important;
                    overflow: visible !important;
                }

                .collapsible-code.collapsed::after {
                    display: none !important;
                }

                .blog-body {
                    font-size: 12pt;
                }

                body {
                    background: #fff !important;
                }
            }
        </style>

</head>


<body>
    <div class="reading-progress" id="reading-progress"></div>
    <!-- Back to top button -->
    <button class="back-to-top" id="back-to-top" aria-label="Back to top" title="Back to top">
        <svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"/>
        </svg>
    </button>

    <%- include('../nav') -%>

        <!-- Image lightbox overlay -->
        <div class="blog-lightbox-overlay" id="lightbox-overlay">
            <button class="blog-lightbox-close" aria-label="Close lightbox">
                <svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/></svg>
            </button>
            <img id="lightbox-img" src="" alt="" />
        </div>

        <!-- Time remaining badge -->
        <div class="time-remaining" id="time-remaining">
            <svg class="inline w-3 h-3 mr-1" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
            <span id="time-remaining-text"></span>
        </div>

        <div class="relative isolate overflow-hidden bg-white">

            <!-- Hero / Title Section -->
            <div class="pt-20 pb-12 sm:pt-28 sm:pb-20">
                <div class="mx-auto max-w-4xl px-6 lg:px-8 text-center relative">
                    <h1 class="text-4xl sm:text-5xl font-extrabold tracking-tight text-gray-900 leading-tight">
                        <%= blogPost.title %>
                    </h1>
                    <p class="mt-6 text-xl text-gray-600 leading-relaxed max-w-3xl mx-auto">
                        <%= blogPost.description %>
                    </p>
                    <div class="mt-8 flex flex-wrap items-center justify-center gap-4 text-sm text-gray-500">
                        <% if(blogPost.author){ %>
                        <a href="<%- blogPost.author.githubUrl -%>" target="_blank" class="flex items-center space-x-2 group">
                            <!-- Added explicit width & height to stabilize layout (CLS) -->
                            <img class="h-8 w-8 rounded-full ring-2 ring-gray-200 group-hover:ring-gray-400 transition" src="<%- blogPost.author.profileImageUrl -%>" alt="<%- blogPost.author.name -%>" width="32" height="32" decoding="async">
                            <span class="font-medium text-gray-700 group-hover:text-gray-900 transition">@<%- blogPost.author.username -%></span>
                        </a>
                        <span class="hidden sm:inline select-none">•</span>
                        <span><%- blogPost.formattedPostDate -%></span>
                        <span class="hidden sm:inline select-none">•</span>
                        <% } %>
                        <span id="reading-time" class="inline-flex items-center gap-1.5 px-3 py-1 rounded-full bg-blue-50 border border-blue-100 text-blue-600">
                            <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
                            <span class="sr-only">Reading time</span>
                        </span>
                    </div>
                    <div class="mt-8">
                        <%- include('./Partials/Tags', { blogPost: blogPost }) -%>
                    </div>
                </div>
            </div>

            <!-- Content -->
            <div class="pb-24">
                <div class="mx-auto max-w-4xl px-6 lg:px-8">
                    <div class="relative" id="content-wrapper">
                        <!-- Floating TOC -->
                        <div class="floating-toc hidden xl:block" id="floating-toc">
                            <div class="floating-toc-inner">
                                <h2 class="text-xs font-semibold tracking-wider text-blue-600 uppercase mb-3 flex items-center gap-2">
                                    <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h7"/></svg>
                                    On this page
                                </h2>
                                <nav id="toc" aria-label="Table of contents" class="overflow-y-auto max-h-[50vh] pr-1"></nav>
                            </div>
                        </div>

                        <!-- Main content -->
                        <article>
                            <div class="relative">
                                <div class="floating-share hidden xl:flex" aria-label="Share this article">
                                    <a title="Share on X" target="_blank" rel="noopener" href="https://twitter.com/intent/tweet?text=<%- encodeURIComponent(blogPost.title) -%>&url=<%- encodeURIComponent(blogPost.blogUrl) -%>">
                                        <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><path d="M17.53 3h3.77l-8.26 9.45L23 21h-6.17l-4.8-6.01L6.4 21H2.62l8.63-9.87L1 3h6.32l4.33 5.41L17.53 3Zm-1.33 15.62h2.09L7.94 4.29H5.71l10.49 14.33Z"/></svg>
                                    </a>
                                    <a title="Share on LinkedIn" target="_blank" rel="noopener" href="https://www.linkedin.com/sharing/share-offsite/?url=<%- encodeURIComponent(blogPost.blogUrl) -%>">
                                        <svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.049c.476-.9 1.637-1.85 3.37-1.85 3.601 0 4.266 2.37 4.266 5.455v6.286ZM5.337 7.433a2.062 2.062 0 1 1 0-4.124 2.062 2.062 0 0 1 0 4.124ZM7.119 20.452H3.553V9h3.566v11.452ZM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003Z"/></svg>
                                    </a>
                                    <a title="Discuss on Hacker News" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=<%- encodeURIComponent(blogPost.blogUrl) -%>&t=<%- encodeURIComponent(blogPost.title) -%>">
                                        <svg class="w-4 h-4" viewBox="0 0 24 24" aria-hidden="true">
                                            <circle cx="12" cy="12" r="10" fill="currentColor" opacity="0.92"></circle>
                                            <path d="M7.2 6.2L12 12.8l4.8-6.6M12 13.4V17.8" fill="none" stroke="#ffffff" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"></path>
                                        </svg>
                                    </a>
                                    <button title="Copy link" class="copy-link-btn" data-url="<%- blogPost.blogUrl -%>">
                                        <svg class="w-4 h-4 copy-link-icon" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>
                                        <svg class="w-4 h-4 copy-link-check hidden" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
                                    </button>
                                </div>
                                <div class="blog-body prose prose-gray max-w-none prose-headings:scroll-mt-28 prose-headings:font-display prose-headings:font-semibold lg:prose-headings:scroll-mt-[8.5rem] prose-a:font-semibold prose-a:text-gray-600 hover:prose-a:text-gray-500 prose-img:rounded-xl prose-pre:rounded-xl prose-code:text-gray-600">
                                    <%- blogPost.htmlBody -%>
                                </div>
                            </div>

                            <!-- Share -->
                            <div class="mt-14 border-t border-gray-200/70 pt-8 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-6">
                                <div class="text-sm font-medium text-gray-600">Share this article</div>
                                <div class="flex gap-3">
                                    <a title="Share on X" target="_blank" rel="noopener" class="group inline-flex items-center justify-center h-9 w-9 rounded-lg border border-gray-200 hover:border-gray-400 hover:bg-gray-100 transition" href="https://twitter.com/intent/tweet?text=<%- encodeURIComponent(blogPost.title) -%>&url=<%- encodeURIComponent(blogPost.blogUrl) -%>">
                                        <svg class="w-4 h-4 text-gray-500 group-hover:text-gray-600" viewBox="0 0 24 24" fill="currentColor"><path d="M17.53 3h3.77l-8.26 9.45L23 21h-6.17l-4.8-6.01L6.4 21H2.62l8.63-9.87L1 3h6.32l4.33 5.41L17.53 3Zm-1.33 15.62h2.09L7.94 4.29H5.71l10.49 14.33Z"/></svg>
                                    </a>
                                    <a title="Share on LinkedIn" target="_blank" rel="noopener" class="group inline-flex items-center justify-center h-9 w-9 rounded-lg border border-gray-200 hover:border-gray-400 hover:bg-gray-100 transition" href="https://www.linkedin.com/sharing/share-offsite/?url=<%- encodeURIComponent(blogPost.blogUrl) -%>">
                                        <svg class="w-4 h-4 text-gray-500 group-hover:text-gray-600" viewBox="0 0 24 24" fill="currentColor"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.049c.476-.9 1.637-1.85 3.37-1.85 3.601 0 4.266 2.37 4.266 5.455v6.286ZM5.337 7.433a2.062 2.062 0 1 1 0-4.124 2.062 2.062 0 0 1 0 4.124ZM7.119 20.452H3.553V9h3.566v11.452ZM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003Z"/></svg>
                                    </a>
                                    <a title="Discuss on Hacker News" target="_blank" rel="noopener" class="group inline-flex items-center justify-center h-9 w-9 rounded-lg border border-gray-200 hover:border-gray-400 hover:bg-gray-100 transition" href="https://news.ycombinator.com/submitlink?u=<%- encodeURIComponent(blogPost.blogUrl) -%>&t=<%- encodeURIComponent(blogPost.title) -%>">
                                        <svg class="w-4 h-4 text-gray-500 group-hover:text-gray-600" viewBox="0 0 24 24" aria-hidden="true">
                                            <circle cx="12" cy="12" r="10" fill="currentColor" opacity="0.92"></circle>
                                            <path d="M7.2 6.2L12 12.8l4.8-6.6M12 13.4V17.8" fill="none" stroke="#ffffff" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"></path>
                                        </svg>
                                    </a>
                                    <button title="Copy link" class="copy-link-btn group inline-flex items-center justify-center h-9 w-9 rounded-lg border border-gray-200 hover:border-gray-400 hover:bg-gray-100 transition" data-url="<%- blogPost.blogUrl -%>">
                                        <svg class="w-4 h-4 text-gray-500 group-hover:text-gray-600 copy-link-icon" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>
                                        <svg class="w-4 h-4 text-green-500 copy-link-check hidden" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>
                                    </button>
                                </div>
                            </div>

                            <!-- Author -->
                            <% if(blogPost.author){ %>
                            <div class="mt-16 p-6 sm:p-8 rounded-2xl bg-gradient-to-br from-gray-50 to-blue-50/30 border border-gray-200 shadow-sm hover:shadow-md hover:border-blue-200/60 transition-all duration-300">
                                <div class="flex flex-col sm:flex-row gap-5 sm:gap-6 items-start">
                                    <!-- Added explicit width/height + lazy loading for below-the-fold author bio image -->
                                    <div class="relative">
                                        <img class="h-16 w-16 sm:h-20 sm:w-20 rounded-full ring-4 ring-white shadow-md" src="<%- blogPost.author.profileImageUrl -%>" alt="<%- blogPost.author.name -%>" width="80" height="80" loading="lazy" decoding="async">
                                        <div class="absolute -bottom-1 -right-1 h-6 w-6 bg-emerald-400 rounded-full border-2 border-white flex items-center justify-center">
                                            <svg class="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/></svg>
                                        </div>
                                    </div>
                                    <div class="flex-1">
                                        <div class="flex items-center gap-2 mb-1">
                                            <h3 class="text-lg font-semibold text-gray-900"><%- blogPost.author.name -%></h3>
                                            <span class="text-xs font-medium text-blue-600 bg-blue-50 px-2 py-0.5 rounded-full">Author</span>
                                        </div>
                                        <p class="text-sm text-gray-500 mb-3">@<%- blogPost.author.username -%> • <%- blogPost.formattedPostDate -%> • <span id="reading-time-inline"></span></p>
                                        <div class="text-sm text-gray-600 leading-relaxed"><%- blogPost.author.bio || 'Building reliable software at OneUptime. Follow along for more on observability & reliability.' -%></div>
                                        <div class="mt-4 flex flex-wrap gap-3">
                                            <a href="<%- blogPost.author.githubUrl -%>" target="_blank" class="text-xs font-medium text-gray-600 hover:text-gray-900 inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full bg-white border border-gray-200 hover:border-gray-300 shadow-sm transition-all duration-200">
                                                <svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/></svg>
                                                GitHub
                                            </a>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <% } %>

                            <div class="mt-20">
                                <%- include('./Partials/OpenSourceCommitment', { blogPost: blogPost }) -%>
                            </div>
                        </article>
                    </div>
                </div>
            </div>
        </div>




        <%- include('./Partials/BlogCta') -%>
            <%- include('../footer') -%>
            <script>
                (function(){
                    // Language name mapping for display
                    var langNames = {
                        'js': 'JavaScript', 'javascript': 'JavaScript', 'ts': 'TypeScript', 'typescript': 'TypeScript',
                        'py': 'Python', 'python': 'Python', 'rb': 'Ruby', 'ruby': 'Ruby',
                        'go': 'Go', 'java': 'Java', 'css': 'CSS', 'html': 'HTML', 'xml': 'XML',
                        'json': 'JSON', 'yaml': 'YAML', 'yml': 'YAML', 'sql': 'SQL',
                        'bash': 'Bash', 'shell': 'Shell', 'sh': 'Shell', 'zsh': 'Shell',
                        'dockerfile': 'Dockerfile', 'docker': 'Dockerfile',
                        'rust': 'Rust', 'cpp': 'C++', 'c': 'C', 'csharp': 'C#',
                        'php': 'PHP', 'graphql': 'GraphQL', 'http': 'HTTP',
                        'markdown': 'Markdown', 'md': 'Markdown', 'plaintext': 'Text'
                    };

                    // Add copy button and language label to code blocks
                    try {
                        var codeBlocks = document.querySelectorAll('.blog-body pre');
                        codeBlocks.forEach(function(pre) {
                            var codeEl = pre.querySelector('code');
                            if(!codeEl) return;

                            // Detect language from class
                            var lang = '';
                            var classes = (codeEl.className || '').split(/\s+/);
                            for(var i = 0; i < classes.length; i++){
                                var m = classes[i].match(/^(?:language-|hljs-)(.+)$/);
                                if(m && m[1] !== 'hljs'){ lang = m[1]; break; }
                            }
                            if(!lang && codeEl.dataset && codeEl.dataset.highlightedLanguage){
                                lang = codeEl.dataset.highlightedLanguage;
                            }

                            var displayLang = langNames[lang] || (lang ? lang.charAt(0).toUpperCase() + lang.slice(1) : '');

                            // Create header bar
                            var header = document.createElement('div');
                            header.className = 'code-block-header';

                            // Language label
                            var labelEl = document.createElement('span');
                            labelEl.className = 'code-lang-label';
                            labelEl.textContent = displayLang;
                            header.appendChild(labelEl);

                            // Copy button
                            var copyBtn = document.createElement('button');
                            copyBtn.className = 'code-copy-btn';
                            copyBtn.setAttribute('aria-label', 'Copy code');
                            var copyIcon = '<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>';
                            var checkIcon = '<svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/></svg>';
                            copyBtn.innerHTML = copyIcon + '<span>Copy</span>';
                            copyBtn.addEventListener('click', function(){
                                var text = codeEl.textContent || '';
                                navigator.clipboard.writeText(text).then(function(){
                                    copyBtn.classList.add('copied');
                                    copyBtn.innerHTML = checkIcon + '<span>Copied!</span>';
                                    setTimeout(function(){
                                        copyBtn.classList.remove('copied');
                                        copyBtn.innerHTML = copyIcon + '<span>Copy</span>';
                                    }, 2000);
                                });
                            });
                            header.appendChild(copyBtn);

                            pre.style.position = 'relative';
                            pre.insertBefore(header, pre.firstChild);
                        });
                    } catch(e) {}

                    // Reading time
                    try {
                        const container = document.querySelector('.blog-body');
                        if(container){
                            const text = container.textContent || '';
                            const words = text.trim().split(/\s+/).filter(Boolean).length;
                            const minutes = Math.max(1, Math.round(words / 200));
                            const rt = minutes + ' min read';
                            const el = document.getElementById('reading-time');
                            if(el){ el.insertAdjacentText('beforeend', rt); }
                            const el2 = document.getElementById('reading-time-inline');
                            if(el2){ el2.textContent = rt; }
                        }
                    } catch (e) {}

                    // Reading progress indicator
                    try {
                        const progressBar = document.getElementById('reading-progress');
                        const target = document.querySelector('.blog-body');
                        if(progressBar && target){
                            let start = 0;
                            let end = 0;
                            const recalc = () => {
                                const rect = target.getBoundingClientRect();
                                const scrollTop = window.scrollY || window.pageYOffset;
                                start = scrollTop + rect.top - 120;
                                end = start + target.offsetHeight;
                            };
                            const update = () => {
                                if(!end){ recalc(); }
                                const scrollPos = window.scrollY || window.pageYOffset;
                                const distance = end - start;
                                if(distance <= 0){ return; }
                                const progress = Math.min(1, Math.max(0, (scrollPos - start) / distance));
                                progressBar.style.width = (progress * 100).toFixed(2) + '%';
                            };
                            recalc();
                            window.addEventListener('scroll', update, { passive: true });
                            window.addEventListener('resize', () => {
                                end = 0;
                                recalc();
                                update();
                            });
                            update();
                        }
                    } catch (e) {}

                    // TOC with active state tracking and sticky positioning
                    try {
                        const container = document.querySelector('.blog-body');
                        const contentWrapper = document.getElementById('content-wrapper');
                        if(!container){ return; }
                        const headings = container.querySelectorAll('h2');
                        if(!headings.length){ return; }
                        const toc = document.getElementById('toc');
                        const floatingToc = document.getElementById('floating-toc');
                        if(!toc){ return; }
                        const tocLinks = [];
                        headings.forEach(h => {
                            if(!h.id){
                                h.id = h.textContent.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'');
                            }
                            const a = document.createElement('a');
                            a.href = '#' + h.id;
                            a.textContent = h.textContent;
                            a.className = h.tagName === 'H3' ? 'toc-h3' : '';
                            a.dataset.target = h.id;
                            toc.appendChild(a);
                            tocLinks.push({ link: a, heading: h });
                        });

                        // Track active TOC item and position on scroll
                        const updateToc = () => {
                            const scrollPos = window.scrollY + 150;
                            let activeIndex = 0;
                            tocLinks.forEach((item, i) => {
                                const rect = item.heading.getBoundingClientRect();
                                const top = rect.top + window.scrollY;
                                if(scrollPos >= top){
                                    activeIndex = i;
                                }
                            });
                            tocLinks.forEach((item, i) => {
                                if(i === activeIndex){
                                    item.link.classList.add('toc-active');
                                } else {
                                    item.link.classList.remove('toc-active');
                                }
                            });

                            // Handle TOC positioning: top (absolute), fixed, or bottom (absolute)
                            if(floatingToc && contentWrapper){
                                const wrapperRect = contentWrapper.getBoundingClientRect();
                                const tocHeight = floatingToc.offsetHeight;
                                const viewportHeight = window.innerHeight;
                                const fixedTopPosition = (viewportHeight - tocHeight) / 2;

                                // When content top is above the point where TOC would be centered
                                const shouldBeFixed = wrapperRect.top < fixedTopPosition && wrapperRect.bottom > (fixedTopPosition + tocHeight);

                                // When content bottom is above where the fixed TOC would end
                                const shouldBeBottom = wrapperRect.bottom <= (fixedTopPosition + tocHeight);

                                floatingToc.classList.remove('is-fixed', 'is-bottom');

                                if(shouldBeBottom){
                                    floatingToc.classList.add('is-bottom');
                                } else if(shouldBeFixed){
                                    floatingToc.classList.add('is-fixed');
                                }
                                // else: stays at absolute top (default)
                            }
                        };
                        window.addEventListener('scroll', updateToc, { passive: true });
                        window.addEventListener('resize', updateToc, { passive: true });
                        updateToc();
                    } catch (e) {}

                    // Back to top button
                    try {
                        const backToTopBtn = document.getElementById('back-to-top');
                        if(backToTopBtn){
                            const toggleVisibility = () => {
                                if(window.scrollY > 400){
                                    backToTopBtn.classList.add('visible');
                                } else {
                                    backToTopBtn.classList.remove('visible');
                                }
                            };
                            window.addEventListener('scroll', toggleVisibility, { passive: true });
                            toggleVisibility();
                            backToTopBtn.addEventListener('click', () => {
                                window.scrollTo({ top: 0, behavior: 'smooth' });
                            });
                        }
                    } catch (e) {}

                    // Stabilize images inside blog body (add width/height if missing & lazy-load non-hero images)
                    try {
                        const imgs = document.querySelectorAll('.blog-body img');
                        imgs.forEach((img, index) => {
                            // If width/height already specified, skip dimension inference
                            if(!img.hasAttribute('width') || !img.hasAttribute('height')){
                                if(img.naturalWidth && img.naturalHeight){
                                    img.setAttribute('width', img.naturalWidth);
                                    img.setAttribute('height', img.naturalHeight);
                                } else {
                                    // If not yet loaded, attach a one-time listener
                                    img.addEventListener('load', function handler(){
                                        if(!img.hasAttribute('width') && img.naturalWidth){ img.setAttribute('width', img.naturalWidth); }
                                        if(!img.hasAttribute('height') && img.naturalHeight){ img.setAttribute('height', img.naturalHeight); }
                                        img.removeEventListener('load', handler);
                                    });
                                }
                            }
                            // Lazy-load images after the first one (often top/hero) for better LCP while reducing CLS
                            if(index > 0 && !img.hasAttribute('loading')){
                                img.setAttribute('loading', 'lazy');
                            }
                            if(!img.hasAttribute('decoding')){
                                img.setAttribute('decoding', 'async');
                            }
                            // Ensure display block for centered images without causing late shifts
                            if(!img.className.includes('inline') && !img.className.includes('block')){
                                img.classList.add('block');
                            }
                        });
                    } catch(e) {}

                    // Mermaid diagrams
                    try {
                        const mermaidCodeBlocks = document.querySelectorAll('.blog-body pre code.language-mermaid, .blog-body pre code.mermaid, .blog-body pre code[class*="language-mermaid"], .blog-body pre code[class*="mermaid"]');
                        if(mermaidCodeBlocks.length){
                            // Dynamically load Mermaid only if needed to avoid extra payload on pages without diagrams
                            const script = document.createElement('script');
                            script.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
                            script.async = true;
                            script.onload = () => {
                                if(!window.mermaid){ return; }
                                const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
                                window.mermaid.initialize({
                                    startOnLoad: false,
                                    securityLevel: 'loose', // Content is trusted (owned blog); allows links & styling
                                    theme: prefersDark ? 'dark' : 'default'
                                });
                                mermaidCodeBlocks.forEach((codeEl, i) => {
                                    // Avoid double-processing
                                    if(codeEl.dataset.mermaidProcessed){ return; }
                                    const pre = codeEl.closest('pre');
                                    if(!pre){ return; }
                                    const def = codeEl.textContent.trim(); // Use textContent to ignore highlight.js span wrappers
                                    const container = document.createElement('div');
                                    container.className = 'mermaid my-6';
                                    // Provide a deterministic ID (optional)
                                    container.id = 'mermaid-diagram-' + (i+1);
                                    container.textContent = def;
                                    pre.replaceWith(container);
                                    codeEl.dataset.mermaidProcessed = 'true';
                                });
                                try { window.mermaid.run && window.mermaid.run(); } catch(_) {}
                            };
                            document.head.appendChild(script);
                        }
                    } catch(e) {}

                    // Anchor links on headings
                    try {
                        var headingsForAnchors = document.querySelectorAll('.blog-body h1, .blog-body h2, .blog-body h3, .blog-body h4');
                        headingsForAnchors.forEach(function(h) {
                            if(!h.id){
                                h.id = h.textContent.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,'');
                            }
                            h.style.position = 'relative';
                            var anchor = document.createElement('a');
                            anchor.href = '#' + h.id;
                            anchor.className = 'heading-anchor';
                            anchor.setAttribute('aria-label', 'Link to ' + h.textContent);
                            anchor.innerHTML = '<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/></svg>';
                            h.appendChild(anchor);
                        });
                    } catch(e) {}

                    // Image lightbox
                    try {
                        var overlay = document.getElementById('lightbox-overlay');
                        var lightboxImg = document.getElementById('lightbox-img');
                        if(overlay && lightboxImg) {
                            var blogImages = document.querySelectorAll('.blog-body img');
                            blogImages.forEach(function(img) {
                                img.addEventListener('click', function() {
                                    lightboxImg.src = img.src;
                                    lightboxImg.alt = img.alt || '';
                                    overlay.classList.add('active');
                                    document.body.style.overflow = 'hidden';
                                });
                            });
                            var closeLightbox = function() {
                                overlay.classList.remove('active');
                                document.body.style.overflow = '';
                                lightboxImg.src = '';
                            };
                            overlay.addEventListener('click', function(e) {
                                if(e.target === overlay || e.target.closest('.blog-lightbox-close')) {
                                    closeLightbox();
                                }
                            });
                            document.addEventListener('keydown', function(e) {
                                if(e.key === 'Escape' && overlay.classList.contains('active')) {
                                    closeLightbox();
                                }
                            });
                        }
                    } catch(e) {}

                    // Collapsible long code blocks (>100 lines)
                    try {
                        var allPres = document.querySelectorAll('.blog-body pre');
                        allPres.forEach(function(pre) {
                            var codeEl = pre.querySelector('code');
                            if(!codeEl) return;
                            var lineCount = (codeEl.textContent || '').split('\n').length;
                            if(lineCount > 100) {
                                pre.classList.add('collapsible-code');
                                pre.classList.add('collapsed');
                                var expandBtn = document.createElement('button');
                                expandBtn.className = 'code-expand-btn';
                                expandBtn.innerHTML = '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg> Show all ' + lineCount + ' lines';
                                expandBtn.addEventListener('click', function() {
                                    if(pre.classList.contains('collapsed')) {
                                        pre.classList.remove('collapsed');
                                        expandBtn.innerHTML = '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M5 15l7-7 7 7"/></svg> Show less';
                                    } else {
                                        pre.classList.add('collapsed');
                                        expandBtn.innerHTML = '<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/></svg> Show all ' + lineCount + ' lines';
                                        pre.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
                                    }
                                });
                                pre.parentNode.insertBefore(expandBtn, pre.nextSibling);
                            }
                        });
                    } catch(e) {}

                    // Time remaining indicator
                    try {
                        var blogBody = document.querySelector('.blog-body');
                        var timeRemainingEl = document.getElementById('time-remaining');
                        var timeRemainingText = document.getElementById('time-remaining-text');
                        if(blogBody && timeRemainingEl && timeRemainingText) {
                            var totalWords = (blogBody.textContent || '').trim().split(/\s+/).filter(Boolean).length;
                            var wpm = 200;
                            var updateTimeRemaining = function() {
                                var rect = blogBody.getBoundingClientRect();
                                var scrollTop = window.scrollY || window.pageYOffset;
                                var bodyTop = scrollTop + rect.top;
                                var bodyHeight = blogBody.offsetHeight;
                                var progress = Math.min(1, Math.max(0, (scrollTop - bodyTop + window.innerHeight * 0.5) / bodyHeight));
                                var wordsRemaining = Math.max(0, Math.round(totalWords * (1 - progress)));
                                var minsRemaining = Math.max(0, Math.ceil(wordsRemaining / wpm));

                                if(progress > 0.02 && progress < 0.95) {
                                    timeRemainingEl.classList.add('visible');
                                    if(minsRemaining <= 1) {
                                        timeRemainingText.textContent = 'Less than 1 min left';
                                    } else {
                                        timeRemainingText.textContent = minsRemaining + ' min remaining';
                                    }
                                } else {
                                    timeRemainingEl.classList.remove('visible');
                                }
                            };
                            window.addEventListener('scroll', updateTimeRemaining, { passive: true });
                            updateTimeRemaining();
                        }
                    } catch(e) {}

                    // Copy link buttons
                    try {
                        var copyLinkBtns = document.querySelectorAll('.copy-link-btn');
                        copyLinkBtns.forEach(function(btn) {
                            btn.addEventListener('click', function() {
                                var url = btn.dataset.url || window.location.href;
                                navigator.clipboard.writeText(url).then(function() {
                                    var icon = btn.querySelector('.copy-link-icon');
                                    var check = btn.querySelector('.copy-link-check');
                                    if(icon) icon.classList.add('hidden');
                                    if(check) check.classList.remove('hidden');
                                    setTimeout(function() {
                                        if(icon) icon.classList.remove('hidden');
                                        if(check) check.classList.add('hidden');
                                    }, 2000);
                                });
                            });
                        });
                    } catch(e) {}
                })();
            </script>
  <%- include("../Partials/cta-tracking") -%>
</body>

</html>