/* === CSS VARIABLES === */ :root { --bg-base: #3d566e; --bg-surface: #3d5a7a; --bg-elevated: #486a8f; --bg-border: rgba(255,255,255,0.08); --bg-subtle: #2d4059; --border-strong: rgba(255,255,255,0.14); --pillar-1: #8b96e8; --pillar-1-soft: rgba(139,150,232,0.18); --pillar-2: #4dbac4; --pillar-2-soft: rgba(77,186,196,0.18); --pillar-3: #e0993a; --pillar-3-soft: rgba(224,153,58,0.18); --text-primary: #e2e8f0; --text-body: #e2e8f0; --text-muted: #a8b8cc; --font-display: 'Space Grotesk', system-ui, -apple-system, 'Segoe UI', sans-serif; --font-ui: 'Inter', system-ui, -apple-system, 'Segoe UI', sans-serif; --font-body: 'Crimson Pro', Georgia, 'Times New Roman', serif; --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace; --text-xs: 0.75rem; --text-sm: 0.875rem; --text-base: 1.05rem; --text-body: 1.15rem; --text-md: 1.25rem; --text-lg: 1.5rem; --text-xl: clamp(1.8rem, 3vw, 2.75rem); --text-2xl: clamp(2rem, 4vw, 3.5rem); --text-3xl: clamp(2.5rem, 5vw, 4rem); --text-hero: clamp(2.4rem, 5vw, 4.4rem); --glow-indigo: rgba(139,150,232,0.22); --glow-cyan: rgba(77,186,196,0.18); --hover-subtle: rgba(255,255,255,0.04); --nav-separator: rgba(120,144,168,0.30); } [data-theme="light"] { --bg-base: #DDEAF7; --bg-surface: #F4F7FB; --bg-elevated: #FFFFFF; --bg-border: rgba(30,48,72,0.10); --bg-subtle: #a8c4e0; --border-strong: rgba(30,48,72,0.18); --text-primary: #1e3048; --text-body: #2d4a66; --text-muted: #4a6080; --hover-subtle: rgba(30,48,72,0.04); --nav-separator: rgba(30,48,72,0.18); --glow-indigo: rgba(101,112,200,0.10); --glow-cyan: rgba(58,157,166,0.10); --pillar-1: #6570c8; --pillar-1-soft: rgba(101,112,200,0.18); --pillar-2: #3a9da6; --pillar-2-soft: rgba(58,157,166,0.18); --pillar-3: #c07a28; --pillar-3-soft: rgba(192,122,40,0.18); --text-muted: #4a6080; } body, .nav-mobile-overlay, h1, h2, h3, p, li, span, a { transition: background-color 0.25s ease, color 0.20s ease, border-color 0.20s ease; } *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { scroll-behavior: smooth; } body { background: var(--bg-base); color: var(--text-body); font-family: var(--font-body); line-height: 1.9; overflow-x: hidden; } /* === NAV === */ .site-nav { position: fixed; top: 0; left: 0; right: 0; z-index: 1000; display: flex; align-items: center; justify-content: space-between; padding: 0 64px; height: 54px; background: rgba(61,86,110,0.85); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-bottom: 1px solid rgba(255,255,255,0.06); transition: background 0.4s ease, backdrop-filter 0.4s ease, border-color 0.4s ease; } [data-theme="light"] .site-nav { background: rgba(255,255,255,0.55); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-bottom: 1px solid rgba(255,255,255,0.65); box-shadow: 0 2px 16px rgba(30,48,72,0.08); } [data-theme="light"] .site-nav.scrolled { background: rgba(255,255,255,0.70); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); box-shadow: 0 4px 24px rgba(30,48,72,0.12); } [data-theme="dark"] .site-nav { background: rgba(35,52,72,0.75); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border-bottom: 1px solid rgba(255,255,255,0.08); box-shadow: 0 2px 16px rgba(0,0,0,0.30); } [data-theme="dark"] .site-nav.scrolled { background: rgba(30,45,65,0.88); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); box-shadow: 0 4px 24px rgba(0,0,0,0.40); } .site-nav.scrolled { background: rgba(61,86,110,0.75); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border-bottom-color: rgba(255,255,255,0.10); } .nav-brand { text-decoration: none; display: flex; flex-direction: column; gap: 2px; } .nav-brand-name { font-family: var(--font-display); font-size: var(--text-md); font-weight: 700; color: var(--text-primary); letter-spacing: 0.18em; text-transform: uppercase; } .nav-brand-sub { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); letter-spacing: 0.1em; text-transform: uppercase; } .nav-links { display: flex; align-items: center; gap: 40px; list-style: none; margin: 0; padding: 0; } .nav-link { font-family: var(--font-display); font-size: var(--text-sm); font-weight: 600; color: var(--text-body); text-decoration: none; letter-spacing: 0.04em; transition: color 0.2s; position: relative; } .nav-link:hover, .nav-link.active { color: var(--text-primary); } .nav-link.active::after { content: ''; position: absolute; bottom: -4px; left: 0; right: 0; height: 3px; background: var(--pillar-2); border-radius: 1px; } .nav-hamburger { display: none; flex-direction: column; gap: 5px; background: none; border: none; cursor: pointer; padding: 8px; } .nav-hamburger span { display: block; width: 22px; height: 2px; background: var(--text-primary); transition: transform 0.3s ease, opacity 0.3s ease; } .nav-hamburger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); } .nav-hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); } .nav-hamburger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } .nav-mobile-overlay { position: fixed; inset: 0; z-index: 999; background: var(--bg-base); display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transform: translateY(-12px); transition: opacity 0.3s ease, transform 0.3s ease; } .nav-mobile-overlay.open { opacity: 1; pointer-events: all; transform: translateY(0); } .nav-mobile-links { list-style: none; text-align: center; padding: 0; } .nav-mobile-links li { margin: 24px 0; } .nav-mobile-links a { font-family: var(--font-display); font-size: 2rem; font-weight: 700; color: var(--text-muted); text-decoration: none; transition: color 0.2s; } .nav-mobile-links a:hover { color: var(--text-primary); } .mobile-cta { color: var(--pillar-2) !important; } @media (max-width: 768px) { .site-nav { padding: 0 24px; } .nav-links, .nav-cta { display: none; } .nav-hamburger { display: flex; } } /* Nav pipe separators */ .nav-links { gap: 40px !important; } .nav-brand-sub { display: none !important; } /* === REVEAL === */ .reveal { opacity: 0; transform: translateY(24px); transition: opacity 0.5s ease, transform 0.5s ease; } .reveal.visible { opacity: 1; transform: none; } /* === PAGE HERO === */ .page-hero { position: relative; padding: 96px 80px 72px; overflow: hidden; border-bottom: 1px solid rgba(255,255,255,0.08); background: var(--bg-subtle) !important; } .hero-lines { position: absolute; inset: 0; pointer-events: none; overflow: hidden; } .hero-line { position: absolute; height: 1px; background: linear-gradient(90deg, transparent, rgba(58,157,166,0.18), rgba(101,112,200,0.18), transparent); animation: lineSweep linear infinite; } @keyframes lineSweep { 0% { transform: translateX(-100%); } 100% { transform: translateX(100vw); } } .hero-glow { position: absolute; top: -60px; right: 10%; width: 500px; height: 500px; background: radial-gradient(ellipse, rgba(6,182,212,0.07) 0%, transparent 70%); pointer-events: none; } .hero-eyebrow { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--pillar-2); letter-spacing: 0.22em; text-transform: uppercase; margin-bottom: 20px; display: block; opacity: 0; animation: fadeUp 0.7s cubic-bezier(0.16,1,0.3,1) 0.1s both; } .page-hero h1 { font-family: var(--font-display); font-size: clamp(2.2rem, 4.5vw, 4rem); font-weight: 600; color: var(--text-primary); line-height: 1.1; letter-spacing: 0.01em; max-width: 800px; margin-bottom: 20px; opacity: 0; animation: fadeUp 0.7s cubic-bezier(0.16,1,0.3,1) 0.2s both; } .page-hero-sub { font-family: var(--font-body); font-size: var(--text-md); color: var(--text-body); max-width: 640px; line-height: 1.9; margin-bottom: 48px; opacity: 0; animation: fadeUp 0.7s cubic-bezier(0.16,1,0.3,1) 0.32s both; } .hero-metrics { display: flex; gap: 48px; flex-wrap: wrap; opacity: 0; animation: fadeUp 0.7s cubic-bezier(0.16,1,0.3,1) 0.44s both; } .hero-metric { display: flex; flex-direction: column; gap: 4px; } .hero-metric-val { font-family: var(--font-mono); font-size: var(--text-lg); font-weight: 500; color: var(--pillar-2); letter-spacing: 0; } .hero-metric-label { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); letter-spacing: 0.1em; text-transform: uppercase; } .hero-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 32px; opacity: 0; animation: fadeUp 0.7s cubic-bezier(0.16,1,0.3,1) 0.38s both; } .hero-tag { font-family: var(--font-mono); font-size: var(--text-sm); padding: 4px 12px; border: 1px solid var(--bg-border); color: var(--text-muted); letter-spacing: 0.04em; } .hero-status { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: var(--text-sm); color: #4ade80; background: rgba(74,222,128,0.10); border: 1px solid rgba(74,222,128,0.25); padding: 6px 14px; margin-bottom: 28px; letter-spacing: 0.08em; text-transform: uppercase; } .hero-status::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: #4ade80; flex-shrink: 0; animation: pulseDot 2s ease-in-out infinite; } @keyframes pulseDot { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } } @keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } .hero-parallels { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); letter-spacing: 0.04em; margin-top: 24px; line-height: 1.8; } @media (max-width: 768px) { .page-hero { padding: 64px 24px 48px; } .hero-metrics { gap: 28px; } } /* === CASE STUDY CONTENT === */ .cs-section { padding: 80px 80px; border-bottom: 1px solid var(--bg-border); } .cs-section:last-of-type { border-bottom: none; } .cs-section-dark { background: var(--bg-surface); } .cs-inner { max-width: 1000px; margin: 0 auto; } .cs-label { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--pillar-2); letter-spacing: 0.22em; text-transform: uppercase; margin-bottom: 16px; display: block; } .cs-heading { font-family: var(--font-display); font-size: var(--text-xl); font-weight: 700; color: var(--text-primary); margin-bottom: 32px; letter-spacing: 0.04em; text-transform: uppercase; } .cs-body { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-body); line-height: 1.9; max-width: 800px; } .cs-body p { margin-bottom: 20px; } .cs-body strong { color: var(--text-primary); font-weight: 600; } .cs-body code { font-family: var(--font-mono); font-size: 0.85rem; background: rgba(255,255,255,0.06); padding: 2px 8px; border: 1px solid var(--bg-border); color: var(--pillar-2); } /* === TECH STACK === */ .stack-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 32px; margin-top: 32px; } .stack-category { background: var(--bg-surface); border: 1px solid var(--bg-border); padding: 28px 32px; } .stack-cat-label { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--pillar-2); letter-spacing: 0.2em; text-transform: uppercase; margin-bottom: 16px; } .stack-tags { display: flex; flex-wrap: wrap; gap: 8px; } .stack-tag { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); background: var(--bg-elevated); border: 1px solid var(--bg-border); padding: 5px 12px; letter-spacing: 0.04em; } @media (max-width: 768px) { .stack-grid { grid-template-columns: 1fr; } .cs-section { padding: 48px 24px; } } /* === ADR CARDS === */ .adr-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; margin-top: 32px; } .adr-card { background: var(--bg-surface); border: 1px solid var(--bg-border); border-left: 4px solid var(--pillar-2); padding: 28px 28px; transition: transform 0.3s, box-shadow 0.3s; } .adr-card:hover { transform: translateY(-4px); box-shadow: 2px 16px 40px rgba(58,157,166,0.15); } .adr-card:nth-child(2) { border-left-color: var(--pillar-1); } .adr-card:nth-child(3) { border-left-color: var(--pillar-3); } .adr-card:nth-child(4) { border-left-color: var(--pillar-2); } .adr-card:nth-child(2):hover { box-shadow: 2px 16px 40px rgba(101,112,200,0.15); } .adr-card:nth-child(3):hover { box-shadow: 2px 16px 40px rgba(192,122,40,0.15); } .adr-card:nth-child(4):hover { box-shadow: 2px 16px 40px rgba(58,157,166,0.15); } .adr-decision { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: 0.15em; text-transform: uppercase; margin-bottom: 8px; } .adr-title { font-family: var(--font-display); font-size: var(--text-md); font-weight: 700; color: var(--text-primary); margin-bottom: 12px; line-height: 1.3; } .adr-desc { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-body); line-height: 1.7; } .adr-desc strong { color: var(--text-primary); } @media (max-width: 768px) { .adr-grid { grid-template-columns: 1fr; } } /* === TIMELINE === */ .timeline { margin-top: 40px; position: relative; } .timeline::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 1px; background: linear-gradient(to bottom, var(--pillar-2), var(--pillar-1), var(--pillar-3), transparent); } .timeline-item { padding-left: 32px; margin-bottom: 40px; position: relative; } .timeline-item::before { content: ''; position: absolute; left: -5px; top: 6px; width: 11px; height: 11px; border-radius: 50%; background: var(--pillar-2); border: 2px solid var(--bg-base); } .timeline-item:nth-child(2)::before { background: var(--pillar-1); } .timeline-item:nth-child(3)::before { background: var(--pillar-3); } .timeline-item:nth-child(4)::before { background: var(--pillar-2); } .tl-label { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 4px; } .tl-title { font-family: var(--font-display); font-size: var(--text-lg); font-weight: 700; color: var(--text-primary); margin-bottom: 4px; } .tl-meta { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--pillar-2); letter-spacing: 0.1em; margin-bottom: 12px; } .tl-desc { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-body); line-height: 1.7; } /* === OUTCOME === */ .outcome-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 32px; } .outcome-card { background: var(--bg-surface); border: 1px solid var(--bg-border); padding: 28px 28px; text-align: center; } .outcome-val { font-family: var(--font-display); font-size: clamp(2rem, 3vw, 2.8rem); font-weight: 800; color: var(--pillar-2); line-height: 1; margin-bottom: 8px; } .outcome-card:nth-child(2) .outcome-val { color: var(--pillar-1); } .outcome-card:nth-child(3) .outcome-val { color: var(--pillar-3); } .outcome-label { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: 0.15em; text-transform: uppercase; } .outcome-list { margin-top: 32px; list-style: none; } .outcome-list li { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-body); padding: 12px 0; border-bottom: 1px solid var(--bg-border); display: flex; align-items: center; gap: 12px; } .outcome-list li::before { content: '◆'; color: var(--pillar-2); font-size: 0.6rem; flex-shrink: 0; } .outcome-list li:last-child { border-bottom: none; } @media (max-width: 768px) { .outcome-grid { grid-template-columns: 1fr; } } /* === RELATED PROJECTS === */ .related-section { padding: 80px 80px 120px; background: var(--bg-base); } .related-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 24px; margin-top: 32px; max-width: 1000px; margin-left: auto; margin-right: auto; } .related-card { background: var(--bg-surface); border: 1px solid var(--bg-border); padding: 32px; text-decoration: none; display: flex; flex-direction: column; transition: border-color 0.3s, transform 0.3s, box-shadow 0.3s; } .related-card:hover { border-color: var(--pillar-2); transform: translateY(-4px); box-shadow: 2px 16px 40px rgba(58,157,166,0.12); } .related-direction { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-muted); letter-spacing: 0.15em; text-transform: uppercase; margin-bottom: 8px; } .related-title { font-family: var(--font-display); font-size: var(--text-md); font-weight: 700; color: var(--text-primary); margin-bottom: 8px; } .related-desc { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-body); line-height: 1.6; } .related-arrow { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--pillar-2); margin-top: auto; padding-top: 16px; letter-spacing: 0.08em; } @media (max-width: 768px) { .related-grid { grid-template-columns: 1fr; } .related-section { padding: 48px 24px 80px; } } /* === FOOTER === */ .site-footer { background: var(--bg-surface); border-top: 1px solid var(--bg-border); padding: 64px 80px 32px; } .footer-inner { display: grid; grid-template-columns: 1.5fr 2fr 0.5fr; gap: 64px; align-items: start; padding-bottom: 48px; border-bottom: 1px solid var(--bg-border); margin-bottom: 32px; } .footer-brand { display: flex; flex-direction: column; gap: 6px; } .footer-name { font-family: var(--font-display); font-size: var(--text-lg); font-weight: 700; color: var(--text-primary); } .footer-title { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--pillar-2); text-transform: uppercase; letter-spacing: 0.12em; } .footer-location { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-body); margin-top: 8px; } .footer-nav { display: flex; gap: 48px; } .footer-nav-col { display: flex; flex-direction: column; gap: 12px; } .footer-nav-heading { font-family: var(--font-ui); font-size: var(--text-sm); font-weight: 700; color: var(--text-body); text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 4px; } .footer-nav-col a { font-family: var(--font-body); font-size: var(--text-base); color: var(--text-muted); text-decoration: none; transition: color 0.2s; } .footer-nav-col a:hover { color: var(--text-primary); } .footer-social { display: flex; flex-direction: column; gap: 12px; align-items: flex-end; } .footer-social-link { width: 36px; height: 36px; border: 1px solid var(--bg-border); display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); text-decoration: none; text-transform: lowercase; transition: border-color 0.2s, color 0.2s; } .footer-social-link:hover { border-color: var(--pillar-2); color: var(--pillar-2); } .footer-bottom { display: flex; justify-content: space-between; align-items: center; font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-body); } .footer-tagline { font-style: italic; opacity: 0.85; } @media (max-width: 768px) { .site-footer { padding: 48px 24px 24px; } .footer-inner { grid-template-columns: 1fr; gap: 32px; } .footer-nav { flex-direction: column; gap: 32px; } .footer-social { flex-direction: row; align-items: flex-start; } .footer-bottom { flex-direction: column; gap: 8px; text-align: center; } } p, li { font-size: var(--text-body); letter-spacing: 0.01em; } h2, h3, h4, .cs-heading, .card-title, .page-title { text-transform: uppercase; letter-spacing: 0.05em !important; } h1 { text-transform: uppercase; } .btn-primary, .btn-outline, .nav-cta, a.btn-primary, a.btn-outline, input, textarea, select { border-radius: 0 !important; } .nav-cta { background: transparent !important; border: 1px solid var(--pillar-2) !important; color: var(--pillar-2) !important; } .nav-cta:hover { background: var(--pillar-2) !important; color: var(--bg-base) !important; transform: none !important; } /* === SVG DIAGRAM === */ .diagram-wrap { position: relative; margin: 48px auto; max-width: 900px; background: var(--bg-surface); border: 1px solid var(--bg-border); padding: 24px; } .diagram-svg { width: 100%; height: auto; display: block; } .diagram-svg text { font-family: 'JetBrains Mono', monospace; } /* Pulsing hub */ @keyframes hubPulse { 0%, 100% { r: 28; opacity: 0.4; } 50% { r: 36; opacity: 0.15; } } @keyframes hubGlow { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.6; } } /* Flowing data packets on NATS lines */ @keyframes packetFlow { 0% { offset-distance: 0%; opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { offset-distance: 100%; opacity: 0; } } @keyframes packetFlowRev { 0% { offset-distance: 100%; opacity: 0; } 10% { opacity: 1; } 90% { opacity: 1; } 100% { offset-distance: 0%; opacity: 0; } } /* Syncthing sync arrows */ @keyframes syncFlow { 0% { stroke-dashoffset: 100; opacity: 0; } 20% { opacity: 1; } 80% { opacity: 1; } 100% { stroke-dashoffset: 0; opacity: 0; } } /* Node bounce */ @keyframes nodeFloat { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-3px); } } /* Data particle stream */ @keyframes dataPulse { 0%, 100% { opacity: 0.3; } 50% { opacity: 0.8; } } .diagram-caption { font-family: var(--font-mono); font-size: var(--text-sm); color: var(--text-muted); text-align: center; padding: 16px 0 0; letter-spacing: 0.04em; } /* === DARK THEME FIXES === */ [data-theme="dark"] .pillar-card, [data-theme="dark"] .project-card, [data-theme="dark"] .adr-card, [data-theme="dark"] .outcome-card, [data-theme="dark"] .stack-category, [data-theme="dark"] .related-card, [data-theme="dark"] .diagram-wrap { box-shadow: 2px 6px 28px rgba(0,0,0,0.40), 0 1px 0 rgba(255,255,255,0.06) inset; } .cs-section-dark { box-shadow: 0 8px 32px rgba(0,0,0,0.25); } /* === DIAGRAM ANIMATIONS (ENHANCED) === */ @keyframes seqStep { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } @keyframes seqArrow { to { stroke-dashoffset: -30; } } @keyframes glowPulse { 0%, 100% { filter: drop-shadow(0 0 3px rgba(77,186,196,0.4)); } 50% { filter: drop-shadow(0 0 12px rgba(77,186,196,0.9)); } } @keyframes floatUp { 0%, 100% { transform: translateY(0px); } 50% { transform: translateY(-5px); } } @keyframes nodeGlow { 0%, 100% { stroke-opacity: 0.3; } 50% { stroke-opacity: 1; } } @keyframes fadeSlideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } } @keyframes progressFill { from { width: 0%; } to { width: 100%; } } @keyframes spinSlow { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes dashFlowRev { to { stroke-dashoffset: 60; } } .diag-label { font-family: 'JetBrains Mono', monospace; font-size: 8px; fill: var(--text-muted); text-anchor: middle; letter-spacing: 0.08em; } .diag-title { font-family: 'Space Grotesk', sans-serif; font-size: 10px; fill: var(--text-primary); text-anchor: middle; font-weight: 700; } .diag-step { font-family: 'JetBrains Mono', monospace; font-size: 7px; fill: var(--pillar-2); text-anchor: middle; } .seq-arrow { stroke-dasharray: 6 4; animation: seqArrow 1s linear infinite; } .seq-dashed { stroke-dasharray: 4 5; animation: dashFlowRev 1.5s linear infinite; } .deploy-node { animation: floatUp 3s ease-in-out infinite; } .glow-node { animation: glowPulse 2.5s ease-in-out infinite; } .step-circle { animation: seqStep 2s ease-in-out infinite; } .node-pulse { animation: nodeGlow 2s ease-in-out infinite; }
Builder Case Study
Active

PAN — Personal Area Network

Bootstraps a heterogeneous home mesh (Fire Stick, Android, Mac) from one command — NATS messaging, WASM plugins, Syncthing sync, no cloud.

Go NATS WASM Syncthing SSH
Industry parallels: Zero-trust (Google BeyondCorp) · Device mesh (Apple ecosystem) · WASM runtime (AWS Lambda)
7 Sessions
1.3M Tokens
13 Key Decisions
4 Device Types
Problem Statement

The Home Cluster Fragmentation Problem

Managing a heterogeneous home cluster — Fire Stick (ARM32 Linux), Android phone, MacBook — means SSHing into each device, manually installing services, and checking health separately. When a device crashes, you do not know until something breaks. Home networks today include streaming dongles, phones, NAS appliances, and development machines — each a different architecture, OS, and software stack.

Keeping a Syncthing data sync layer, a SilverBullet notes server, a go-git HTTP host, and a NATS messaging bus running across all of them is a parade of manual steps. Each device needs its own binary variant, its own package manager, its own dependency resolution. The Fire Stick runs ARM32 Linux with no package manager to speak of; the Android phone runs a Linux userland through Termux; the MacBook is x86_64 (or Apple Silicon) with Homebrew. There is no common substrate.

Existing solutions — Ansible, Chef, Puppet — assume homogeneous targets, SSH accessibility, and server-grade uptime. They do not handle set-top boxes that sleep, phones that roam networks, or ARM32 devices without a compiler toolchain. Docker assumes a common kernel; not every device runs one.

Pan replaces the checklist with: pan deploy firestick. One command discovers the target, pushes the right binary for its architecture, bootstraps the NATS leaf node, installs Syncthing, and registers the device in the mesh. From there, all control flows through NATS topics; no more SSH session roulette.

Approach & Architecture

Multi-Binary Mesh with NATS Leaf Topology

Pan is a multi-binary Go system: pan CLI, pan-agent (device daemon with WASM plugin host), pan-server (web dashboard), pan-gitd (pure-Go git HTTP server). Orchestration is built on NATS with leaf node topology — each device runs a NATS leaf node connecting outbound to the Mac hub (no inbound ports required). Control flows through NATS pub/sub; data sync through Syncthing P2P. Device behaviour is hot-extensible via WASM plugins pushed through Syncthing.

# Deploy to a new device
pan deploy firestick

# Subscribe to device health on NATS
nats sub "pan.health.>" --server nats://macbook.local:4222
[12:34:01] pan.health.firestick → {"cpu":12,"mem":64,"uptime":"3d"}
[12:34:02] pan.health.galaxy    → {"cpu":8,"mem":128,"uptime":"12h"}
MacBook NATS Hub
pan-server · pan-gitd
NATS Server · Syncthing
SSH bootstrap
Fire Stick
NATS Leaf · Syncthing
pan-agent · SilverBullet
ARM32 Linux
Galaxy Note 9
NATS Leaf · Syncthing
pan-agent · Termux:Boot
Android + Termux
NATS leaf Syncthing P2P NATS pub/sub Syncthing sync Data packet
Star topology: MacBook as NATS hub (center), Fire Stick and Galaxy Note 9 as leaf nodes connecting outbound. Syncthing P2P sync between all three. Dashed lines = NATS control plane; dotted arcs = Syncthing data sync; animated dots = in-flight messages.
Deployment Topology

Device Mesh Topology

PHYSICAL TOPOLOGY MAC HUB pan CLI pan-server (Web) NATS Hub Syncthing Relay FIRE STICK pan-agent NATS Leaf ARM32 Linux NATS Leaf ANDROID pan-agent (Termux) NATS Leaf Syncthing NATS Leaf PI / NAS pan-gitd SilverBullet Content Store NATS Leaf Syncthing P2P Syncthing P2P NATS Hub Syncthing P2P NATS Leaf Connection
Sequence Diagram

Device Bootstrap Sequence

BOOTSTRAP SEQUENCE: pan deploy firestick pan CLI SSH SCP pan-agent NATS Hub 1 ssh user@firestick 2 Detect ARM32 arch 3 scp pan-agent → device 4 ./pan-agent start 5 NATS Leaf Connect 6 Register 7 Syncthing N Sequence Step Message
Technology Stack

Languages, Transport & Runtime

Languages & Runtime
Go TinyGo Extism PDK
Transport & Messaging
NATS pub/sub JetStream Leaf Node Topology Outbound-only
Sync & Storage
Syncthing P2P Encrypted Relay-friendly go-git
Hosting & Bootstrap
SSH bootstrap Termux:Boot Android service mgmt
Plugin Runtime
WASM via Extism Sandboxed Hot-reloadable fsnotify <50ms
UI & CLI
React Bubble Tea Web Dashboard
Architecture Decisions

Key Design Records

ADR-001
NATS over MQTT
Leaf node topology is the killer feature. Devices connect outbound with zero broker configuration; no port forwarding, no dynamic DNS. JetStream provides persistence without a separate database — ideal for ephemeral devices that disconnect and reconnect. MQTT would require a centralized broker with inbound ports and does not natively support leaf node hierarchies.
ADR-002
WASM Plugins over Shell Scripts
Sandboxed execution by construction. WASM provides memory safety, CPU cycle limits, and a well-defined ABI across ARM32 (Fire Stick), ARM64 (Android), and x86_64 (Mac). Hot-reload via fsnotify in <50ms — drop a new .wasm into the plugins directory and the agent picks it up. Typed dispatch via Extism eliminates the string-parsing fragility of shell-based extension.
ADR-003
SSH Bootstrap over Agent-Initiated Tunnels
Simpler, works today, uses existing SSH keys. pan deploy SSHs into the target, scp-s the correct binary, and starts pan-agent as a system service. No chicken-and-egg problem of deploying an agent to bootstrap agent connectivity. Agent-initiated reverse tunnels (e.g. frp/ngrok) would require a public relay and add latency to every control message.
ADR-004
go-git over wasm-git
Battle-tested, full smart HTTP protocol. go-git is a pure-Go git implementation with years of production use, complete refspec negotiation, and packfile support. WASM-based git runtimes (wasm-git) are promising but lack smart HTTP protocol support, have larger binary sizes due to the WASM runtime overhead, and introduce another failure domain at home scale where performance is irrelevant.
Session Timeline

Development Journey

Session 1
Project Explanation & Architecture
571 messages · 1.22M tokens
Understanding and documenting the entire PAN codebase — Go binary structure, NATS topology, WASM plugin system, SSH bootstrap flow, and cross-compilation strategy for ARM32/ARM64/x86_64.
Session 2
WASM-git for pan-gitd
115 messages
Evaluating wasm-git vs go-git for the pure-Go git HTTP server. Compared smart HTTP protocol support, binary size, build complexity, and runtime characteristics on ARM32. Decision: go-git for production use.
Session 3
Ultra-Lightweight Photo Management
91 messages
Researching a WASM-based photo management service for the Fire Stick. Evaluated EXIF parsing in WASM, thumbnail generation via libvips bindings, and Syncthing-based photo sync to avoid cloud storage.
Session 4
Lightweight UI like Umbrel
73 messages
Evaluating WASM-based dashboard UIs for TV display. Compared React-in-WASM, Preact, and server-rendered HTML approaches for the pan-server dashboard, targeting the Fire Stick's limited GPU and 1080p output.
Outcome & Impact

Results & Industry Alignment

7
Sessions Documented
13
Key Decisions
1,312,755
Tokens
  • Active development with production deployment on home infrastructure — Fire Stick, Galaxy Note 9, and MacBook running in sync
  • Industry alignment: Google BeyondCorp zero-trust principles — every device authenticates individually and connects outbound; no assumed network trust
  • Industry alignment: Apple ecosystem orchestration — device mesh, continuity, and handoff concepts applied to a heterogeneous (non-Apple) fleet
  • Industry alignment: AWS Lambda/Envoy WASM runtime — WASM plugin sandbox for hot-reloadable device behaviour, analogous to Lambda@Edge and Envoy filters