1068 lines
57 KiB
HTML
1068 lines
57 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>NX9 — Next Generation 9 | Software people can own, understand, and control</title>
|
|
<meta name="description" content="NX9 is a philosophy-driven ecosystem of self-hosted, privacy-first Rust software by Sunil Thakare. No telemetry. No vendor lock-in. Software people can own, understand, and control.">
|
|
<meta name="color-scheme" content="light dark">
|
|
|
|
<!-- Favicons & App Icons -->
|
|
<link rel="apple-touch-icon" sizes="57x57" href="/apple-icon-57x57.png">
|
|
<link rel="apple-touch-icon" sizes="60x60" href="/apple-icon-60x60.png">
|
|
<link rel="apple-touch-icon" sizes="72x72" href="/apple-icon-72x72.png">
|
|
<link rel="apple-touch-icon" sizes="76x76" href="/apple-icon-76x76.png">
|
|
<link rel="apple-touch-icon" sizes="114x114" href="/apple-icon-114x114.png">
|
|
<link rel="apple-touch-icon" sizes="120x120" href="/apple-icon-120x120.png">
|
|
<link rel="apple-touch-icon" sizes="144x144" href="/apple-icon-144x144.png">
|
|
<link rel="apple-touch-icon" sizes="152x152" href="/apple-icon-152x152.png">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/apple-icon-180x180.png">
|
|
<link rel="icon" type="image/png" sizes="192x192" href="/android-icon-192x192.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
|
<link rel="manifest" href="/manifest.json">
|
|
<meta name="msapplication-TileColor" content="#020617">
|
|
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png">
|
|
<meta name="theme-color" content="#020617">
|
|
|
|
<style>
|
|
/* ============================================================
|
|
1. DESIGN TOKENS
|
|
============================================================ */
|
|
:root {
|
|
--bg: #ffffff;
|
|
--bg-raised: #ffffff;
|
|
--surface: #f5f6f9;
|
|
--surface-hover: #ecefF4;
|
|
--border: #e1e4ea;
|
|
--border-strong: #cbd0da;
|
|
--text: #12151a;
|
|
--text-secondary: #4b5563;
|
|
--text-muted: #6b7280;
|
|
--accent: #6d5df6;
|
|
--accent-strong: #5643e0;
|
|
--accent-soft: #eeecff;
|
|
--accent-contrast: #ffffff;
|
|
--dot: rgba(109, 93, 246, 0.14);
|
|
--line: rgba(109, 93, 246, 0.08);
|
|
--shadow: 0 1px 2px rgba(16, 20, 26, 0.04), 0 4px 16px rgba(16, 20, 26, 0.05);
|
|
--shadow-hover: 0 4px 10px rgba(109, 93, 246, 0.12), 0 16px 36px rgba(16, 20, 26, 0.1);
|
|
--radius: 10px;
|
|
--radius-sm: 6px;
|
|
--font-ui: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, Cantarell, sans-serif;
|
|
--font-mono: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
|
|
--content-max: 1180px;
|
|
--transition-theme: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
|
|
|
|
/* Gradient palette — reused across icons, borders and glow shadows */
|
|
--grad-1: linear-gradient(135deg, #7c5cf9, #b18bfa);
|
|
--grad-2: linear-gradient(135deg, #06b6d4, #5eead4);
|
|
--grad-3: linear-gradient(135deg, #f59e0b, #fb923c);
|
|
--grad-4: linear-gradient(135deg, #ec4899, #fb7185);
|
|
--grad-5: linear-gradient(135deg, #10b981, #6ee7b7);
|
|
--grad-6: linear-gradient(135deg, #3b82f6, #7dd3fc);
|
|
--grad-7: linear-gradient(135deg, #ef4444, #f97316);
|
|
--glow-1: rgba(124, 92, 249, .35);
|
|
--glow-2: rgba(6, 182, 212, .35);
|
|
--glow-3: rgba(245, 158, 11, .35);
|
|
--glow-4: rgba(236, 72, 153, .35);
|
|
--glow-5: rgba(16, 185, 129, .35);
|
|
--glow-6: rgba(59, 130, 246, .35);
|
|
--glow-7: rgba(239, 68, 68, .35);
|
|
}
|
|
|
|
html[data-theme="dark"] {
|
|
--bg: #0a0c10;
|
|
--bg-raised: #12151b;
|
|
--surface: #161a21;
|
|
--surface-hover: #1d222b;
|
|
--border: #262c37;
|
|
--border-strong: #363e4b;
|
|
--text: #eceef2;
|
|
--text-secondary: #a7aec0;
|
|
--text-muted: #7c8497;
|
|
--accent: #9d8bff;
|
|
--accent-strong: #b6a7ff;
|
|
--accent-soft: #1c1a33;
|
|
--accent-contrast: #0a0c10;
|
|
--dot: rgba(157, 139, 255, 0.16);
|
|
--line: rgba(157, 139, 255, 0.08);
|
|
--shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 4px 20px rgba(0, 0, 0, 0.4);
|
|
--shadow-hover: 0 4px 14px rgba(157, 139, 255, 0.18), 0 18px 40px rgba(0, 0, 0, 0.45);
|
|
}
|
|
|
|
/* ============================================================
|
|
2. RESET & BASE
|
|
============================================================ */
|
|
*, *::before, *::after { box-sizing: border-box; }
|
|
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
|
|
@media (prefers-reduced-motion: reduce) {
|
|
html { scroll-behavior: auto; }
|
|
*, *::before, *::after { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; scroll-behavior: auto !important; }
|
|
}
|
|
|
|
body {
|
|
margin: 0;
|
|
font-family: var(--font-ui);
|
|
background-color: var(--bg);
|
|
color: var(--text);
|
|
line-height: 1.6;
|
|
transition: var(--transition-theme);
|
|
background-image:
|
|
linear-gradient(var(--line) 1px, transparent 1px),
|
|
linear-gradient(90deg, var(--line) 1px, transparent 1px),
|
|
radial-gradient(var(--dot) 1px, transparent 1px);
|
|
background-size: 64px 64px, 64px 64px, 16px 16px;
|
|
background-position: -1px -1px, -1px -1px, -1px -1px;
|
|
}
|
|
|
|
/* Logo Theme Dynamic Filter */
|
|
html[data-theme="light"] .nav-icon,
|
|
html[data-theme="light"] .hero-logo,
|
|
html[data-theme="light"] .footer-logo {
|
|
filter: invert(1) hue-rotate(180deg);
|
|
}
|
|
|
|
img { max-width: 100%; display: block; }
|
|
a { color: inherit; }
|
|
ul { list-style: none; margin: 0; padding: 0; }
|
|
h1, h2, h3, h4, p { margin: 0; }
|
|
button { font-family: inherit; }
|
|
table { border-collapse: collapse; width: 100%; }
|
|
|
|
::selection { background: var(--accent); color: var(--accent-contrast); }
|
|
|
|
a:focus-visible, button:focus-visible, summary:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 3px;
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
.skip-link {
|
|
position: absolute; left: 1rem; top: -100px;
|
|
background: var(--accent); color: var(--accent-contrast);
|
|
padding: 0.65rem 1.1rem; border-radius: var(--radius-sm);
|
|
font-size: 0.9rem; font-weight: 600; z-index: 200;
|
|
transition: top 0.2s ease;
|
|
}
|
|
.skip-link:focus { top: 1rem; }
|
|
|
|
/* Hidden icon sprite sheet */
|
|
.icon-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
|
|
|
|
.icon {
|
|
width: 20px; height: 20px;
|
|
stroke: currentColor;
|
|
fill: none;
|
|
stroke-width: 1.8;
|
|
stroke-linecap: round;
|
|
stroke-linejoin: round;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.icon-badge {
|
|
width: 46px; height: 46px;
|
|
border-radius: var(--radius-sm);
|
|
display: flex; align-items: center; justify-content: center;
|
|
color: #fff;
|
|
margin-bottom: 1rem;
|
|
box-shadow: var(--shadow);
|
|
}
|
|
.icon-badge .icon { width: 24px; height: 24px; }
|
|
|
|
/* Gradient utility classes */
|
|
.grad-1 { background: var(--grad-1); }
|
|
.grad-2 { background: var(--grad-2); }
|
|
.grad-3 { background: var(--grad-3); }
|
|
.grad-4 { background: var(--grad-4); }
|
|
.grad-5 { background: var(--grad-5); }
|
|
.grad-6 { background: var(--grad-6); }
|
|
.grad-7 { background: var(--grad-7); }
|
|
|
|
/* ============================================================
|
|
3. LAYOUT PRIMITIVES
|
|
============================================================ */
|
|
.wrap { max-width: var(--content-max); margin-inline: auto; padding-inline: 1.5rem; }
|
|
section { padding-block: clamp(3.5rem, 7vw, 6.5rem); }
|
|
.section-head { max-width: 680px; margin-bottom: clamp(2rem, 4vw, 3rem); }
|
|
.eyebrow {
|
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
|
font-family: var(--font-mono); font-size: 0.75rem;
|
|
letter-spacing: 0.14em; text-transform: uppercase;
|
|
background: linear-gradient(90deg, var(--accent), #ec4899);
|
|
-webkit-background-clip: text; background-clip: text; color: transparent;
|
|
margin-bottom: 0.75rem; font-weight: 700;
|
|
}
|
|
.section-title { font-size: clamp(1.6rem, 3.2vw, 2.25rem); font-weight: 800; letter-spacing: -0.01em; margin-bottom: 0.75rem; }
|
|
.section-sub { color: var(--text-secondary); font-size: 1.05rem; max-width: 65ch; }
|
|
|
|
/* ============================================================
|
|
4. NAVIGATION
|
|
============================================================ */
|
|
.site-header {
|
|
position: sticky; top: 0; z-index: 100;
|
|
background-color: color-mix(in srgb, var(--bg) 85%, transparent);
|
|
backdrop-filter: saturate(160%) blur(10px);
|
|
-webkit-backdrop-filter: saturate(160%) blur(10px);
|
|
border-bottom: 1px solid var(--border);
|
|
transition: var(--transition-theme);
|
|
}
|
|
.nav-inner {
|
|
max-width: var(--content-max); margin-inline: auto; padding: 0.85rem 1.5rem;
|
|
display: flex; align-items: center; justify-content: space-between; gap: 1rem;
|
|
}
|
|
.brand { display: flex; align-items: center; gap: 0.5rem; font-weight: 800; font-size: 1.15rem; letter-spacing: -0.01em; text-decoration: none; }
|
|
.nav-icon { height: 26px; width: auto; }
|
|
.nav-links { display: flex; align-items: center; gap: 0.15rem; }
|
|
.nav-links a {
|
|
text-decoration: none; font-size: 0.9rem; font-weight: 500; color: var(--text-secondary);
|
|
padding: 0.5rem 0.65rem; border-radius: var(--radius-sm);
|
|
transition: color 0.15s ease, background-color 0.15s ease;
|
|
}
|
|
.nav-links a:hover { color: var(--text); background-color: var(--surface); }
|
|
.nav-links a.is-active { color: var(--accent); }
|
|
.nav-right { display: flex; align-items: center; gap: 0.5rem; }
|
|
|
|
.theme-toggle {
|
|
display: inline-flex; align-items: center; gap: 0.4rem;
|
|
background: var(--surface); border: 1px solid var(--border); color: var(--text);
|
|
border-radius: var(--radius); padding: 0.45rem 0.7rem; font-size: 0.82rem;
|
|
font-family: var(--font-mono); cursor: pointer;
|
|
transition: background-color 0.15s ease, border-color 0.15s ease;
|
|
}
|
|
.theme-toggle:hover { background: var(--surface-hover); border-color: var(--border-strong); }
|
|
|
|
.hamburger {
|
|
display: none; background: transparent; border: 1px solid var(--border); border-radius: var(--radius-sm);
|
|
width: 40px; height: 40px; cursor: pointer; color: var(--text);
|
|
align-items: center; justify-content: center;
|
|
}
|
|
.hamburger span, .hamburger span::before, .hamburger span::after {
|
|
content: ""; display: block; width: 18px; height: 2px; background: currentColor; position: relative;
|
|
transition: transform 0.2s ease, opacity 0.2s ease;
|
|
}
|
|
.hamburger span::before { position: absolute; top: -6px; }
|
|
.hamburger span::after { position: absolute; top: 6px; }
|
|
.hamburger[aria-expanded="true"] span { background: transparent; }
|
|
.hamburger[aria-expanded="true"] span::before { transform: translateY(6px) rotate(45deg); }
|
|
.hamburger[aria-expanded="true"] span::after { transform: translateY(-6px) rotate(-45deg); }
|
|
|
|
@media (max-width: 860px) {
|
|
.hamburger { display: inline-flex; }
|
|
.nav-links {
|
|
position: fixed; inset: 61px 0 auto 0; flex-direction: column; align-items: stretch;
|
|
background: var(--bg-raised); border-bottom: 1px solid var(--border); padding: 0.5rem;
|
|
gap: 0; transform: translateY(-8px); opacity: 0; pointer-events: none;
|
|
transition: transform 0.18s ease, opacity 0.18s ease; max-height: 80vh; overflow-y: auto;
|
|
}
|
|
.nav-links.is-open { transform: translateY(0); opacity: 1; pointer-events: auto; }
|
|
.nav-links a { padding: 0.85rem 1rem; border-radius: var(--radius-sm); }
|
|
}
|
|
|
|
/* ============================================================
|
|
5. HERO
|
|
============================================================ */
|
|
.hero { padding-block: clamp(4rem, 10vw, 7.5rem) clamp(3rem, 6vw, 5rem); position: relative; overflow: hidden; }
|
|
.hero::before {
|
|
content: ""; position: absolute; top: -20%; right: -10%; width: 560px; height: 560px;
|
|
background: radial-gradient(circle, var(--glow-1), transparent 70%);
|
|
filter: blur(10px); pointer-events: none; opacity: 0.55; z-index: 0;
|
|
}
|
|
.hero::after {
|
|
content: ""; position: absolute; bottom: -30%; left: -10%; width: 480px; height: 480px;
|
|
background: radial-gradient(circle, var(--glow-2), transparent 70%);
|
|
filter: blur(10px); pointer-events: none; opacity: 0.4; z-index: 0;
|
|
}
|
|
.hero .wrap { position: relative; z-index: 1; }
|
|
.hero-kicker {
|
|
font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.16em; text-transform: uppercase;
|
|
color: var(--text-muted); margin-bottom: 1.25rem;
|
|
}
|
|
.hero-kicker::before { content: "$ "; color: var(--accent); }
|
|
.hero-logo { max-width: 600px; width: 100%; height: auto; margin-bottom: 1rem; filter: drop-shadow(0 20px 40px rgba(0,0,0,0.3)); }
|
|
.hero h2 { font-size: clamp(1.1rem, 2.6vw, 1.5rem); font-weight: 500; color: var(--text-secondary); margin-top: 0.6rem; letter-spacing: -0.005em; }
|
|
.hero blockquote {
|
|
margin: 1.75rem 0 1.5rem; padding-left: 1.1rem;
|
|
border-left: 3px solid transparent;
|
|
border-image: linear-gradient(180deg, var(--accent), #ec4899) 1;
|
|
font-size: clamp(1.05rem, 2vw, 1.3rem); font-weight: 600; max-width: 46ch;
|
|
}
|
|
.hero p.lede { max-width: 64ch; color: var(--text-secondary); font-size: 1.05rem; margin-bottom: 1.25rem; }
|
|
.hero-byline { font-size: 0.9rem; color: var(--text-muted); margin-bottom: 2rem; }
|
|
.hero-byline strong { color: var(--text-secondary); }
|
|
.hero-ctas { display: flex; flex-wrap: wrap; gap: 0.85rem; }
|
|
|
|
.btn {
|
|
position: relative; overflow: hidden; display: inline-flex; align-items: center; gap: 0.5rem;
|
|
font-weight: 600; font-size: 0.95rem; padding: 0.75rem 1.4rem; border-radius: var(--radius);
|
|
text-decoration: none; cursor: pointer; border: 1px solid transparent;
|
|
transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease, border-color 0.15s ease;
|
|
}
|
|
.btn:active { transform: translateY(1px); }
|
|
.btn-primary { background: linear-gradient(135deg, var(--accent), #ec4899); color: #fff; }
|
|
.btn-primary:hover { box-shadow: 0 10px 24px var(--glow-1); transform: translateY(-1px); }
|
|
.btn-outline { background: transparent; color: var(--text); border-color: var(--border-strong); }
|
|
.btn-outline:hover { background: var(--surface); border-color: var(--accent); }
|
|
.btn-sm { padding: 0.5rem 0.9rem; font-size: 0.85rem; }
|
|
|
|
.btn .ripple { position: absolute; border-radius: 50%; background: rgba(255,255,255,0.5); transform: scale(0); pointer-events: none; animation: ripple 0.6s ease-out; }
|
|
html[data-theme="dark"] .btn-outline .ripple { background: rgba(255,255,255,0.18); }
|
|
@keyframes ripple { to { transform: scale(3); opacity: 0; } }
|
|
|
|
/* ============================================================
|
|
6. ABOUT
|
|
============================================================ */
|
|
.about-panel {
|
|
background: linear-gradient(135deg, var(--accent-soft), var(--surface));
|
|
border: 1px solid var(--border); border-radius: var(--radius);
|
|
padding: clamp(1.5rem, 4vw, 2.5rem); font-size: 1.1rem; color: var(--text-secondary); line-height: 1.75;
|
|
}
|
|
.about-panel strong { color: var(--text); }
|
|
|
|
/* ============================================================
|
|
7. COLORFUL CARD GRIDS
|
|
============================================================ */
|
|
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); gap: 1.15rem; }
|
|
|
|
.card {
|
|
background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius);
|
|
padding: 1.6rem; position: relative; overflow: hidden;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
|
}
|
|
.card::before {
|
|
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
|
|
background: var(--card-grad, var(--grad-1));
|
|
}
|
|
.card:hover { transform: translateY(-4px); box-shadow: 0 14px 34px var(--card-glow, var(--glow-1)); border-color: var(--border-strong); }
|
|
.card .icon-badge { background: var(--card-grad, var(--grad-1)); }
|
|
.card h3 { font-size: 1.08rem; font-weight: 700; margin-bottom: 0.5rem; }
|
|
.card p { color: var(--text-secondary); font-size: 0.94rem; }
|
|
.card ul { margin-top: 0.6rem; }
|
|
.card li {
|
|
color: var(--text-secondary); font-size: 0.9rem; position: relative; padding-left: 1.1rem; margin-top: 0.3rem;
|
|
}
|
|
.card li::before { content: "●"; position: absolute; left: 0; top: 0.05em; font-size: 0.55rem; color: var(--accent); }
|
|
.card .card-tag {
|
|
font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.04em; text-transform: uppercase;
|
|
color: var(--text-muted); display: block; margin-top: 0.9rem;
|
|
}
|
|
|
|
/* ============================================================
|
|
8. PROJECTS
|
|
============================================================ */
|
|
.callout {
|
|
display: flex; gap: 0.85rem; align-items: flex-start;
|
|
background: var(--accent-soft); border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--border));
|
|
border-radius: var(--radius); padding: 1.1rem 1.3rem; margin-bottom: 2rem; font-size: 0.95rem; color: var(--text-secondary);
|
|
}
|
|
.callout strong { color: var(--text); }
|
|
.callout .icon { color: var(--accent); margin-top: 0.15rem; }
|
|
|
|
.project-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 1.15rem; }
|
|
@media (max-width: 780px) { .project-grid { grid-template-columns: 1fr; } }
|
|
|
|
.project-card {
|
|
background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius);
|
|
padding: 1.6rem; display: flex; flex-direction: column; gap: 0.9rem; position: relative; overflow: hidden;
|
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
|
}
|
|
.project-card::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: var(--card-grad, var(--grad-1)); }
|
|
.project-card:hover { transform: translateY(-4px); box-shadow: 0 14px 34px var(--card-glow, var(--glow-1)); border-color: var(--border-strong); }
|
|
.project-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
|
|
.project-title { display: flex; align-items: center; gap: 0.65rem; }
|
|
.project-title .icon-badge { width: 38px; height: 38px; margin-bottom: 0; border-radius: var(--radius-sm); background: var(--card-grad, var(--grad-1)); }
|
|
.project-title .icon-badge .icon { width: 19px; height: 19px; }
|
|
.project-card h3 { font-family: var(--font-mono); font-size: 1.02rem; font-weight: 700; }
|
|
.status-badge {
|
|
font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.06em; text-transform: uppercase;
|
|
padding: 0.22rem 0.55rem; border-radius: 999px; white-space: nowrap; color: #fff; align-self: flex-start;
|
|
}
|
|
.project-card p.desc { color: var(--text-secondary); font-size: 0.93rem; }
|
|
.tech-badges { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-top: 0.5rem; }
|
|
.tech-badge {
|
|
font-family: var(--font-mono); font-size: 0.72rem; color: #fff; border-radius: var(--radius-sm);
|
|
padding: 0.22rem 0.55rem; background: var(--card-grad, var(--grad-1)); opacity: 0.92;
|
|
}
|
|
.project-links { display: flex; gap: 0.6rem; margin-top: auto; padding-top: 0.5rem; }
|
|
|
|
/* ============================================================
|
|
9. STATS
|
|
============================================================ */
|
|
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; text-align: center; }
|
|
.stat {
|
|
background: var(--bg-raised); border: 1px solid var(--border); border-radius: var(--radius);
|
|
padding: 1.75rem 1rem; position: relative; overflow: hidden;
|
|
}
|
|
.stat::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--card-grad, var(--grad-1)); }
|
|
.stat .num {
|
|
font-family: var(--font-mono); font-size: clamp(1.8rem, 4vw, 2.5rem); font-weight: 700; display: block;
|
|
background: var(--card-grad, var(--grad-1)); -webkit-background-clip: text; background-clip: text; color: transparent;
|
|
}
|
|
.stat .label { color: var(--text-secondary); font-size: 0.85rem; margin-top: 0.35rem; }
|
|
|
|
/* ============================================================
|
|
10. POSITIONING TABLE
|
|
============================================================ */
|
|
.positioning { overflow-x: auto; border-radius: var(--radius); border: 1px solid var(--border); }
|
|
.positioning table { min-width: 560px; }
|
|
.positioning th, .positioning td { text-align: left; padding: 0.9rem 1.2rem; font-size: 0.92rem; }
|
|
.positioning thead th {
|
|
font-family: var(--font-mono); font-size: 0.72rem; letter-spacing: 0.06em; text-transform: uppercase;
|
|
color: #fff; padding-block: 1rem;
|
|
}
|
|
.positioning thead th:first-child { background: var(--grad-7); }
|
|
.positioning thead th:last-child { background: var(--grad-5); }
|
|
.positioning tbody tr { border-top: 1px solid var(--border); background: var(--bg-raised); }
|
|
.positioning tbody tr:nth-child(even) { background: var(--surface); }
|
|
.positioning td:first-child { color: var(--text-muted); position: relative; padding-left: 2.2rem; }
|
|
.positioning td:first-child::before { content: "✕"; position: absolute; left: 1.2rem; color: #ef4444; font-weight: 700; font-size: 0.8rem; }
|
|
.positioning td:last-child { color: var(--text); font-weight: 600; position: relative; padding-left: 2.2rem; }
|
|
.positioning td:last-child::before { content: "✓"; position: absolute; left: 1.2rem; color: #10b981; font-weight: 700; font-size: 0.8rem; }
|
|
|
|
/* ============================================================
|
|
11. VISION
|
|
============================================================ */
|
|
.vision-panel {
|
|
position: relative; overflow: hidden;
|
|
background: linear-gradient(135deg, var(--accent-soft), var(--surface));
|
|
border: 1px solid var(--border); border-radius: var(--radius); padding: clamp(1.75rem, 5vw, 3rem);
|
|
}
|
|
.vision-panel::after {
|
|
content: ""; position: absolute; inset: 0; border-radius: var(--radius); padding: 1px;
|
|
background: linear-gradient(135deg, var(--accent), #ec4899, #06b6d4);
|
|
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
|
-webkit-mask-composite: xor; mask-composite: exclude; opacity: 0.5; pointer-events: none;
|
|
}
|
|
.vision-panel p {
|
|
font-size: clamp(1.1rem, 2.4vw, 1.4rem); font-weight: 500; letter-spacing: -0.005em; color: var(--text); max-width: 62ch; position: relative;
|
|
}
|
|
.vision-panel p + p { margin-top: 1.1rem; color: var(--text-secondary); font-weight: 400; font-size: clamp(1rem, 2vw, 1.15rem); }
|
|
|
|
/* ============================================================
|
|
12. VALUES
|
|
============================================================ */
|
|
.value-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); gap: 0.8rem; }
|
|
.value-chip {
|
|
border: 1px solid var(--border); border-radius: var(--radius); padding: 1.2rem 0.9rem; text-align: center;
|
|
background: var(--bg-raised); position: relative; overflow: hidden;
|
|
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
|
}
|
|
.value-chip::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: var(--card-grad, var(--grad-1)); }
|
|
.value-chip:hover { transform: translateY(-3px); box-shadow: 0 10px 24px var(--card-glow, var(--glow-1)); }
|
|
.value-chip .n {
|
|
font-family: var(--font-mono); font-size: 0.7rem; display: block; margin-bottom: 0.5rem; font-weight: 700;
|
|
background: var(--card-grad, var(--grad-1)); -webkit-background-clip: text; background-clip: text; color: transparent;
|
|
}
|
|
.value-chip .t { font-weight: 600; font-size: 0.95rem; }
|
|
|
|
/* ============================================================
|
|
13. ROADMAP
|
|
============================================================ */
|
|
.roadmap-intro { color: var(--text-secondary); max-width: 70ch; margin-bottom: 2.5rem; font-size: 1rem; }
|
|
.roadmap-intro strong { color: var(--text); }
|
|
|
|
.circuit { display: flex; align-items: flex-start; overflow-x: auto; padding-block: 1rem 0.5rem; scrollbar-width: thin; }
|
|
.node { flex: 1 0 100px; min-width: 100px; display: flex; flex-direction: column; align-items: center; text-align: center; position: relative; padding-top: 2.75rem; }
|
|
.node::before {
|
|
content: ""; position: absolute; top: 12px; left: -50%; width: 100%; height: 2px;
|
|
background: repeating-linear-gradient(90deg, var(--border-strong) 0 6px, transparent 6px 11px); z-index: 0;
|
|
}
|
|
.node:first-child::before { display: none; }
|
|
.node.is-active::before { background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 11px); }
|
|
.node .pad {
|
|
width: 28px; height: 28px; border-radius: 50%; border: 2px solid var(--border-strong); background: var(--bg-raised);
|
|
display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 0.72rem;
|
|
font-weight: 700; color: var(--text-muted); position: absolute; top: 0; z-index: 1;
|
|
transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
|
}
|
|
.node.is-active .pad {
|
|
border-color: transparent; background: var(--card-grad, var(--grad-1)); color: #fff;
|
|
box-shadow: 0 0 0 4px var(--card-glow, var(--glow-1));
|
|
}
|
|
.node:hover .pad { transform: scale(1.12); }
|
|
.node .name { font-family: var(--font-mono); font-size: 0.82rem; font-weight: 700; margin-top: 0.4rem; }
|
|
.node .state { font-size: 0.72rem; color: var(--text-muted); margin-top: 0.15rem; }
|
|
.node.is-active .state { color: var(--accent); font-weight: 600; }
|
|
|
|
@media (max-width: 720px) {
|
|
.circuit { flex-direction: column; align-items: stretch; overflow-x: visible; }
|
|
.node { flex-direction: row; text-align: left; padding: 0 0 1.75rem 2.9rem; min-width: 0; align-items: flex-start; }
|
|
.node::before { top: 0; left: 13px; width: 2px; height: 100%; background: repeating-linear-gradient(180deg, var(--border-strong) 0 6px, transparent 6px 11px); }
|
|
.node.is-active::before { background: repeating-linear-gradient(180deg, var(--accent) 0 6px, transparent 6px 11px); }
|
|
.node .pad { top: 0; left: 0; }
|
|
.node .name { margin-top: 0; margin-left: 0.15rem; }
|
|
.node .text { display: flex; flex-direction: column; }
|
|
}
|
|
|
|
.roadmap-footnote { margin-top: 2rem; font-size: 0.95rem; color: var(--text-secondary); border-top: 1px solid var(--border); padding-top: 1.5rem; }
|
|
.roadmap-footnote strong { color: var(--text); }
|
|
|
|
/* ============================================================
|
|
14. REVEAL ANIMATION
|
|
============================================================ */
|
|
.reveal { opacity: 0; transform: translateY(14px); transition: opacity 0.5s ease, transform 0.5s ease; }
|
|
.reveal.is-visible { opacity: 1; transform: translateY(0); }
|
|
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; transition: none; } }
|
|
|
|
/* ============================================================
|
|
15. FOOTER
|
|
============================================================ */
|
|
.site-footer { border-top: 1px solid var(--border); padding-block: 3rem 2rem; margin-top: 2rem; position: relative; overflow: hidden; }
|
|
.site-footer::before {
|
|
content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
|
|
background: linear-gradient(90deg, var(--grad-1), var(--grad-2), var(--grad-3), var(--grad-4), var(--grad-5));
|
|
}
|
|
.footer-grid { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 2rem; }
|
|
.footer-brand .mark {
|
|
font-family: var(--font-mono); font-weight: 800; font-size: 1.3rem;
|
|
background: linear-gradient(135deg, var(--accent), #ec4899); -webkit-background-clip: text; background-clip: text; color: transparent;
|
|
}
|
|
.footer-logo { max-width: 300px; width: 100%; height: auto; opacity: 0.5; margin-bottom: 0.5rem; }
|
|
.footer-brand .sub { color: var(--text-muted); font-size: 0.85rem; margin-top: 0.2rem; }
|
|
.footer-motto { font-size: 0.95rem; color: var(--text-secondary); line-height: 1.7; }
|
|
.footer-links { display: flex; gap: 1.5rem; }
|
|
.footer-links a { text-decoration: none; color: var(--text-secondary); font-size: 0.9rem; font-weight: 600; display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
.footer-links a:hover { color: var(--accent); }
|
|
.footer-bottom {
|
|
margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid var(--border);
|
|
font-family: var(--font-mono); font-size: 0.78rem; color: var(--text-muted); text-align: center; letter-spacing: 0.02em;
|
|
}
|
|
|
|
@media print {
|
|
.site-header, .theme-toggle, .hamburger, .hero-ctas, .btn, .project-links, .circuit { display: none !important; }
|
|
body { background-image: none; color: #000; }
|
|
section { padding-block: 1rem; }
|
|
a { color: #000; text-decoration: underline; }
|
|
.card::before, .project-card::before, .stat::before, .value-chip::before { display: none; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<a href="#main" class="skip-link">Skip to content</a>
|
|
|
|
<!-- ICON SPRITE -->
|
|
<svg class="icon-sprite" aria-hidden="true">
|
|
<defs>
|
|
<symbol id="i-key" viewBox="0 0 24 24"><circle cx="8" cy="15" r="4"/><path d="M11 12l9-9M17 6l3 3M14 9l2 2"/></symbol>
|
|
<symbol id="i-server" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="6" rx="1.5"/><rect x="3" y="14" width="18" height="6" rx="1.5"/><circle cx="7" cy="7" r="1" fill="currentColor" stroke="none"/><circle cx="7" cy="17" r="1" fill="currentColor" stroke="none"/></symbol>
|
|
<symbol id="i-gauge" viewBox="0 0 24 24"><path d="M4 15a8 8 0 1116 0"/><path d="M12 15l4-5"/><circle cx="12" cy="15" r="1" fill="currentColor" stroke="none"/></symbol>
|
|
<symbol id="i-lock" viewBox="0 0 24 24"><rect x="5" y="11" width="14" height="9" rx="2"/><path d="M8 11V7a4 4 0 018 0v4"/></symbol>
|
|
<symbol id="i-shield-check" viewBox="0 0 24 24"><path d="M12 3l7 3v6c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3z"/><path d="M9 12l2 2 4-4"/></symbol>
|
|
<symbol id="i-wrench" viewBox="0 0 24 24"><path d="M14.7 6.3a4 4 0 00-5.4 5.4L4 17v3h3l5.3-5.3a4 4 0 005.4-5.4l-2.5 2.5-2-2 2.5-2.5z"/></symbol>
|
|
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 7v5l3.5 2"/></symbol>
|
|
<symbol id="i-cpu" viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="1.5"/><path d="M9 3v3M15 3v3M9 18v3M15 18v3M3 9h3M3 15h3M18 9h3M18 15h3"/></symbol>
|
|
<symbol id="i-box" viewBox="0 0 24 24"><path d="M12 3l8 4.5v9L12 21l-8-4.5v-9L12 3z"/><path d="M4.5 7.5L12 12l7.5-4.5M12 12v9"/></symbol>
|
|
<symbol id="i-database" viewBox="0 0 24 24"><ellipse cx="12" cy="6" rx="7" ry="3"/><path d="M5 6v6c0 1.7 3.1 3 7 3s7-1.3 7-3V6"/><path d="M5 12v6c0 1.7 3.1 3 7 3s7-1.3 7-3v-6"/></symbol>
|
|
<symbol id="i-bolt" viewBox="0 0 24 24"><path d="M13 2L4 14h6l-1 8 9-12h-6l1-8z"/></symbol>
|
|
<symbol id="i-route" viewBox="0 0 24 24"><circle cx="5" cy="6" r="2.3"/><circle cx="19" cy="18" r="2.3"/><path d="M5 8.3v3a4 4 0 004 4h6a4 4 0 004 4"/></symbol>
|
|
<symbol id="i-layers" viewBox="0 0 24 24"><path d="M12 3l9 5-9 5-9-5 9-5z"/><path d="M3 13l9 5 9-5M3 8l9 5 9-5"/></symbol>
|
|
<symbol id="i-terminal" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9l4 3-4 3M13 15h4"/></symbol>
|
|
<symbol id="i-monitor" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="12" rx="1.5"/><path d="M8 20h8M12 16v4"/><path d="M6 12l3-3 2 2 4-4"/></symbol>
|
|
<symbol id="i-infinity" viewBox="0 0 24 24"><path d="M7 9a3.5 3.5 0 100 7 3.5 3.5 0 002.9-1.5L12 12l2.1 2.5A3.5 3.5 0 1017 9a3.5 3.5 0 00-2.9 1.5L12 13l-2.1-2.5A3.5 3.5 0 007 9z"/></symbol>
|
|
<symbol id="i-chart" viewBox="0 0 24 24"><path d="M4 20V10M10 20V4M16 20v-7M22 20H2"/></symbol>
|
|
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 3l7 3v6c0 4.5-3 7.5-7 9-4-1.5-7-4.5-7-9V6l7-3z"/></symbol>
|
|
<symbol id="i-link" viewBox="0 0 24 24"><path d="M9 15l6-6"/><path d="M11 6l1-1a4 4 0 015.5 5.5l-2 2"/><path d="M13 18l-1 1a4 4 0 01-5.5-5.5l2-2"/></symbol>
|
|
<symbol id="i-globe" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17M12 3.5c2.5 2.3 4 5.3 4 8.5s-1.5 6.2-4 8.5c-2.5-2.3-4-5.3-4-8.5s1.5-6.2 4-8.5z"/></symbol>
|
|
<symbol id="i-flag" viewBox="0 0 24 24"><path d="M5 3v18"/><path d="M5 4h13l-3 4 3 4H5"/></symbol>
|
|
<symbol id="i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M12 11v5.5M12 8v.01"/></symbol>
|
|
<symbol id="i-git" viewBox="0 0 24 24"><circle cx="6" cy="6" r="2.2"/><circle cx="6" cy="18" r="2.2"/><circle cx="18" cy="12" r="2.2"/><path d="M6 8.2v7.6M6 8.2c0 4 3 5.8 6 5.8h4"/></symbol>
|
|
</defs>
|
|
</svg>
|
|
|
|
<header class="site-header">
|
|
<div class="nav-inner">
|
|
<a href="#home" class="brand">
|
|
<img src="icon.svg" alt="" class="nav-icon">
|
|
NX9
|
|
</a>
|
|
<nav class="nav-links" id="navLinks" aria-label="Primary">
|
|
<a href="#home" data-nav="home">Home</a>
|
|
<a href="#philosophy" data-nav="philosophy">Philosophy</a>
|
|
<a href="#architecture" data-nav="architecture">Architecture</a>
|
|
<a href="#projects" data-nav="projects">Projects</a>
|
|
<a href="#vision" data-nav="vision">Vision</a>
|
|
<a href="#values" data-nav="values">Values</a>
|
|
<a href="#roadmap" data-nav="roadmap">Roadmap</a>
|
|
<a href="case-study.html" target="_self">Case Study</a>
|
|
</nav>
|
|
<div class="nav-right">
|
|
<button class="theme-toggle" id="themeToggle" aria-label="Toggle color theme">
|
|
<span id="themeIcon" aria-hidden="true">◐</span><span id="themeLabel">Theme</span>
|
|
</button>
|
|
<button class="hamburger" id="hamburger" aria-label="Toggle navigation menu" aria-expanded="false" aria-controls="navLinks">
|
|
<span></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main id="main">
|
|
|
|
<section class="hero" id="home">
|
|
<div class="wrap">
|
|
<p class="hero-kicker">nx9 --version</p>
|
|
<img src="logo.svg" alt="NX9 Logo" class="hero-logo reveal">
|
|
<h2>Next Generation 9</h2>
|
|
<blockquote>Software people can own, understand, and control.</blockquote>
|
|
<p class="lede">NX9 is a philosophy-driven ecosystem of independent, self-hosted, privacy-first infrastructure software — single-binary Rust applications built for people and organizations who would rather run their own infrastructure than rent someone else's. No telemetry. No subscriptions. No mandatory cloud.</p>
|
|
<p class="hero-byline">Built and maintained by <strong>Sunil Thakare</strong> (<span style="font-family: var(--font-mono);">@thakares</span>)</p>
|
|
<div class="hero-ctas">
|
|
<a href="#projects" class="btn btn-primary">View Projects</a>
|
|
<a href="#philosophy" class="btn btn-outline">Read Philosophy</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="about" aria-labelledby="about-heading">
|
|
<div class="wrap">
|
|
<div class="about-panel reveal">
|
|
<p id="about-heading"><strong>NX9</strong> is not a framework, and it is not a cloud product that happens to support self-hosting. It is a collection of carefully engineered, single-binary Rust applications — designed for individuals and organizations that value ownership, simplicity, and independence, and built to run equally well on a Raspberry Pi, a mini PC, a VPS, or air-gapped government infrastructure.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="philosophy" aria-labelledby="philosophy-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-flag"/></svg> 01 — Design Philosophy</p>
|
|
<h2 class="section-title" id="philosophy-title">Principles That Don't Change Per Project</h2>
|
|
<p class="section-sub">Nine commitments that stay constant across every NX9 application, regardless of what it does.</p>
|
|
</div>
|
|
<div class="card-grid" id="philosophyGrid"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="architecture" aria-labelledby="architecture-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-layers"/></svg> 02 — Architecture</p>
|
|
<h2 class="section-title" id="architecture-title">Core Architectural Pillars</h2>
|
|
<p class="section-sub">Every NX9 application is built on the same nine foundations — the same reason a BZOD deployment and a ChronoSeal deployment feel like they came from the same shop.</p>
|
|
</div>
|
|
<div class="card-grid" id="architectureGrid"></div>
|
|
|
|
<div class="section-head reveal" style="margin-top: 3.5rem;">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-shield-check"/></svg> Security Stack</p>
|
|
<h2 class="section-title">Embedded, Not Bolted On</h2>
|
|
<p class="section-sub">Modern cryptographic primitives and hardened defaults, standardized across the whole ecosystem.</p>
|
|
</div>
|
|
<div class="card-grid" id="securityGrid"></div>
|
|
|
|
<div class="section-head reveal" style="margin-top: 3.5rem;">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-chart"/></svg> Positioning</p>
|
|
<h2 class="section-title">The NX9 Trade-off, Made Explicit</h2>
|
|
</div>
|
|
<div class="positioning reveal">
|
|
<table>
|
|
<thead>
|
|
<tr><th>Traditional Stack</th><th>NX9</th></tr>
|
|
</thead>
|
|
<tbody id="positioningBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="projects" aria-labelledby="projects-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-box"/></svg> 03 — Ecosystem</p>
|
|
<h2 class="section-title" id="projects-title">Current Projects</h2>
|
|
<p class="section-sub">Four projects are currently available. The NX9 ecosystem is designed to grow into a family of nine focused, self-hosted applications.</p>
|
|
</div>
|
|
|
|
<div class="callout reveal">
|
|
<svg class="icon"><use href="#i-info"/></svg>
|
|
<p><strong>Codeberg is the canonical home of NX9.</strong> GitHub repositories are maintained as mirrors to improve discoverability and make adoption easier.</p>
|
|
</div>
|
|
|
|
<div class="project-grid" id="projectGrid"></div>
|
|
|
|
<div class="stats-grid" id="statsGrid" style="margin-top: 3rem;"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="vision" aria-labelledby="vision-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-globe"/></svg> 04 — Vision</p>
|
|
<h2 class="section-title" id="vision-title">Why NX9 Exists</h2>
|
|
</div>
|
|
<div class="vision-panel reveal">
|
|
<p>In an era of cloud lock-in, subscriptions, and heavy dependencies, NX9 builds software that serves humanity — not platforms.</p>
|
|
<p>Technology should empower people to own their infrastructure, data, and future.</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="values" aria-labelledby="values-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-key"/></svg> 05 — Values</p>
|
|
<h2 class="section-title" id="values-title">Core Values</h2>
|
|
<p class="section-sub">The twelve commitments every NX9 decision is measured against.</p>
|
|
</div>
|
|
<div class="value-grid" id="valueGrid"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="roadmap" aria-labelledby="roadmap-title">
|
|
<div class="wrap">
|
|
<div class="section-head reveal">
|
|
<p class="eyebrow"><svg class="icon" style="width:14px;height:14px"><use href="#i-route"/></svg> 06 — Roadmap</p>
|
|
<h2 class="section-title" id="roadmap-title">Roadmap to Nine</h2>
|
|
</div>
|
|
<p class="roadmap-intro reveal"><strong>NX9 is not a single application — it is an ecosystem of nine focused, interoperable tools</strong> built around ownership, privacy, simplicity, and open standards. Four projects are already available, with five more planned to complete the vision.</p>
|
|
|
|
<div class="circuit reveal" id="circuit" role="list" aria-label="NX9 ecosystem roadmap, four of nine milestones complete"></div>
|
|
|
|
<p class="roadmap-footnote reveal">Nodes are ordered by ship date, not ambition — <strong>chronoseal, dns-server, auth,</strong> and <strong>url-shortener</strong> shipped in that sequence. Five more applications are planned to complete the family of nine, reusing the same Tokio · Axum · SQLite · Argon2 · Ed25519 · BLAKE3 foundation.</p>
|
|
</div>
|
|
</section>
|
|
|
|
</main>
|
|
|
|
<footer class="site-footer">
|
|
<div class="wrap">
|
|
<div class="footer-grid">
|
|
<div class="footer-brand">
|
|
<img src="logo.svg" alt="NX9 Logo" class="footer-logo">
|
|
<div class="sub">Next Generation 9</div>
|
|
</div>
|
|
<p class="footer-motto">Software people can own,<br>understand,<br>and control.<br><br><strong>Always Free. Always Open. Always Yours.</strong></p>
|
|
<nav class="footer-links" aria-label="Repository links">
|
|
<a href="https://codeberg.org/thakares" target="_blank" rel="noopener noreferrer"><svg class="icon" style="width:16px;height:16px"><use href="#i-git"/></svg> Codeberg</a>
|
|
<a href="https://github.com/thakares?tab=repositories" target="_blank" rel="noopener noreferrer"><svg class="icon" style="width:16px;height:16px"><use href="#i-link"/></svg> GitHub</a>
|
|
</nav>
|
|
</div>
|
|
<div class="footer-bottom">© <span id="year"></span> NX9 — an open-source ecosystem by Sunil Thakare. MIT / Apache-2.0.</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script>
|
|
(function () {
|
|
"use strict";
|
|
|
|
/* ============================================================
|
|
THEME
|
|
============================================================ */
|
|
const root = document.documentElement;
|
|
const themeToggle = document.getElementById("themeToggle");
|
|
const themeIcon = document.getElementById("themeIcon");
|
|
const themeLabel = document.getElementById("themeLabel");
|
|
const STORAGE_KEY = "nx9-theme";
|
|
|
|
function applyTheme(theme) {
|
|
root.setAttribute("data-theme", theme);
|
|
themeIcon.textContent = theme === "dark" ? "●" : "○";
|
|
themeLabel.textContent = theme === "dark" ? "Dark" : "Light";
|
|
themeToggle.setAttribute("aria-pressed", theme === "dark" ? "true" : "false");
|
|
}
|
|
|
|
function initTheme() {
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
if (stored === "light" || stored === "dark") { applyTheme(stored); return; }
|
|
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
applyTheme(prefersDark ? "dark" : "light");
|
|
}
|
|
|
|
themeToggle.addEventListener("click", function () {
|
|
const current = root.getAttribute("data-theme");
|
|
const next = current === "dark" ? "light" : "dark";
|
|
applyTheme(next);
|
|
localStorage.setItem(STORAGE_KEY, next);
|
|
});
|
|
|
|
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", function (e) {
|
|
if (!localStorage.getItem(STORAGE_KEY)) applyTheme(e.matches ? "dark" : "light");
|
|
});
|
|
|
|
initTheme();
|
|
|
|
/* ============================================================
|
|
MOBILE NAV
|
|
============================================================ */
|
|
const hamburger = document.getElementById("hamburger");
|
|
const navLinks = document.getElementById("navLinks");
|
|
|
|
hamburger.addEventListener("click", function () {
|
|
const isOpen = navLinks.classList.toggle("is-open");
|
|
hamburger.setAttribute("aria-expanded", isOpen ? "true" : "false");
|
|
});
|
|
|
|
navLinks.querySelectorAll("a").forEach(function (link) {
|
|
link.addEventListener("click", function () {
|
|
navLinks.classList.remove("is-open");
|
|
hamburger.setAttribute("aria-expanded", "false");
|
|
});
|
|
});
|
|
|
|
/* ============================================================
|
|
ICON HELPER
|
|
============================================================ */
|
|
function iconSvg(id) {
|
|
return '<svg class="icon"><use href="#' + id + '"/></svg>';
|
|
}
|
|
|
|
/* ============================================================
|
|
DATA: DESIGN PHILOSOPHY
|
|
============================================================ */
|
|
const philosophy = [
|
|
{ icon: "i-key", grad: 1, glow: "--glow-1", title: "Operator Ownership", body: "The operator owns the binaries, the configuration, the databases, the backups, and the encryption keys. No service depends on proprietary APIs or cloud-hosted control planes." },
|
|
{ icon: "i-server", grad: 6, glow: "--glow-6", title: "Self-Hosting First", body: "Every decision begins with one question: \u201cCan this be deployed and operated by a single administrator?\u201d If it needs Kubernetes or a managed database, the design is reconsidered." },
|
|
{ icon: "i-gauge", grad: 3, glow: "--glow-3", title: "Simplicity Over Complexity", body: "One binary. One configuration. One SQLite database. One administrator. Simple systems are easier to understand, secure, and recover." },
|
|
{ icon: "i-lock", grad: 4, glow: "--glow-4", title: "Privacy by Default", body: "Applications never assume cloud connectivity, analytics collection, telemetry uploads, or third-party tracking. Users decide what information leaves their infrastructure." },
|
|
{ icon: "i-shield-check", grad: 5, glow: "--glow-5", title: "Security by Design", body: "Argon2id, Ed25519, BLAKE3, RBAC, CSRF protection, and audit logs are embedded throughout the stack \u2014 not added later." },
|
|
{ icon: "i-wrench", grad: 2, glow: "--glow-2", title: "Operational Excellence", body: "Diagnostics, backups, restore procedures, migration tools, CLI administration, and documentation, in every project, from day one." },
|
|
{ icon: "i-clock", grad: 7, glow: "--glow-7", title: "Long-Term Stability", body: "NX9 intentionally avoids dependency churn. The goal is software that remains understandable and maintainable years into the future." },
|
|
{ icon: "i-git", grad: 4, glow: "--glow-4", title: "Open Source First", body: "Every NX9 codebase is 100% open source under permissive licenses. You have the fundamental right to inspect, build, and modify the software." },
|
|
{ icon: "i-infinity", grad: 5, glow: "--glow-5", title: "Zero Vendor Lock-In", body: "All data is stored in standard SQLite files, configurations are plain text, and architectures strictly avoid proprietary integrations." }
|
|
];
|
|
|
|
const philosophyGrid = document.getElementById("philosophyGrid");
|
|
philosophy.forEach(function (p) {
|
|
const card = document.createElement("article");
|
|
card.className = "card reveal";
|
|
card.style.setProperty("--card-grad", "var(--grad-" + p.grad + ")");
|
|
card.style.setProperty("--card-glow", "var(" + p.glow + ")");
|
|
card.innerHTML =
|
|
'<div class="icon-badge">' + iconSvg(p.icon) + '</div>' +
|
|
'<h3>' + p.title + '</h3>' +
|
|
'<p>' + p.body + '</p>';
|
|
philosophyGrid.appendChild(card);
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: ARCHITECTURAL PILLARS
|
|
============================================================ */
|
|
const architecture = [
|
|
{ icon: "i-cpu", grad: 1, glow: "--glow-1", title: "Pure Rust", body: "Memory safety, zero-cost abstractions, and fearless concurrency. Rust 2021 Edition, release-mode optimization, no runtime interpreter.", tag: "~10\u201312 MB per binary" },
|
|
{ icon: "i-box", grad: 6, glow: "--glow-6", title: "Single Binary Deployment", body: "One executable contains the HTTP server, business logic, auth, migrations, CLI, and templates. No JVM, no Node.js, no Python, no app server.", tag: "cargo build --release" },
|
|
{ icon: "i-database", grad: 5, glow: "--glow-5", title: "SQLite First", body: "ACID transactions, WAL concurrency, zero administration, and portable files. Several projects isolate data across multiple databases for tenant isolation.", tag: "users.db \u00b7 system.db \u00b7 content.db" },
|
|
{ icon: "i-bolt", grad: 3, glow: "--glow-3", title: "Tokio Async Runtime", body: "Asynchronous I/O and lightweight tasks power web servers, DNS resolution, and background workers with high concurrency.", tag: "async / await" },
|
|
{ icon: "i-route", grad: 4, glow: "--glow-4", title: "Axum Web Layer", body: "Routing, middleware, REST APIs, HTML rendering, authentication, and sessions \u2014 integrated naturally with Tokio and Rust's type system.", tag: "HTTP \u00b7 middleware \u00b7 sessions" },
|
|
{ icon: "i-layers", grad: 2, glow: "--glow-2", title: "Layered Architecture", body: "auth / cli / db / handlers / jobs / services / templates / utils / web \u2014 each layer owns one responsibility, for easier testing and lower coupling.", tag: "src/ layout" },
|
|
{ icon: "i-terminal", grad: 7, glow: "--glow-7", title: "CLI-First Operations", body: "serve \u00b7 doctor \u00b7 backup \u00b7 restore \u00b7 migrate \u00b7 create-admin \u00b7 init \u2014 every service is administrable headless, over SSH, from a script.", tag: "automation-friendly" },
|
|
{ icon: "i-shield", grad: 1, glow: "--glow-1", title: "Security Stack", body: "Argon2id password hashing, Ed25519 signatures, BLAKE3 hashing, CSRF protection, RBAC, and hardened compiler flags \u2014 RELRO, PIE, least privilege.", tag: "defense in depth" },
|
|
{ icon: "i-monitor", grad: 6, glow: "--glow-6", title: "Operational Tooling", body: "Health checks, migration verification, automatic backups, restore tools, Docker support, systemd units, and structured logging, out of the box.", tag: "diagnostics \u00b7 systemd \u00b7 docker" }
|
|
];
|
|
|
|
const architectureGrid = document.getElementById("architectureGrid");
|
|
architecture.forEach(function (a) {
|
|
const card = document.createElement("article");
|
|
card.className = "card reveal";
|
|
card.style.setProperty("--card-grad", "var(--grad-" + a.grad + ")");
|
|
card.style.setProperty("--card-glow", "var(" + a.glow + ")");
|
|
card.innerHTML =
|
|
'<div class="icon-badge">' + iconSvg(a.icon) + '</div>' +
|
|
'<h3>' + a.title + '</h3>' +
|
|
'<p>' + a.body + '</p>' +
|
|
'<span class="card-tag">' + a.tag + '</span>';
|
|
architectureGrid.appendChild(card);
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: SECURITY
|
|
============================================================ */
|
|
const security = [
|
|
{ icon: "i-shield-check", grad: 4, glow: "--glow-4", title: "Argon2id Hashing", body: "Memory-hard, CPU-hard password hashing by default for all user credentials." },
|
|
{ icon: "i-key", grad: 1, glow: "--glow-1", title: "Ed25519 Signatures", body: "Fast, secure public-key cryptography for tokens and inter-service authentication." },
|
|
{ icon: "i-lock", grad: 6, glow: "--glow-6", title: "Strict RBAC", body: "Role-Based Access Control enforced at the middleware layer before business logic executes." },
|
|
{ icon: "i-shield", grad: 5, glow: "--glow-5", title: "CSRF & Security Headers", body: "Double-submit cookies, strict transport security, and hardened Content Security Policies built-in." }
|
|
];
|
|
const securityGrid = document.getElementById("securityGrid");
|
|
security.forEach(function (s) {
|
|
const card = document.createElement("article");
|
|
card.className = "card reveal";
|
|
card.style.setProperty("--card-grad", "var(--grad-" + s.grad + ")");
|
|
card.style.setProperty("--card-glow", "var(" + s.glow + ")");
|
|
card.innerHTML =
|
|
'<div class="icon-badge">' + iconSvg(s.icon) + '</div>' +
|
|
'<h3>' + s.title + '</h3>' +
|
|
'<p>' + s.body + '</p>';
|
|
securityGrid.appendChild(card);
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: POSITIONING
|
|
============================================================ */
|
|
const positioningBody = document.getElementById("positioningBody");
|
|
const posData = [
|
|
{ bad: "Requires Kubernetes & Helm", good: "Deployed via a single binary or systemd" },
|
|
{ bad: "Hidden telemetry and usage analytics", good: "Zero tracking, completely air-gapped capable" },
|
|
{ bad: "1GB+ RAM for a 'Hello World' microservice", good: "10-20MB RAM for a high-traffic production service" },
|
|
{ bad: "Forced cloud subscriptions", good: "Your hardware, your data, your rules" },
|
|
{ bad: "Complex web of dependencies", good: "Static binaries with SQLite built-in" }
|
|
];
|
|
posData.forEach(p => {
|
|
positioningBody.innerHTML += `<tr><td>${p.bad}</td><td>${p.good}</td></tr>`;
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: PROJECTS
|
|
============================================================ */
|
|
const projectGrid = document.getElementById("projectGrid");
|
|
const projects = [
|
|
{
|
|
title: "nx9-url-shortener",
|
|
desc: "A lightweight, self-hosted URL management platform. Combines URL shortening, analytics, QR codes, password protection, and API automation with zero external dependencies.",
|
|
status: "STABLE",
|
|
icon: "i-link",
|
|
tech: ["Rust", "SQLite", "Axum", "Askama"],
|
|
grad: 2, glow: "--glow-2",
|
|
link: "https://codeberg.org/thakares/nx9-url-shortener",
|
|
github: "https://github.com/thakares/nx9-url-shortener"
|
|
},
|
|
{
|
|
title: "nx9-auth",
|
|
desc: "Identity and Access Management (IAM) service. Provides authentication, authorization, session management, and RBAC in a single incredibly fast deployable binary.",
|
|
status: "BETA",
|
|
icon: "i-lock",
|
|
tech: ["Rust", "SQLite", "Security"],
|
|
grad: 4, glow: "--glow-4",
|
|
link: "https://codeberg.org/thakares/nx9-auth",
|
|
github: "https://github.com/thakares/nx9-auth"
|
|
},
|
|
{
|
|
title: "nx9-dns-server",
|
|
desc: "High-performance, fully RFC-compliant authoritative DNS server leveraging modern async networking and a robust SQLite backend.",
|
|
status: "ALPHA",
|
|
icon: "i-globe",
|
|
tech: ["Rust", "DNS", "Async"],
|
|
grad: 6, glow: "--glow-6",
|
|
link: "https://codeberg.org/thakares/nx9-dns-server",
|
|
github: "https://github.com/thakares/nx9-dns-server"
|
|
},
|
|
{
|
|
title: "nx9-chronoseal-rs",
|
|
desc: "Privacy-first browser attestation framework for anti-bot and anti-AI scraping. Uses Ed25519 heartbeat chains and WASM attestation without tracking users.",
|
|
status: "STABLE",
|
|
icon: "i-shield",
|
|
tech: ["Rust", "WASM", "Ed25519"],
|
|
grad: 5, glow: "--glow-5",
|
|
link: "https://codeberg.org/thakares/nx9-chronoseal-rs",
|
|
github: "https://github.com/thakares/nx9-chronoseal-rs"
|
|
}
|
|
];
|
|
projects.forEach(p => {
|
|
projectGrid.innerHTML += `
|
|
<div class="project-card reveal" style="--card-grad: var(--grad-${p.grad}); --card-glow: var(${p.glow})">
|
|
<div class="project-card-head">
|
|
<div class="project-title">
|
|
<div class="icon-badge">${iconSvg(p.icon)}</div>
|
|
<h3>${p.title}</h3>
|
|
</div>
|
|
<span class="status-badge" style="background: var(--grad-${p.grad === 5 ? 5 : p.grad === 4 ? 4 : p.grad === 6 ? 6 : 2})">${p.status}</span>
|
|
</div>
|
|
<p class="desc">${p.desc}</p>
|
|
<div class="tech-badges">
|
|
${p.tech.map(t => `<span class="tech-badge" style="background: var(--grad-${p.grad})">${t}</span>`).join("")}
|
|
</div>
|
|
<div class="project-links">
|
|
<a href="${p.link}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline"><svg class="icon" style="width:14px;height:14px"><use href="#i-git"/></svg> Codeberg</a>
|
|
<a href="${p.github}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-outline"><svg class="icon" style="width:14px;height:14px"><use href="#i-link"/></svg> GitHub</a>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: STATS
|
|
============================================================ */
|
|
const statsGrid = document.getElementById("statsGrid");
|
|
const stats = [
|
|
{ num: "0", label: "External Dependencies", grad: 1 },
|
|
{ num: "1", label: "Binary to Deploy", grad: 6 },
|
|
{ num: "9", label: "Core Projects", grad: 4 },
|
|
{ num: "<15MB", label: "RAM per Service", grad: 5 }
|
|
];
|
|
stats.forEach(s => {
|
|
statsGrid.innerHTML += `
|
|
<div class="stat reveal" style="--card-grad: var(--grad-${s.grad})">
|
|
<span class="num">${s.num}</span>
|
|
<span class="label">${s.label}</span>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: VALUES
|
|
============================================================ */
|
|
const valueGrid = document.getElementById("valueGrid");
|
|
const values = [
|
|
{ num: "01", text: "User Ownership", grad: 1, glow: "--glow-1" },
|
|
{ num: "02", text: "Absolute Privacy", grad: 2, glow: "--glow-2" },
|
|
{ num: "03", text: "Open Source", grad: 3, glow: "--glow-3" },
|
|
{ num: "04", text: "Simplicity First", grad: 4, glow: "--glow-4" },
|
|
{ num: "05", text: "CLI Automation", grad: 5, glow: "--glow-5" },
|
|
{ num: "06", text: "Open Standards", grad: 6, glow: "--glow-6" },
|
|
{ num: "07", text: "Long-term Stability", grad: 7, glow: "--glow-7" },
|
|
{ num: "08", text: "Linux Native", grad: 1, glow: "--glow-1" },
|
|
{ num: "09", text: "Zero Tracking", grad: 2, glow: "--glow-2" },
|
|
{ num: "10", text: "No Vendor Lock", grad: 3, glow: "--glow-3" },
|
|
{ num: "11", text: "Performant", grad: 4, glow: "--glow-4" },
|
|
{ num: "12", text: "Understandable", grad: 5, glow: "--glow-5" }
|
|
];
|
|
values.forEach(v => {
|
|
valueGrid.innerHTML += `
|
|
<div class="value-chip reveal" style="--card-grad: var(--grad-${v.grad}); --card-glow: var(${v.glow})">
|
|
<span class="n">${v.num}</span>
|
|
<span class="t">${v.text}</span>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
/* ============================================================
|
|
DATA: ROADMAP
|
|
============================================================ */
|
|
const circuit = document.getElementById("circuit");
|
|
const roadmap = [
|
|
{ num: "01", name: "ChronoSeal", state: "STABLE", active: true },
|
|
{ num: "02", name: "DNS Server", state: "ALPHA", active: true },
|
|
{ num: "03", name: "Auth", state: "BETA", active: true },
|
|
{ num: "04", name: "URL Shortener", state: "STABLE", active: true },
|
|
{ num: "05", name: "Project 5", state: "PLANNED", active: false },
|
|
{ num: "06", name: "Project 6", state: "PLANNED", active: false },
|
|
{ num: "07", name: "Project 7", state: "PLANNED", active: false },
|
|
{ num: "08", name: "Project 8", state: "PLANNED", active: false },
|
|
{ num: "09", name: "Project 9", state: "PLANNED", active: false }
|
|
];
|
|
roadmap.forEach(r => {
|
|
circuit.innerHTML += `
|
|
<div class="node ${r.active ? 'is-active' : ''} reveal">
|
|
<div class="pad">${r.num}</div>
|
|
<div class="text">
|
|
<div class="name">${r.name}</div>
|
|
<div class="state">${r.state}</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
});
|
|
|
|
/* ============================================================
|
|
REVEAL ANIMATIONS
|
|
============================================================ */
|
|
const observerOptions = {
|
|
root: null,
|
|
rootMargin: "0px",
|
|
threshold: 0.1
|
|
};
|
|
const observer = new IntersectionObserver((entries, observer) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add("is-visible");
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
document.querySelectorAll(".reveal").forEach(el => observer.observe(el));
|
|
|
|
// Set copyright year
|
|
document.getElementById("year").textContent = new Date().getFullYear();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html> |