/var/www/sites/splice/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SpliceVPN — Mesh every endpoint you've got</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Syne:wght@600;700;800&family=Hanken+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#0a0d0c;
--bg-2:#0e1311;
--panel:#101615;
--ink:#e9efe9;
--muted:#869089;
--faint:#586059;
--line:rgba(190,255,190,.09);
--line-2:rgba(190,255,190,.05);
--signal:#c1f53e; /* live link */
--signal-dim:rgba(193,245,62,.14);
--wire:#54d6c9; /* secondary path */
--relay:#ff8a5c; /* fallback relay */
--radius:14px;
--maxw:1180px;
}
*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
body{
background:var(--bg);
color:var(--ink);
font-family:'Hanken Grotesk',sans-serif;
line-height:1.55;
-webkit-font-smoothing:antialiased;
overflow-x:hidden;
}
/* ---- atmosphere ---- */
body::before{
content:"";position:fixed;inset:0;z-index:0;pointer-events:none;
background:
radial-gradient(900px 600px at 78% -5%, rgba(84,214,201,.10), transparent 60%),
radial-gradient(800px 700px at 12% 8%, rgba(193,245,62,.07), transparent 55%);
}
body::after{
content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.5;
background-image:
linear-gradient(var(--line-2) 1px,transparent 1px),
linear-gradient(90deg,var(--line-2) 1px,transparent 1px);
background-size:44px 44px;
mask-image:radial-gradient(circle at 50% 30%,#000,transparent 85%);
}
.grain{
position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.035;mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
.wrap{position:relative;z-index:2;max-width:var(--maxw);margin:0 auto;padding:0 28px}
/* ---- kickers / mono ---- */
.mono{font-family:'JetBrains Mono',monospace}
.kicker{
font-family:'JetBrains Mono',monospace;font-size:12px;letter-spacing:.22em;
text-transform:uppercase;color:var(--signal);display:inline-flex;align-items:center;gap:9px;
}
.kicker::before{content:"";width:22px;height:1px;background:var(--signal);display:inline-block}
/* ---- nav ---- */
nav{
position:relative;z-index:3;display:flex;align-items:center;justify-content:space-between;
max-width:var(--maxw);margin:0 auto;padding:26px 28px;
}
.brand{display:flex;align-items:center;gap:11px;font-family:'Syne',sans-serif;font-weight:800;font-size:20px;letter-spacing:-.01em}
.brand .dot{width:11px;height:11px;border-radius:50%;background:var(--signal);box-shadow:0 0 0 4px var(--signal-dim);animation:pulse 2.6s infinite}
@keyframes pulse{0%,100%{box-shadow:0 0 0 3px var(--signal-dim)}50%{box-shadow:0 0 0 7px rgba(193,245,62,.05)}}
.navlinks{display:flex;gap:30px;font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--muted)}
.navlinks a{color:var(--muted);text-decoration:none;transition:color .2s}
.navlinks a:hover{color:var(--ink)}
.navbtn{
font-family:'JetBrains Mono',monospace;font-size:13px;color:var(--bg);background:var(--signal);
padding:9px 16px;border-radius:8px;text-decoration:none;font-weight:700;transition:transform .15s,box-shadow .2s;
}
.navbtn:hover{transform:translateY(-1px);box-shadow:0 6px 22px rgba(193,245,62,.25)}
@media(max-width:760px){.navlinks{display:none}}
/* ---- hero ---- */
header{position:relative;padding:48px 0 80px}
.hero-grid{display:grid;grid-template-columns:1.05fr .95fr;gap:50px;align-items:center}
@media(max-width:920px){.hero-grid{grid-template-columns:1fr;gap:34px}}
h1{
font-family:'Syne',sans-serif;font-weight:800;letter-spacing:-.025em;line-height:.98;
font-size:clamp(44px,6.6vw,82px);margin:22px 0 0;
}
h1 .lit{color:var(--signal)}
.lede{font-size:clamp(17px,2vw,20px);color:var(--muted);max-width:46ch;margin-top:24px}
.cta-row{display:flex;gap:14px;margin-top:34px;flex-wrap:wrap}
.btn-primary{
font-family:'JetBrains Mono',monospace;font-weight:700;font-size:14px;color:var(--bg);background:var(--signal);
padding:14px 24px;border-radius:10px;text-decoration:none;display:inline-flex;align-items:center;gap:10px;
transition:transform .15s,box-shadow .2s;
}
.btn-primary:hover{transform:translateY(-2px);box-shadow:0 10px 30px rgba(193,245,62,.28)}
.btn-ghost{
font-family:'JetBrains Mono',monospace;font-weight:500;font-size:14px;color:var(--ink);
padding:14px 22px;border-radius:10px;text-decoration:none;border:1px solid var(--line);transition:border-color .2s,background .2s;
}
.btn-ghost:hover{border-color:rgba(193,245,62,.4);background:rgba(193,245,62,.04)}
/* hero stage / diagram */
.stage{
position:relative;border:1px solid var(--line);border-radius:var(--radius);
background:linear-gradient(160deg,rgba(20,28,26,.7),rgba(10,14,13,.4));
padding:18px;overflow:hidden;backdrop-filter:blur(2px);
}
.stage-bar{display:flex;align-items:center;justify-content:space-between;font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--faint);margin-bottom:10px;padding:0 4px}
.stage-bar .live{color:var(--signal)}
.stage svg{width:100%;height:auto;display:block}
/* ---- section scaffolding ---- */
section{position:relative;padding:74px 0;border-top:1px solid var(--line-2)}
.sec-head{max-width:62ch}
.sec-head h2{font-family:'Syne',sans-serif;font-weight:700;font-size:clamp(28px,3.6vw,42px);letter-spacing:-.02em;line-height:1.05;margin-top:16px}
.sec-head p{color:var(--muted);margin-top:16px;font-size:17px}
/* ---- capability grid ---- */
.grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:46px}
@media(max-width:900px){.grid{grid-template-columns:1fr 1fr}}
@media(max-width:600px){.grid{grid-template-columns:1fr}}
.card{
position:relative;border:1px solid var(--line);border-radius:var(--radius);padding:26px 24px;
background:rgba(16,22,21,.55);transition:transform .25s,border-color .25s,background .25s;overflow:hidden;
}
.card::after{content:"";position:absolute;inset:0;background:radial-gradient(400px 200px at var(--mx,50%) var(--my,0%),rgba(193,245,62,.07),transparent 70%);opacity:0;transition:opacity .3s;pointer-events:none}
.card:hover{transform:translateY(-4px);border-color:rgba(193,245,62,.32);background:rgba(20,28,26,.7)}
.card:hover::after{opacity:1}
.card .num{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--signal);letter-spacing:.1em}
.card .ico{width:40px;height:40px;margin:16px 0 14px;color:var(--signal)}
.card h3{font-family:'Syne',sans-serif;font-weight:700;font-size:20px;letter-spacing:-.01em}
.card p{color:var(--muted);font-size:15px;margin-top:9px}
/* ---- path-finding ladder ---- */
.ladder{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:46px}
@media(max-width:820px){.ladder{grid-template-columns:1fr}}
.tier{
position:relative;border:1px solid var(--line);border-radius:var(--radius);padding:26px;
background:rgba(16,22,21,.5);
}
.tier .tg{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.2em;text-transform:uppercase}
.tier h4{font-family:'Syne',sans-serif;font-weight:700;font-size:22px;margin:14px 0 8px;letter-spacing:-.01em}
.tier p{color:var(--muted);font-size:15px}
.tier .barline{height:4px;border-radius:3px;margin-top:20px;background:var(--line)}
.t1 .tg{color:var(--signal)} .t1 .barline{background:linear-gradient(90deg,var(--signal),transparent)}
.t2 .tg{color:var(--wire)} .t2 .barline{background:linear-gradient(90deg,var(--wire),transparent)}
.t3 .tg{color:var(--relay)} .t3 .barline{background:linear-gradient(90deg,var(--relay),transparent)}
.t3{border-color:rgba(255,138,92,.22)}
.tier .meta{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--faint);margin-top:14px}
/* ---- bonding split ---- */
.split{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;margin-top:10px}
@media(max-width:860px){.split{grid-template-columns:1fr;gap:30px}}
.bond-list{list-style:none;margin-top:26px}
.bond-list li{display:flex;gap:14px;padding:15px 0;border-bottom:1px solid var(--line-2);align-items:flex-start}
.bond-list li:last-child{border-bottom:0}
.bond-list .tick{flex:none;width:22px;height:22px;border-radius:50%;border:1px solid var(--signal);color:var(--signal);display:grid;place-items:center;font-size:12px;margin-top:2px}
.bond-list b{font-family:'Syne',sans-serif;font-weight:600;display:block;font-size:16px}
.bond-list span{color:var(--muted);font-size:14px}
.bond-visual{border:1px solid var(--line);border-radius:var(--radius);background:rgba(16,22,21,.5);padding:16px}
.bond-visual svg{width:100%;height:auto;display:block}
/* ---- setup steps ---- */
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:0;margin-top:46px;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
@media(max-width:760px){.steps{grid-template-columns:1fr}}
.step{padding:30px 26px;border-right:1px solid var(--line);background:rgba(16,22,21,.4)}
.step:last-child{border-right:0}
@media(max-width:760px){.step{border-right:0;border-bottom:1px solid var(--line)}.step:last-child{border-bottom:0}}
.step .q{font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--signal);letter-spacing:.12em}
.step h4{font-family:'Syne',sans-serif;font-weight:700;font-size:19px;margin:14px 0 8px}
.step p{color:var(--muted);font-size:14px}
.step .opts{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px}
.step .opts span{font-family:'JetBrains Mono',monospace;font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:6px;padding:5px 10px}
.terminal{
margin-top:30px;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
font-family:'JetBrains Mono',monospace;font-size:13.5px;background:#0c100f;
}
.terminal .tt{display:flex;gap:7px;padding:11px 15px;border-bottom:1px solid var(--line);align-items:center}
.terminal .tt i{width:11px;height:11px;border-radius:50%;display:block}
.terminal .tt i:nth-child(1){background:#ff5f56}.terminal .tt i:nth-child(2){background:#ffbd2e}.terminal .tt i:nth-child(3){background:#27c93f}
.terminal .tt span{color:var(--faint);margin-left:8px;font-size:12px}
.terminal .body{padding:20px;line-height:1.85}
.terminal .body .c{color:var(--faint)}
.terminal .body .ok{color:var(--signal)}
.terminal .body .w{color:var(--wire)}
.terminal .body .r{color:var(--relay)}
.terminal .body .prompt{color:var(--ink)}
.terminal .body .cursor{display:inline-block;width:9px;height:16px;background:var(--signal);vertical-align:-3px;animation:blink 1.1s steps(2) infinite}
@keyframes blink{50%{opacity:0}}
/* ---- closing cta ---- */
.closer{text-align:center;padding:96px 0}
.closer h2{font-family:'Syne',sans-serif;font-weight:800;font-size:clamp(32px,5vw,60px);letter-spacing:-.025em;line-height:1.02}
.closer p{color:var(--muted);margin:20px auto 0;max-width:50ch;font-size:18px}
footer{border-top:1px solid var(--line-2);padding:34px 0}
.foot{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:16px;font-family:'JetBrains Mono',monospace;font-size:12px;color:var(--faint)}
/* ---- reveal ---- */
.reveal{opacity:0;transform:translateY(24px);transition:opacity .7s cubic-bezier(.2,.7,.2,1),transform .7s cubic-bezier(.2,.7,.2,1)}
.reveal.in{opacity:1;transform:none}
.d1{transition-delay:.08s}.d2{transition-delay:.16s}.d3{transition-delay:.24s}.d4{transition-delay:.32s}.d5{transition-delay:.4s}.d6{transition-delay:.48s}
/* animated dashes */
.flow{stroke-dasharray:6 9;animation:dash 1.3s linear infinite}
@keyframes dash{to{stroke-dashoffset:-30}}
.flow-slow{stroke-dasharray:5 12;animation:dash 2.4s linear infinite}
.lit-link{color:var(--signal);text-decoration:none;border-bottom:1px solid rgba(193,245,62,.4)}
</style>
</head>
<body>
<!-- SPLICEVPN-MVP-NOTICE-START -->
<div style="margin:1rem 0;padding:.85rem 1rem;border:1px solid #bae6fd;background:#f0f9ff;border-radius:10px;color:#0c4a6e">
<strong>Current MVP architecture:</strong>
SpliceVPN currently uses your own WireGuard hub relay. Direct peer-to-peer traversal and hole-punching are planned for a later build.
</div>
<!-- SPLICEVPN-MVP-NOTICE-END -->
<div class="grain"></div>
<nav>
<div class="brand"><span class="dot"></span>SPLICEVPN</div>
<div class="navlinks">
<a href="#how">How it works</a>
<a href="#paths">Connection</a>
<a href="#bond">Bonding</a>
<a href="#setup">Setup</a>
</div>
<a href="#getstarted" class="navbtn">Get started</a>
</nav>
<header>
<div class="wrap hero-grid">
<div>
<span class="kicker reveal in">Peer-to-peer mesh · multi-link · zero-config</span>
<h1 class="reveal in d1">Link all your points.<br><span class="lit">Use every connection</span> you've got.</h1>
<p class="lede reveal in d2">SpliceVPN builds an encrypted mesh across all your endpoints — each one a single machine or a whole network — then bonds every internet link at each node for more bandwidth, more resilience, or both. It finds a path between any two nodes on its own, and when there's no other way, it falls back to a relay we run for you.</p>
<div class="cta-row reveal in d3">
<a href="#getstarted" class="btn-primary">Build your mesh →</a>
<a href="#how" class="btn-ghost">See how it works</a>
</div>
</div>
<div class="stage reveal in d4">
<div class="stage-bar"><span>mesh://a ⇄ b ⇄ c ⇄ d ⇄ e</span><span class="live">● MESH · 5 NODES</span></div>
<!-- HERO DIAGRAM: many endpoints in a full mesh, relay on standby -->
<svg viewBox="0 0 520 330" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Many endpoints connected in a full mesh, with a relay node on standby">
<defs>
<linearGradient id="gsig" x1="0" x2="1"><stop offset="0" stop-color="#c1f53e"/><stop offset="1" stop-color="#54d6c9"/></linearGradient>
</defs>
<!-- relay (standby) -->
<g opacity=".5">
<rect x="232" y="20" width="56" height="32" rx="9" fill="#13100e" stroke="#ff8a5c" stroke-width="1.2"/>
<text x="260" y="40" text-anchor="middle" fill="#ff8a5c" font-family="JetBrains Mono,monospace" font-size="10">RELAY</text>
<path d="M252 52 C 210 84, 180 112, 166 150" fill="none" stroke="#ff8a5c" stroke-width="1.1" stroke-dasharray="4 7" opacity=".7"/>
<path d="M268 52 C 312 84, 344 114, 358 150" fill="none" stroke="#ff8a5c" stroke-width="1.1" stroke-dasharray="4 7" opacity=".7"/>
</g>
<!-- mesh links (every pair) -->
<g stroke="#33433d" stroke-width="1.2">
<line x1="260" y1="96" x2="360" y2="163"/>
<line x1="260" y1="96" x2="322" y2="280"/>
<line x1="260" y1="96" x2="198" y2="280"/>
<line x1="260" y1="96" x2="160" y2="163"/>
<line x1="360" y1="163" x2="322" y2="280"/>
<line x1="360" y1="163" x2="198" y2="280"/>
<line x1="360" y1="163" x2="160" y2="163"/>
<line x1="322" y1="280" x2="198" y2="280"/>
<line x1="322" y1="280" x2="160" y2="163"/>
<line x1="198" y1="280" x2="160" y2="163"/>
</g>
<!-- live traffic on several links -->
<g stroke="url(#gsig)" stroke-width="2.2" fill="none">
<line x1="260" y1="96" x2="360" y2="163" class="flow"/>
<line x1="160" y1="163" x2="260" y2="96" class="flow" style="animation-delay:.3s"/>
<line x1="360" y1="163" x2="322" y2="280" class="flow" style="animation-delay:.6s"/>
<line x1="198" y1="280" x2="322" y2="280" class="flow" style="animation-delay:.15s"/>
<line x1="160" y1="163" x2="198" y2="280" class="flow" style="animation-delay:.5s"/>
</g>
<!-- nodes (hosts + networks) -->
<g>
<rect x="235" y="79" width="50" height="34" rx="9" fill="#10211d" stroke="#c1f53e" stroke-width="1.4"/>
<rect x="248" y="89" width="24" height="14" rx="2" fill="none" stroke="#c1f53e" stroke-width="1.2"/>
</g>
<g>
<rect x="335" y="146" width="50" height="34" rx="9" fill="#0e211f" stroke="#54d6c9" stroke-width="1.4"/>
<circle cx="352" cy="163" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
<circle cx="368" cy="157" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
<circle cx="369" cy="170" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
</g>
<g>
<rect x="297" y="263" width="50" height="34" rx="9" fill="#10211d" stroke="#c1f53e" stroke-width="1.4"/>
<rect x="310" y="273" width="24" height="14" rx="2" fill="none" stroke="#c1f53e" stroke-width="1.2"/>
</g>
<g>
<rect x="173" y="263" width="50" height="34" rx="9" fill="#0e211f" stroke="#54d6c9" stroke-width="1.4"/>
<circle cx="190" cy="280" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
<circle cx="206" cy="274" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
<circle cx="207" cy="287" r="4.2" fill="none" stroke="#54d6c9" stroke-width="1.2"/>
</g>
<g>
<rect x="135" y="146" width="50" height="34" rx="9" fill="#10211d" stroke="#c1f53e" stroke-width="1.4"/>
<rect x="148" y="156" width="24" height="14" rx="2" fill="none" stroke="#c1f53e" stroke-width="1.2"/>
</g>
<text x="260" y="322" text-anchor="middle" fill="#869089" font-family="JetBrains Mono,monospace" font-size="10">every node ⇄ every node · direct, bonded, relay-backed</text>
</svg>
</div>
</div>
</header>
<!-- ============ CAPABILITIES ============ -->
<section id="how">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">What it does</span>
<h2>Six things, one mesh.</h2>
<p>Everything below happens automatically once the mesh is up. You don't configure routes, exchange keys by hand, or touch a firewall rule.</p>
</div>
<div class="grid">
<div class="card reveal d1">
<div class="num">01</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><circle cx="20" cy="9" r="4"/><circle cx="9" cy="30" r="4"/><circle cx="31" cy="30" r="4"/><line x1="18" y1="13" x2="11" y2="26"/><line x1="22" y1="13" x2="29" y2="26"/><line x1="13" y1="30" x2="27" y2="30"/></svg>
<h3>Full mesh</h3>
<p>The current MVP creates a controlled hub-relayed WireGuard overlay between endpoints. A lightweight coordinator helps nodes find each other; traffic is relayed through your own SpliceVPN hub in the current MVP.</p>
</div>
<div class="card reveal d2">
<div class="num">02</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><rect x="5" y="14" width="12" height="12" rx="2"/><circle cx="29" cy="13" r="3.5"/><circle cx="35" cy="24" r="3.5"/><circle cx="25" cy="27" r="3.5"/><line x1="29" y1="16.5" x2="27" y2="24"/><line x1="32" y1="14.5" x2="33" y2="21"/></svg>
<h3>A host or a whole network</h3>
<p>Each node can be a single computer or an entire site. Bridge offices, attach a laptop to a LAN, or join networks that were never meant to meet.</p>
</div>
<div class="card reveal d3">
<div class="num">03</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 12h10c4 0 6 4 12 4"/><path d="M6 20h10c4 0 6-4 12-4"/><path d="M6 28h10c4 0 6 4 12 4"/><circle cx="30" cy="16" r="3.5"/></svg>
<h3>Bond your links</h3>
<p>Where you have more than one internet connection, SpliceVPN combines them — for aggregate bandwidth, automatic failover, or both at once.</p>
</div>
<div class="card reveal d4">
<div class="num">04</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 28 L16 28 L20 12 L28 28 L34 28"/><circle cx="6" cy="28" r="2.5"/><circle cx="34" cy="28" r="2.5"/></svg>
<h3>Always finds a path</h3>
<p>The current MVP uses your own SpliceVPN hub relay so nodes can communicate without opening inbound client firewall rules. Direct traversal and hole-punching are planned for a later build.</p>
</div>
<div class="card reveal d5">
<div class="num">05</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="10" width="6" height="20" rx="1"/><rect x="17" y="10" width="6" height="20" rx="1"/><rect x="28" y="10" width="6" height="20" rx="1"/></svg>
<h3>Any port, any protocol</h3>
<p>UDP, TCP, or both, across one port or many. SpliceVPN adapts to whatever the network in the middle is willing to let through.</p>
</div>
<div class="card reveal d6">
<div class="num">06</div>
<svg class="ico" viewBox="0 0 40 40" fill="none" stroke="currentColor" stroke-width="2"><circle cx="20" cy="20" r="12"/><path d="M20 14v6l4 3"/></svg>
<h3>Done for you</h3>
<p>Run the software, answer a few questions about what you want, and the rest is handled — discovery, keys, routing, failover, all of it.</p>
</div>
</div>
</div>
</section>
<!-- ============ CONNECTION LADDER ============ -->
<section id="paths">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">Point 04 · how a path is found</span>
<h2>It tries the best route, then the next, then ours.</h2>
<p>SpliceVPN works out connectivity for you, between every pair of nodes, wherever they happen to be. It climbs down a ladder until something connects — and the bottom rung is a relay you don't have to find or maintain, because we provide it.</p>
</div>
<div class="ladder">
<div class="tier t1 reveal d1">
<div class="tg">Tier 1</div>
<h4>Direct</h4>
<p>If a reachable path exists between two nodes, SpliceVPN uses it. Lowest latency, no middlemen, full bandwidth.</p>
<div class="meta">latency: lowest · hops: 0</div>
<div class="barline"></div>
</div>
<div class="tier t2 reveal d2">
<div class="tg">Tier 2</div>
<h4>Traversal</h4>
<p>Behind NAT or a strict firewall, SpliceVPN negotiates a path through — hole-punching and multiplexing over whatever ports are open.</p>
<div class="meta">latency: near-direct · hops: 0</div>
<div class="barline"></div>
</div>
<div class="tier t3 reveal d3">
<div class="tg">Tier 3 · last resort</div>
<h4>Relay</h4>
<p>Current MVP traffic routes through your own SpliceVPN hub relay. Direct and traversal modes are planned for a later build.</p>
<div class="meta">supplied & maintained by us · always available</div>
<div class="barline"></div>
</div>
</div>
</div>
</section>
<!-- ============ BONDING ============ -->
<section id="bond">
<div class="wrap">
<div class="split">
<div class="reveal">
<span class="kicker">Point 03 · link bonding</span>
<h2 style="font-family:Syne;font-weight:700;font-size:clamp(28px,3.6vw,42px);letter-spacing:-.02em;line-height:1.05;margin-top:16px">More links in, one fat pipe out.</h2>
<ul class="bond-list">
<li><span class="tick">↑</span><div><b>More bandwidth</b><span>Aggregate two or more connections so a single transfer can use the combined capacity of all of them.</span></div></li>
<li><span class="tick">⟲</span><div><b>More resilience</b><span>If one link drops, traffic keeps flowing over the others — no reconnect, no dropped session.</span></div></li>
<li><span class="tick">⊕</span><div><b>Or both</b><span>Run hot-hot for raw throughput, or keep a link in reserve for failover. You pick at setup; SpliceVPN does the balancing.</span></div></li>
</ul>
</div>
<div class="bond-visual reveal d2">
<svg viewBox="0 0 460 280" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Two separate internet links merging into one bonded tunnel">
<!-- left: three links -->
<text x="14" y="40" fill="#869089" font-family="JetBrains Mono,monospace" font-size="11">fibre</text>
<text x="14" y="146" fill="#869089" font-family="JetBrains Mono,monospace" font-size="11">5g</text>
<text x="14" y="252" fill="#869089" font-family="JetBrains Mono,monospace" font-size="11">dsl</text>
<circle cx="60" cy="34" r="6" fill="#10211d" stroke="#c1f53e" stroke-width="1.5"/>
<circle cx="60" cy="140" r="6" fill="#10211d" stroke="#54d6c9" stroke-width="1.5"/>
<circle cx="60" cy="246" r="6" fill="#10211d" stroke="#ff8a5c" stroke-width="1.5"/>
<!-- merge curves -->
<path d="M66 34 C 180 34, 200 140, 300 140" fill="none" stroke="#c1f53e" stroke-width="2.2" class="flow-slow"/>
<path d="M66 140 C 180 140, 200 140, 300 140" fill="none" stroke="#54d6c9" stroke-width="2.2" class="flow-slow" style="animation-delay:.5s"/>
<path d="M66 246 C 180 246, 200 140, 300 140" fill="none" stroke="#ff8a5c" stroke-width="2.2" class="flow-slow" style="animation-delay:1s"/>
<!-- bonded out -->
<path d="M300 140 L 408 140" fill="none" stroke="#c1f53e" stroke-width="6" stroke-linecap="round" class="flow"/>
<circle cx="300" cy="140" r="10" fill="#0a0d0c" stroke="#c1f53e" stroke-width="2"/>
<text x="408" y="124" text-anchor="end" fill="#c1f53e" font-family="JetBrains Mono,monospace" font-size="11">bonded tunnel</text>
</svg>
</div>
</div>
</div>
</section>
<!-- ============ SETUP ============ -->
<section id="setup">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">Point 06 · setup</span>
<h2>Run it. Answer a few questions. You're connected.</h2>
<p>No manual key exchange, no routing tables, no firewall archaeology. SpliceVPN asks what matters and works out the rest.</p>
</div>
<div class="steps">
<div class="step reveal d1">
<div class="q">Q1</div>
<h4>What's at each node?</h4>
<p>A single machine or a whole network — add as many nodes as you like.</p>
<div class="opts"><span>this host</span><span>remote host</span><span>network / site</span></div>
</div>
<div class="step reveal d2">
<div class="q">Q2</div>
<h4>Optimise for what?</h4>
<p>More speed, more reliability, or a balance of the two across your links.</p>
<div class="opts"><span>speed</span><span>resilience</span><span>both</span></div>
</div>
<div class="step reveal d3">
<div class="q">Q3</div>
<h4>Any limits to respect?</h4>
<p>Specific ports or protocols only? Otherwise SpliceVPN uses whatever works.</p>
<div class="opts"><span>auto</span><span>udp</span><span>tcp</span><span>pick ports</span></div>
</div>
</div>
<div class="terminal reveal d2">
<div class="tt"><i></i><i></i><i></i><span>splicevpn up</span></div>
<div class="body mono">
<div><span class="prompt">$ splicevpn up</span></div>
<div><span class="c"># probing links at this node …</span> <span class="ok">2 found (fibre, 5g)</span></div>
<div><span class="c"># discovering peers …</span> <span class="ok">4 nodes</span></div>
<div><span class="c"> tier 1 direct …</span> <span class="r">blocked</span></div>
<div><span class="c"> tier 2 traversal …</span> <span class="ok">open (udp/41641, tcp/443)</span></div>
<div><span class="c"># bonding 2 links · mode = both</span></div>
<div><span class="ok">✓ mesh up</span> <span class="w">· 5 nodes · 2 links bonded</span></div>
<div><span class="prompt">$</span> <span class="cursor"></span></div>
</div>
</div>
</div>
</section>
<!-- ============ GET STARTED / HOW TO ============ -->
<section id="getstarted">
<div class="wrap">
<div class="sec-head reveal">
<span class="kicker">How to</span>
<h2>Get connected in three steps.</h2>
<p>No manual keys, no config files. On Windows it's a single click — the installer sets up WireGuard and the agent for you.</p>
</div>
<div class="grid">
<div class="card reveal d1">
<div class="num">01</div>
<h3>Install</h3>
<p><b>Windows</b> — download the one-click installer and run it. It installs WireGuard and the SpliceVPN agent for you; just approve the admin prompt.</p>
<div class="cta-row" style="margin-top:16px">
<a href="/Install-SpliceVPN.bat" class="btn-primary">One-click Windows installer →</a>
</div>
<p style="margin-top:14px"><b>Linux</b> — <a href="/splicevpn.tar.gz" class="lit-link">download the build</a>, then <code style="font-family:'JetBrains Mono',monospace;font-size:12px;background:rgba(255,255,255,.05);padding:2px 6px;border-radius:5px">tar xzf splicevpn.tar.gz && sudo ./splicevpn/bin/splice-agent</code></p>
</div>
<div class="card reveal d2">
<div class="num">02</div>
<h3>Enter your join details</h3>
<p>Paste the <b>join key</b> and <b>coordinator address</b> your administrator gives you. That's the only input the agent needs — no key exchange, no routing tables.</p>
</div>
<div class="card reveal d3">
<div class="num">03</div>
<h3>You're on the mesh</h3>
<p>The agent discovers every other node, brings up the encrypted tunnel, and keeps it connected — automatically, even after a reboot. Nothing else to configure.</p>
</div>
</div>
</div>
</section>
<!-- ============ CLOSER ============ -->
<section class="closer">
<div class="wrap">
<span class="kicker reveal" style="justify-content:center">Ready when you are</span>
<h2 class="reveal d1">Every endpoint.<br>Every link between them.</h2>
<p class="reveal d2">Install SpliceVPN on each node, answer three questions, and let it find the way. We'll be the safety net.</p>
<div class="cta-row reveal d3" style="justify-content:center;margin-top:34px">
<a href="/splicevpn.tar.gz" class="btn-primary">Download for Linux →</a>
<a href="/splicevpn-windows-amd64.zip" class="btn-primary">Download for Windows →</a>
<a href="/help.html" class="btn-ghost">Read the docs</a>
</div>
<p class="reveal d3" style="margin-top:24px;font:13px/1.6 'JetBrains Mono',ui-monospace,monospace;color:#8b9096">or one-line install on Windows — PowerShell as Administrator (installs WireGuard for you):</p>
<code class="reveal d3" style="display:block;max-width:720px;margin:10px auto 0;padding:13px 16px;text-align:left;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);border-radius:10px;font:12px/1.55 'JetBrains Mono',ui-monospace,monospace;color:#e8ffd0;overflow-x:auto;white-space:pre-wrap;word-break:break-all">iwr https://splicevpn.com.au/install-splicevpn.ps1 -OutFile $env:TEMP\splice.ps1; Start-Process powershell "-ExecutionPolicy Bypass -File $env:TEMP\splice.ps1" -Verb RunAs</code>
</div>
</section>
<footer>
<div class="wrap foot">
<span>SPLICEVPN — peer-to-peer bonded mesh - splicevpn.com.au</span>
<span>relay supplied · zero-config · any-port</span>
</div>
</footer>
<script>
// scroll reveal
const io = new IntersectionObserver((entries)=>{
entries.forEach(e=>{ if(e.isIntersecting){ e.target.classList.add('in'); io.unobserve(e.target);} });
},{threshold:.14});
document.querySelectorAll('.reveal:not(.in)').forEach(el=>io.observe(el));
// card spotlight follows cursor
document.querySelectorAll('.card').forEach(card=>{
card.addEventListener('pointermove',e=>{
const r=card.getBoundingClientRect();
card.style.setProperty('--mx',((e.clientX-r.left)/r.width*100)+'%');
card.style.setProperty('--my',((e.clientY-r.top)/r.height*100)+'%');
});
});
</script>
</body>
</html>