1:"$Sreact.fragment" a:I[65488,["4219","static/chunks/app/global-error-9c48b408a6bb9cc8.js"],"default"] :HL["/_next/static/media/1a4aa50920b5315c-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/558ca1a6aa3cb55e-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/media/982ceffe7b733b3b-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] :HL["/_next/static/css/1d5613aa0edf9320.css","style"] :HL["/_next/static/css/4541afd60ae2ab49.css","style"] :HL["/_next/static/css/3e2250ad879c9632.css","style"] 2:Ta59, (function() { function getSystemTheme() { // Check if user prefers dark mode if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { return 'scripthammer-dark'; } return 'scripthammer-light'; } function applyTheme(theme) { if (!theme) { try { // First check if user has manually selected a theme theme = localStorage.getItem('theme'); // If no saved theme, use system preference if (!theme) { theme = getSystemTheme(); } } catch (e) { // Fallback if localStorage is not available theme = getSystemTheme(); } } document.documentElement.setAttribute('data-theme', theme); // Also set on body as backup if (document.body) { document.body.setAttribute('data-theme', theme); } return theme; } // Apply theme immediately on initial load var currentTheme = applyTheme(); // Reapply when DOM is ready (only on initial load) if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { applyTheme(currentTheme); }); } // Listen for storage changes (theme changes from other tabs/windows) window.addEventListener('storage', function(e) { if (e.key === 'theme' && e.newValue) { currentTheme = e.newValue; applyTheme(currentTheme); } }); // Listen for custom theme change events (from same tab) window.addEventListener('themechange', function(e) { if (e.detail && e.detail.theme) { currentTheme = e.detail.theme; applyTheme(currentTheme); } }); // Fallback: watch for body element if it doesn't exist yet if (!document.body) { var observer = new MutationObserver(function(mutations) { if (document.body) { applyTheme(currentTheme); observer.disconnect(); } }); observer.observe(document.documentElement, { childList: true, subtree: true }); } // Listen for system theme changes if (window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) { // Only apply system theme if user hasn't manually selected a theme if (!localStorage.getItem('theme')) { currentTheme = getSystemTheme(); applyTheme(currentTheme); } }); } })(); 0:{"P":null,"b":"cDbOpnea0t2iU92r0zCb5","p":"","c":["","blog",""],"i":false,"f":[[["",{"children":["blog",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/1d5613aa0edf9320.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","1",{"rel":"stylesheet","href":"/_next/static/css/4541afd60ae2ab49.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}],["$","link","2",{"rel":"stylesheet","href":"/_next/static/css/3e2250ad879c9632.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"__variable_b9057e __variable_93a132 __variable_898c26","suppressHydrationWarning":true,"children":["$","body",null,{"className":"flex min-h-screen flex-col antialiased","suppressHydrationWarning":true,"children":[["$","script",null,{"dangerouslySetInnerHTML":{"__html":"$2"}}],"$L3","$L4","$L5","$L6"]}]}]]}],{"children":["blog","$L7",{"children":["__PAGE__","$L8",{},null,false]},null,false]},null,false],"$L9",false]],"m":"$undefined","G":["$a",[]],"s":false,"S":true} c:I[34446,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"ColorblindFilters"] d:I[62963,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"ConsentProvider"] e:I[22617,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"default"] f:I[64306,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"default"] 10:I[74468,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"AuthProvider"] 11:I[89405,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"AccessibilityProvider"] 12:I[62911,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"GlobalNav"] 13:I[17193,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"CountdownBanner"] 14:I[31144,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"SetupBanner"] 15:I[81851,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"default"] 16:I[16239,[],""] 17:I[41107,[],""] 18:I[35958,["3831","static/chunks/app/blog/page-c6a59bcc51a632e3.js"],""] 19:I[40417,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"CookieConsent"] 1a:I[50081,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"ConsentModal"] 1b:I[87698,["7177","static/chunks/app/layout-cde01688422f6a27.js"],"default"] 1d:I[67942,[],"OutletBoundary"] 1f:I[25243,[],"AsyncMetadataOutlet"] 21:I[67942,[],"ViewportBoundary"] 23:I[67942,[],"MetadataBoundary"] 24:"$Sreact.suspense" b:Tf8b, (function() { var SCALE = {"small":1.25,"medium":1.5,"large":1.75,"x-large":2.125}; var LINE = {"compact":"1.25","normal":"1.5","relaxed":"1.75"}; var FAMILY = {"sans-serif":"var(--font-archivo), ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif","serif":"ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif","mono":"var(--font-jetbrains), ui-monospace, SFMono-Regular, \"SF Mono\", Consolas, \"Liberation Mono\", Menlo, monospace"}; var DISPLAY = {"sans-serif":"var(--font-archivo-black), var(--font-archivo), ui-sans-serif, system-ui, sans-serif","serif":"ui-serif, Georgia, Cambria, \"Times New Roman\", Times, serif","mono":"var(--font-jetbrains), ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, monospace"}; var DEFAULTS = {"fontSize":"medium","lineHeight":"normal","fontFamily":"sans-serif","highContrast":"normal","reduceMotion":"no-preference"}; var KEYS = {"fontSize":"fontSize","lineHeight":"lineHeight","fontFamily":"fontFamily","highContrast":"highContrast","reduceMotion":"reduceMotion"}; var CONSENT_KEY = "cookie-consent"; // Mirrors canUseCookies(CookieCategory.FUNCTIONAL) from utils/consent. // The provider picks its store the same way; reading the wrong one here // would reintroduce exactly the flash this script exists to remove. function canPersist() { try { var raw = localStorage.getItem(CONSENT_KEY); if (!raw) return false; return JSON.parse(raw).functional === true; } catch (e) { return false; } } function read(storage, key, fallback) { try { return storage.getItem(key) || fallback; } catch (e) { return fallback; } } try { var storage = canPersist() ? localStorage : sessionStorage; var fontSize = read(storage, KEYS.fontSize, DEFAULTS.fontSize); var lineHeight = read(storage, KEYS.lineHeight, DEFAULTS.lineHeight); var fontFamily = read(storage, KEYS.fontFamily, DEFAULTS.fontFamily); var reduceMotion = read(storage, KEYS.reduceMotion, DEFAULTS.reduceMotion); // A stored value could be stale or hand-edited; fall back rather than // writing "undefined" into a custom property. var scale = SCALE[fontSize] || SCALE[DEFAULTS.fontSize]; var leading = LINE[lineHeight] || LINE[DEFAULTS.lineHeight]; var family = FAMILY[fontFamily] || FAMILY[DEFAULTS.fontFamily]; var display = DISPLAY[fontFamily] || DISPLAY[DEFAULTS.fontFamily]; var root = document.documentElement; root.style.setProperty('--font-scale-factor', String(scale)); // Font choice rides on custom properties now (#377), which means it can // be applied to immediately instead of waiting for to // exist. globals.css maps these onto body and h1-h6. root.style.setProperty('--sh-font-body', family); root.style.setProperty('--sh-font-display', display); if (reduceMotion === 'reduce') { root.setAttribute('data-reduce-motion', 'true'); } // Line height is still an inline body style, so it still has to wait // for body to exist. It is the remaining cause of visible reflow. function applyBody() { if (!document.body) return false; document.body.style.lineHeight = leading; return true; } if (!applyBody()) { var observer = new MutationObserver(function () { if (applyBody()) observer.disconnect(); }); observer.observe(document.documentElement, { childList: true, subtree: true, }); } } catch (e) { // Never block paint on a storage or parse failure — the CSS defaults // already match the default setting. } })(); 3:["$","script",null,{"dangerouslySetInnerHTML":{"__html":"$b"}}] 4:["$","script",null,{"type":"application/ld+json","dangerouslySetInnerHTML":{"__html":"{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"ScriptHammer\",\"description\":\"A production Next.js and Supabase platform with auth, payments, encrypted messaging, and an accessible offline-capable PWA\",\"url\":\"https://scripthammer.com\",\"publisher\":{\"@type\":\"Organization\",\"name\":\"TortoiseWolfe\",\"url\":\"https://github.com/TortoiseWolfe/ScriptHammer\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://scripthammer.com/favicon.svg\"}},\"potentialAction\":{\"@type\":\"SearchAction\",\"target\":\"https://scripthammer.com/search?q={search_term_string}\",\"query-input\":\"required name=search_term_string\"}}"}}] 5:["$","$Lc",null,{}] 6:["$","$Ld",null,{"children":[["$","$Le",null,{}],["$","$Lf",null,{}],["$","$L10",null,{"children":["$","$L11",null,{"children":[["$","a",null,{"href":"#main-content","data-skip-link":true,"className":"btn btn-sm btn-primary sr-only min-h-11 min-w-11 focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50","children":"Skip to main content"}],["$","$L12",null,{}],["$","$L13",null,{}],["$","$L14",null,{}],["$","$L15",null,{"level":"page","children":["$","div",null,{"id":"main-content","tabIndex":-1,"className":"bg-base-200 min-h-0 flex-1 overflow-x-clip","children":["$","$L16",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L17",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","main",null,{"className":"flex min-h-full flex-col items-center justify-center","children":["$","div",null,{"className":"text-center","children":[["$","h1",null,{"className":"mb-4 text-6xl font-bold","children":"404"}],["$","h2",null,{"className":"mb-4 text-2xl","children":"Page Not Found"}],["$","p",null,{"className":"text-base-content mb-8","children":"The page you are looking for doesn't exist."}],["$","$L18",null,{"href":"/","className":"btn btn-primary","children":"Go Home"}]]}]}],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}],["$","footer",null,{"data-site-footer":true,"className":"bg-base-300 mt-auto py-4 text-center sm:py-6","children":["$","div",null,{"className":"container mx-auto px-4 sm:px-6 lg:px-8","children":[["$","p",null,{"className":"text-base-content text-sm leading-relaxed","children":["Made by"," ",["$","a",null,{"href":"https://crudgames.com","target":"_blank","rel":"noopener noreferrer","className":"link-hover link inline-block min-h-11 align-middle leading-11 font-medium","children":"CRUDgames.com"}]," ","for"," ",["$","a",null,{"href":"https://geolarp.com","target":"_blank","rel":"noopener noreferrer","className":"link-hover link inline-block min-h-11 align-middle leading-11 font-medium","children":"geoLARP.com"}]]}],["$","p",null,{"className":"text-base-content mt-1 text-xs","children":["Built with"," ",["$","a",null,{"href":"https://github.com/TortoiseWolfe/ScriptHammer","target":"_blank","rel":"noopener noreferrer","className":"link-hover link","children":"ScriptHammer"}]," ","template"]}]]}]}],["$","$L19",null,{}],["$","$L1a",null,{}],["$","$L1b",null,{}],false]}]}]]}] 7:["$","$1","c",{"children":[null,["$","$L16",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L17",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":"$undefined","forbidden":"$undefined","unauthorized":"$undefined"}]]}] 8:["$","$1","c",{"children":["$L1c",null,["$","$L1d",null,{"children":["$L1e",["$","$L1f",null,{"promise":"$@20"}]]}]]}] 9:["$","$1","h",{"children":[null,[["$","$L21",null,{"children":"$L22"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$L23",null,{"children":["$","div",null,{"hidden":true,"children":["$","$24",null,{"fallback":null,"children":"$L25"}]}]}]]}] 22:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, maximum-scale=5, viewport-fit=cover"}],["$","meta","2",{"name":"theme-color","media":"(prefers-color-scheme: light)","content":"#f5f0eb"}],["$","meta","3",{"name":"theme-color","media":"(prefers-color-scheme: dark)","content":"#1a1a2e"}]] 1e:null 20:{"metadata":[["$","title","0",{"children":"Blog"}],["$","meta","1",{"name":"description","content":"Read our latest blog posts and insights"}],["$","meta","2",{"name":"application-name","content":"ScriptHammer"}],["$","meta","3",{"name":"author","content":"TortoiseWolfe"}],["$","link","4",{"rel":"manifest","href":"/manifest.json","crossOrigin":"$undefined"}],["$","meta","5",{"name":"generator","content":"Next.js"}],["$","meta","6",{"name":"keywords","content":"Next.js,React,TypeScript,PWA,DaisyUI,Next.js,React,TypeScript,PWA,DaisyUI,TailwindCSS"}],["$","meta","7",{"name":"referrer","content":"origin-when-cross-origin"}],["$","meta","8",{"name":"creator","content":"TortoiseWolfe"}],["$","meta","9",{"name":"publisher","content":"TortoiseWolfe"}],["$","meta","10",{"name":"robots","content":"index, follow"}],["$","meta","11",{"name":"googlebot","content":"index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"}],["$","meta","12",{"name":"Cache-Control","content":"no-cache, no-store, must-revalidate"}],["$","meta","13",{"name":"Pragma","content":"no-cache"}],["$","meta","14",{"name":"Expires","content":"0"}],["$","meta","15",{"name":"Content-Security-Policy","content":"default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://*.google-analytics.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https: blob:; font-src 'self' data:; connect-src 'self' https://www.googleapis.com https://*.google-analytics.com https://tile.openstreetmap.org https://*.tile.openstreetmap.org https://*.supabase.co wss://*.supabase.co https://*.basemaps.cartocdn.com https://api.web3forms.com https://*.ingest.sentry.io https://*.ingest.us.sentry.io https://challenges.cloudflare.com; frame-src 'self' https://www.google.com https://challenges.cloudflare.com; object-src 'none'; base-uri 'self'; form-action 'self' https://api.web3forms.com; upgrade-insecure-requests"}],["$","link","16",{"rel":"canonical","href":"https://scripthammer.com/"}],["$","meta","17",{"name":"format-detection","content":"telephone=no, address=no, email=no"}],["$","meta","18",{"name":"mobile-web-app-capable","content":"yes"}],["$","meta","19",{"name":"apple-mobile-web-app-title","content":"ScriptHammer"}],["$","meta","20",{"name":"apple-mobile-web-app-status-bar-style","content":"default"}],["$","meta","21",{"property":"og:title","content":"ScriptHammer - Modern Web Starter"}],["$","meta","22",{"property":"og:description","content":"A production Next.js and Supabase platform with auth, payments, encrypted messaging, and an accessible offline-capable PWA"}],["$","meta","23",{"property":"og:url","content":"https://scripthammer.com/"}],["$","meta","24",{"property":"og:site_name","content":"ScriptHammer"}],["$","meta","25",{"property":"og:locale","content":"en_US"}],["$","meta","26",{"property":"og:image","content":"https://scripthammer.com/opengraph-image.png"}],["$","meta","27",{"property":"og:image:width","content":"1200"}],["$","meta","28",{"property":"og:image:height","content":"630"}],["$","meta","29",{"property":"og:image:alt","content":"ScriptHammer - A production Next.js and Supabase platform with auth, payments, encrypted messaging, and an accessible offline-capable PWA"}],["$","meta","30",{"property":"og:type","content":"website"}],["$","meta","31",{"name":"twitter:card","content":"summary_large_image"}],["$","meta","32",{"name":"twitter:site","content":"@TortoiseWolfe"}],["$","meta","33",{"name":"twitter:creator","content":"@TortoiseWolfe"}],["$","meta","34",{"name":"twitter:title","content":"ScriptHammer - Modern Web Starter"}],["$","meta","35",{"name":"twitter:description","content":"A production Next.js and Supabase platform with auth, payments, encrypted messaging, and an accessible offline-capable PWA"}],["$","meta","36",{"name":"twitter:image","content":"https://scripthammer.com/opengraph-image.png"}],["$","link","37",{"rel":"icon","href":"/favicon.ico"}],"$L26","$L27","$L28"],"error":null,"digest":"$undefined"} 29:I[38435,[],"IconMark"] 26:["$","link","38",{"rel":"icon","href":"/icon.svg"}] 27:["$","link","39",{"rel":"apple-touch-icon","href":"/apple-touch-icon.png"}] 28:["$","$L29","40",{}] 25:"$20:metadata" 2a:I[15675,["3831","static/chunks/app/blog/page-c6a59bcc51a632e3.js"],"Image"] 1c:["$","main",null,{"className":"container mx-auto px-4 py-6 sm:px-6 sm:py-8 md:py-12 lg:px-8","children":[["$","header",null,{"className":"mb-8 flex flex-wrap items-end justify-between gap-4 sm:mb-10 md:mb-12","children":[["$","div",null,{"children":[["$","p",null,{"className":"text-base-content mb-2 font-mono text-xs tracking-wider uppercase","children":"Build log"}],["$","h1",null,{"className":"text-base-content font-display text-4xl tracking-tight sm:text-5xl","children":"Notes from the shop floor"}]]}],["$","nav",null,{"aria-label":"Filter posts by tag","className":"sh-rail flex-wrap","children":[["$","span",null,{"className":"sh-rail-active text-base-content inline-flex min-h-11 items-center rounded-full px-3.5 font-mono text-[11px] tracking-[.1em] uppercase","children":"All"}],[["$","$L18","supabase",{"href":"/blog/tags/supabase","className":"text-base-content hover:bg-base-200 inline-flex min-h-11 items-center rounded-full px-3.5 font-mono text-[11px] tracking-[.1em] uppercase transition-colors","children":"supabase"}],["$","$L18","typescript",{"href":"/blog/tags/typescript","className":"text-base-content hover:bg-base-200 inline-flex min-h-11 items-center rounded-full px-3.5 font-mono text-[11px] tracking-[.1em] uppercase transition-colors","children":"typescript"}],["$","$L18","ai-agents",{"href":"/blog/tags/ai-agents","className":"text-base-content hover:bg-base-200 inline-flex min-h-11 items-center rounded-full px-3.5 font-mono text-[11px] tracking-[.1em] uppercase transition-colors","children":"ai-agents"}],["$","$L18","github",{"href":"/blog/tags/github","className":"text-base-content hover:bg-base-200 inline-flex min-h-11 items-center rounded-full px-3.5 font-mono text-[11px] tracking-[.1em] uppercase transition-colors","children":"github"}]]]}]]}],["$","div",null,{"children":[["$","$L18",null,{"href":"/blog/auto-configuration-system","className":"sh-plate bg-base-100 focus-within:ring-primary mb-8 grid grid-cols-1 gap-6 rounded-[24px] p-[10px] focus-within:ring-2 lg:grid-cols-[520px_1fr]","children":[["$","span",null,{"className":"sh-well bg-base-200 flex h-56 items-center justify-center overflow-hidden rounded-[17px] lg:h-[300px]","children":["$","img",null,{"src":"/blog-images/auto-config/featured.svg","alt":"Auto-Configuration System - Zero Config Magic for Your New Project","className":"h-full w-full object-cover"}]}],["$","span",null,{"className":"flex flex-col gap-3 p-2 lg:py-5 lg:pr-6 lg:pl-0","children":[["$","span",null,{"className":"text-base-content flex flex-wrap items-center gap-3 font-mono text-[11px] tracking-[.14em] uppercase","children":[["$","span",null,{"className":"sh-groove bg-base-100 rounded-lg px-2.5 py-1.5","children":"DevOps"}],["$","span",null,{"children":"27 Sept 2025"}],[["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[10," min"]}]]]}],["$","span",null,{"className":"text-base-content font-display text-2xl leading-[1.08] tracking-[-0.025em] sm:text-[34px]","children":"Auto-Configuration: Use Template and Start Building"}],["$","span",null,{"className":"text-base-content/80 leading-[1.6]","children":"ScriptHammer's auto-configuration eliminates setup friction. Use the template, run Docker, and watch your project automatically adapt with zero manual config."}],["$","span",null,{"className":"sh-btn sh-btn-primary mt-2 self-start","children":["Read it",["$","span",null,{"aria-hidden":"true","children":"→"}]]}]]}]]}],["$","div",null,{"className":"mb-8 grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3","children":[["$","article","post_40b0c24c",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/playable-city-chattanooga/featured-og.svg","alt":"A playable civic digital twin of Chattanooga: neighborhoods over the Tennessee River, with a live city-health score","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],"$L2b"]}],"$L2c","$L2d","$L2e","$L2f","$L30","$L31","$L32","$L33","$L34","$L35"]}]]}]]}] 2b:["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/playable-city-chattanooga","className":"hover:text-primary inline-flex min-h-11 items-center","children":"What If Your City Were Playable? A Chattanooga Brainstorm"}]}],["$","p",null,{"className":"text-base-content/85","children":"A 3D digital twin of Chattanooga left me asking a bigger question. What if the map weren't just to look at, but to play? Explore the idea and weigh in."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","chattanooga",{"href":"/blog/tags/chattanooga","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with chattanooga","data-testid":"tag-badge","children":[["$","span",null,{"children":"chattanooga"}],false]}],["$","$L18","digital-twin",{"href":"/blog/tags/digital-twin","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with digital-twin","data-testid":"tag-badge","children":[["$","span",null,{"children":"digital-twin"}],false]}],["$","$L18","civic-tech",{"href":"/blog/tags/civic-tech","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with civic-tech","data-testid":"tag-badge","children":[["$","span",null,{"children":"civic-tech"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",2," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Jul 17, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[11," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}] 2c:["$","article","post_c2f5475e",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/custom-domain-go-live/featured-og.svg","alt":"Going live - taking a static Next.js app from a project URL to a custom domain","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/custom-domain-go-live","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Going Live: A Static Next.js App on a Custom Domain"}]}],["$","p",null,{"className":"text-base-content/85","children":"A step-by-step runbook for taking a static Next.js PWA from a project URL to a custom domain — choosing among domains you own, GitHub Pages vs Vercel, DNS, the basePath drop, and the Supabase auth-redirect gotcha that will bite you."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","deployment",{"href":"/blog/tags/deployment","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with deployment","data-testid":"tag-badge","children":[["$","span",null,{"children":"deployment"}],false]}],["$","$L18","github-pages",{"href":"/blog/tags/github-pages","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with github-pages","data-testid":"tag-badge","children":[["$","span",null,{"children":"github-pages"}],false]}],["$","$L18","vercel",{"href":"/blog/tags/vercel","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with vercel","data-testid":"tag-badge","children":[["$","span",null,{"children":"vercel"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",3," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Jul 8, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[9," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 2d:["$","article","post_2c1f2ff6",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/client-email-not-a-spec/featured-og.svg","alt":"Your Client's Email Is Not a Spec - an MCP server turning loose client mail into tightly-scoped GitHub tickets","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/client-email-not-a-spec","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Your Client's Email Is Not a Spec: Turning Mail Into Tickets"}]}],["$","p",null,{"className":"text-base-content/85","children":"A client's email hides the real expectation inside chatty prose and un-transcribed screenshots. Here's how an MCP server that reads your mail and drafts GitHub tickets tightens the contract between what they asked for and what you ship."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","mcp",{"href":"/blog/tags/mcp","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with mcp","data-testid":"tag-badge","children":[["$","span",null,{"children":"mcp"}],false]}],["$","$L18","github",{"href":"/blog/tags/github","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with github","data-testid":"tag-badge","children":[["$","span",null,{"children":"github"}],false]}],["$","$L18","freelancing",{"href":"/blog/tags/freelancing","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with freelancing","data-testid":"tag-badge","children":[["$","span",null,{"children":"freelancing"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",3," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Jul 7, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[15," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 2e:["$","article","post_c57c28a9",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/reject-without-taking-over/featured-og.svg","alt":"Send It Back Without Taking It Over - rejecting an AI agent's pull request while preserving its authorship","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/reject-without-taking-over","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Send It Back Without Taking It Over: Rejecting an Agent's PR"}]}],["$","p",null,{"className":"text-base-content/85","children":"When a collaborator's AI agent hands you a flawed pull request, the trustworthy way to block it is a request-changes review with a paste-ready fix — never a fixup commit on their branch. That one convenience commit erases the authorship your whole identity discipline exists to protect."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","code-review",{"href":"/blog/tags/code-review","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with code-review","data-testid":"tag-badge","children":[["$","span",null,{"children":"code-review"}],false]}],["$","$L18","github",{"href":"/blog/tags/github","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with github","data-testid":"tag-badge","children":[["$","span",null,{"children":"github"}],false]}],["$","$L18","ai-agents",{"href":"/blog/tags/ai-agents","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with ai-agents","data-testid":"tag-badge","children":[["$","span",null,{"children":"ai-agents"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",3," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Jul 7, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[13," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 2f:["$","article","post_fe31591b",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/cursor-github-identity/featured-og.svg","alt":"A Token Is an Identity - letting a collaborator's AI agent commit as themselves with fine-grained GitHub tokens","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/cursor-github-identity","className":"hover:text-primary inline-flex min-h-11 items-center","children":"A Token Is an Identity: Let Cursor Commit as Themselves"}]}],["$","p",null,{"className":"text-base-content/85","children":"When a teammate's AI agent touches your repo, whose name ends up on the commit? A guide to fine-grained tokens, least privilege, and letting Cursor act as the collaborator — not you."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","cursor",{"href":"/blog/tags/cursor","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with cursor","data-testid":"tag-badge","children":[["$","span",null,{"children":"cursor"}],false]}],["$","$L18","github",{"href":"/blog/tags/github","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with github","data-testid":"tag-badge","children":[["$","span",null,{"children":"github"}],false]}],["$","$L18","security",{"href":"/blog/tags/security","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with security","data-testid":"tag-badge","children":[["$","span",null,{"children":"security"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",3," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Jul 5, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[16," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 30:["$","article","post_24522232",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/admin-dashboard-overview/overview.png","alt":"Admin Dashboard Overview showing stat cards for payments, auth, users, and messaging across a tabbed interface","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/admin-dashboard-overview","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Building a Centralized Admin Dashboard for ScriptHammer"}]}],["$","p",null,{"className":"text-base-content/85","children":"A centralized admin dashboard that surfaces payment, auth, user, and messaging metrics — built with defense-in-depth security on a static-export Next.js site."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","admin",{"href":"/blog/tags/admin","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with admin","data-testid":"tag-badge","children":[["$","span",null,{"children":"admin"}],false]}],["$","$L18","dashboard",{"href":"/blog/tags/dashboard","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with dashboard","data-testid":"tag-badge","children":[["$","span",null,{"children":"dashboard"}],false]}],["$","$L18","supabase",{"href":"/blog/tags/supabase","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with supabase","data-testid":"tag-badge","children":[["$","span",null,{"children":"supabase"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",4," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Feb 23, 2026"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[11," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 31:["$","article","post_e21a3b06",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/offline-payment-system-stripe-paypal/featured-og.svg","alt":"Offline-First Payment System with Stripe, PayPal, Supabase Edge Functions, and GDPR Consent","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/offline-payment-system-stripe-paypal","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Offline Payment: Stripe, PayPal & GDPR"}]}],["$","p",null,{"className":"text-base-content/85","children":"Learn how to build offline-first payments on GitHub Pages with Supabase Edge Functions, Stripe, PayPal, GDPR consent, and IndexedDB resilience."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","payments",{"href":"/blog/tags/payments","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with payments","data-testid":"tag-badge","children":[["$","span",null,{"children":"payments"}],false]}],["$","$L18","stripe",{"href":"/blog/tags/stripe","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with stripe","data-testid":"tag-badge","children":[["$","span",null,{"children":"stripe"}],false]}],["$","$L18","paypal",{"href":"/blog/tags/paypal","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with paypal","data-testid":"tag-badge","children":[["$","span",null,{"children":"paypal"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",4," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Oct 9, 2025"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[16," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 32:["$","article","post_1b810476",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/authentication-supabase-oauth/featured-og.svg","alt":"Production-Ready Authentication with Supabase - OAuth, Security Hardening, and PostgreSQL","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/authentication-supabase-oauth","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Supabase Authentication: OAuth & Security"}]}],["$","p",null,{"className":"text-base-content/85","children":"Secure authentication with Supabase, OAuth providers, server-side rate limiting, and Row-Level Security in Next.js 15 & PostgreSQL."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","authentication",{"href":"/blog/tags/authentication","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with authentication","data-testid":"tag-badge","children":[["$","span",null,{"children":"authentication"}],false]}],["$","$L18","supabase",{"href":"/blog/tags/supabase","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with supabase","data-testid":"tag-badge","children":[["$","span",null,{"children":"supabase"}],false]}],["$","$L18","oauth",{"href":"/blog/tags/oauth","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with oauth","data-testid":"tag-badge","children":[["$","span",null,{"children":"oauth"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",3," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Oct 8, 2025"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[18," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 33:["$","article","post_3e9ba449",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/countdown-timer-tutorial/countdown-banner-preview.svg","alt":"Countdown timer component showing days, hours, minutes, seconds with New Year sale promotion","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/countdown-timer-tutorial","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Build a Countdown Timer Tutorial"}]}],["$","p",null,{"className":"text-base-content/85","children":"Learn the PRP/SpecKit workflow by building a countdown timer. From requirements to production code with ScriptHammer template."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","tutorial",{"href":"/blog/tags/tutorial","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with tutorial","data-testid":"tag-badge","children":[["$","span",null,{"children":"tutorial"}],false]}],["$","$L18","prp-workflow",{"href":"/blog/tags/prp-workflow","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with prp-workflow","data-testid":"tag-badge","children":[["$","span",null,{"children":"prp-workflow"}],false]}],["$","$L18","react",{"href":"/blog/tags/react","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with react","data-testid":"tag-badge","children":[["$","span",null,{"children":"react"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",4," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Sep 30, 2025"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[9," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 34:["$","article","post_03f34e7b",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[null,["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/this-is-an-extremely-long-url-slug-that-should-be-much-shorter-for-better-seo","className":"hover:text-primary inline-flex min-h-11 items-center","children":"low seo"}]}],["$","p",null,{"className":"text-base-content/85","children":"This is a blog post"}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","seo",{"href":"/blog/tags/seo","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with seo","data-testid":"tag-badge","children":[["$","span",null,{"children":"seo"}],false]}],["$","$L18","optimization",{"href":"/blog/tags/optimization","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with optimization","data-testid":"tag-badge","children":[["$","span",null,{"children":"optimization"}],false]}],["$","$L18","content-writing",{"href":"/blog/tags/content-writing","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with content-writing","data-testid":"tag-badge","children":[["$","span",null,{"children":"content-writing"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",2," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Sep 28, 2025"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[2," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}] 35:["$","article","post_04db796e",{"className":"blog-post-card card sh-plate bg-base-100 rounded-box transition-transform hover:-translate-y-0.5 h-full","onClick":"$undefined","children":[["$","figure",null,{"className":"sh-well rounded-box relative m-3 mb-0 h-48 overflow-hidden","children":["$","$L2a",null,{"src":"/blog-images/scripthammer-intro/featured-og.svg","alt":"Scripthammer - The Opinionated Next.js PWA Template with 32 themes and Docker development","fill":true,"className":"object-cover","sizes":"(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"}]}],["$","div",null,{"className":"card-body","children":[false,["$","h2",null,{"className":"card-title text-xl leading-snug","children":["$","$L18",null,{"href":"/blog/scripthammer-intro","className":"hover:text-primary inline-flex min-h-11 items-center","children":"Scripthammer - Opinionated Next.js PWA Template"}]}],["$","p",null,{"className":"text-base-content/85","children":"Opinionated Next.js 15.5 template with PWA support, 32 DaisyUI themes, Docker-first development environment, and comprehensive testing suite."}],["$","div",null,{"className":"card-actions mt-4 items-center justify-between","children":[["$","div",null,{"className":"flex flex-wrap gap-2","children":[[["$","$L18","scripthammer",{"href":"/blog/tags/scripthammer","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with scripthammer","data-testid":"tag-badge","children":[["$","span",null,{"children":"scripthammer"}],false]}],["$","$L18","next.js",{"href":"/blog/tags/next.js","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with next.js","data-testid":"tag-badge","children":[["$","span",null,{"children":"next.js"}],false]}],["$","$L18","pwa",{"href":"/blog/tags/pwa","className":"badge\n badge-sm text-xs\n badge-outline\n \n cursor-pointer hover:scale-105 transition-transform min-h-11 min-w-11","aria-label":"View posts tagged with pwa","data-testid":"tag-badge","children":[["$","span",null,{"children":"pwa"}],false]}]],["$","span",null,{"className":"badge badge-ghost badge-sm","children":["+",2," more"]}]]}],["$","div",null,{"className":"text-base-content/80 flex gap-3 font-mono text-xs whitespace-nowrap","children":[["$","span",null,{"children":"Sep 26, 2025"}],["$","span",null,{"aria-hidden":"true","children":"·"}],["$","span",null,{"children":[5," min",["$","span",null,{"className":"sr-only","children":" read"}]]}]]}]]}],false]}]]}]