Files
keysat-xyz-landing/index.html
T
Grant fcfbb95354 Launch-special remaining: drop the total
Dynamic tier-card JS now renders "Limited: N remaining" instead of
"Limited: N of M remaining". Matches the buy page (v0.2.0:36); the
operator's initial launch volume stays private.
2026-05-11 18:30:46 -05:00

1287 lines
60 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Keysat — Bitcoin-native self-hosted licensing service for software creators</title>
<meta name="description" content="Keysat is a Bitcoin-native, self-hosted licensing service for software creators. Runs on Start9. Pays in Bitcoin or fiat. Verifies offline. You own the signing key, the customer list, and the payment rails.">
<link rel="icon" type="image/svg+xml" href="assets/favicon.svg">
<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=Manrope:wght@400;500;600;700;900&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
/* ---------- Brand ---------- */
--navy-950:#0E1F33; --navy-900:#142A47; --navy-800:#1E3A5F; --navy-700:#2A4A75;
--navy-600:#3A5C8A; --navy-500:#5074A1; --navy-400:#7892B8; --navy-300:#A6B7CF;
--navy-200:#CBD5E2; --navy-100:#E4EAF1; --navy-50:#F2F5F9;
--cream-50:#FBF9F2; --cream-100:#F5F1E8; --cream-200:#EDE7D7; --cream-300:#E1D8C0;
--cream-400:#C9BC9A;
--gold-700:#8A6F3D; --gold-600:#A88652; --gold-500:#BFA068; --gold-400:#D4B985;
--gold-300:#E5CFA5; --gold-200:#F0E2C5;
--ink-900:#0E1F33; --ink-700:#2C3E54; --ink-500:#5A6B7F; --ink-400:#7E8C9D; --ink-300:#A4AEBB;
--success:#2D7A5F; --success-bg:#E3F0EA;
--warning:#B8861F; --warning-bg:#F7EFD7;
--danger:#B23A3A; --danger-bg:#F4E0E0;
--border-1:rgba(14,31,51,0.12);
--border-2:rgba(14,31,51,0.20);
--font-display:'Manrope','Helvetica Neue',Arial,sans-serif;
--font-body:'Inter','Helvetica Neue',Arial,sans-serif;
--font-mono:'JetBrains Mono',ui-monospace,'SF Mono',Menlo,monospace;
--shadow-sm:0 1px 2px rgba(14,31,51,0.06),0 1px 1px rgba(14,31,51,0.03);
--shadow-md:0 2px 4px rgba(14,31,51,0.06),0 4px 12px rgba(14,31,51,0.06);
--shadow-lg:0 4px 8px rgba(14,31,51,0.07),0 12px 32px rgba(14,31,51,0.10);
--shadow-xl:0 8px 16px rgba(14,31,51,0.10),0 24px 64px rgba(14,31,51,0.14);
--ease-standard:cubic-bezier(0.2,0.7,0.2,1);
}
* { box-sizing:border-box; }
html, body { margin:0; padding:0; }
body {
font-family:var(--font-body);
color:var(--ink-900);
background:var(--cream-100);
background-image:
radial-gradient(rgba(14,31,51,0.025) 1px, transparent 1px),
radial-gradient(rgba(138,111,61,0.022) 1px, transparent 1px);
background-size:3px 3px, 7px 7px;
background-position:0 0, 1px 1px;
-webkit-font-smoothing:antialiased;
-moz-osx-font-smoothing:grayscale;
}
.wrap { max-width:1180px; margin:0 auto; padding:0 32px; }
a { color:var(--ink-900); text-decoration:none; }
a:hover { color:var(--navy-900); }
/* ---------- Header ---------- */
header.site {
position:sticky; top:0; z-index:20;
background:rgba(245,241,232,0.85);
backdrop-filter:blur(12px);
border-bottom:1px solid var(--border-1);
}
header.site .inner { display:flex; align-items:center; gap:28px; padding:16px 0; }
header.site .brand {
display:flex; align-items:center; gap:10px;
font-family:var(--font-display); font-weight:500; font-size:15px;
letter-spacing:0.30em; text-transform:uppercase; color:var(--navy-900);
}
header.site .brand img { width:32px; height:32px; }
header.site nav { margin-left:auto; display:flex; gap:28px; font-size:14px; }
header.site nav a { color:var(--ink-700); font-weight:500; }
header.site nav a:hover { color:var(--navy-900); }
header.site .cta { margin-left:8px; }
/* ---------- Buttons ---------- */
.btn {
display:inline-flex; align-items:center; gap:8px;
font-family:var(--font-body); font-weight:600; font-size:14px;
padding:11px 20px; border-radius:8px; border:1px solid transparent;
cursor:pointer; transition:all 120ms var(--ease-standard); line-height:1;
text-decoration:none;
}
.btn.lg { font-size:15.5px; padding:14px 24px; }
.btn.primary { background:var(--navy-800); color:var(--cream-50); border-color:var(--navy-800); }
.btn.primary:hover { background:var(--navy-900); border-color:var(--navy-900); color:var(--cream-50); }
.btn.secondary { background:transparent; color:var(--navy-900); border-color:var(--border-2); }
.btn.secondary:hover { background:var(--cream-200); color:var(--navy-900); }
.btn.ghost { background:transparent; color:var(--navy-900); border:none; }
.btn.ghost:hover { background:rgba(14,31,51,0.06); }
.btn .arrow { transition:transform 200ms var(--ease-standard); }
.btn:hover .arrow { transform:translateX(2px); }
/* ---------- Hero ---------- */
section.hero { padding:92px 0 72px; }
.hero-grid { display:grid; grid-template-columns:1.15fr 1fr; gap:64px; align-items:center; }
.hero .eyebrow {
font-size:11.5px; font-weight:700; letter-spacing:0.18em;
text-transform:uppercase; color:var(--gold-700);
display:inline-flex; align-items:center; gap:10px; margin-bottom:22px;
}
.hero .eyebrow::before {
content:''; display:inline-block; width:28px; height:1px; background:var(--gold-500);
}
.hero h1 {
font-family:var(--font-display);
font-size:clamp(44px, 5.4vw, 72px);
font-weight:500; line-height:1.02;
letter-spacing:-0.022em; color:var(--navy-950);
margin:0 0 22px; text-wrap:balance;
}
.hero h1 .gold {
background-image:linear-gradient(to top, var(--gold-400) 0, var(--gold-400) 6px, transparent 6px);
background-repeat:no-repeat; background-position:0 95%; padding-bottom:2px;
}
.hero p.lede {
font-size:19px; line-height:1.55; color:var(--ink-700);
max-width:540px; margin:0 0 32px;
}
.hero .cta-row { display:flex; gap:12px; align-items:center; flex-wrap:wrap; }
.hero .trust {
margin-top:36px; display:flex; align-items:center; gap:18px;
font-size:13px; color:var(--ink-500); flex-wrap:wrap;
}
.hero .trust span { display:inline-flex; align-items:center; gap:7px; }
.hero .trust .dot { width:4px; height:4px; border-radius:50%; background:var(--gold-500); }
.hero .trust [data-lucide] { color:var(--navy-700); }
/* Hero certificate visual */
.cert {
background:var(--cream-50);
border:1px solid var(--border-1);
border-radius:14px;
box-shadow:0 0 0 1px var(--gold-500) inset, 0 8px 16px rgba(14,31,51,0.10), 0 24px 64px rgba(14,31,51,0.10);
padding:36px 36px 30px;
position:relative;
transform:rotate(-1.2deg);
max-width:460px;
margin-left:auto;
}
.cert::before, .cert::after {
content:''; position:absolute; left:16px; right:16px;
height:1px; background:var(--gold-500); opacity:0.5;
}
.cert::before { top:16px; } .cert::after { bottom:16px; }
.cert .seal {
position:absolute; right:-30px; top:-30px;
width:88px; height:88px; border-radius:50%;
background:var(--cream-50);
box-shadow:0 0 0 1px var(--gold-500) inset, 0 0 0 5px var(--cream-50), 0 0 0 6px var(--gold-500), var(--shadow-md);
display:flex; align-items:center; justify-content:center;
font-family:var(--font-display); font-weight:900; font-size:32px; color:var(--navy-800);
transform:rotate(8deg);
}
.cert .stamp {
font-size:9.5px; font-weight:700; letter-spacing:0.22em;
text-transform:uppercase; color:var(--gold-700);
text-align:center; margin-bottom:14px;
}
.cert h4 {
font-family:var(--font-display); font-weight:500; font-size:22px;
text-align:center; color:var(--navy-900); margin:0 0 4px; letter-spacing:-0.015em;
}
.cert .sub { text-align:center; font-size:12px; color:var(--ink-500); margin-bottom:22px; }
.cert .field { font-size:11px; font-weight:600; letter-spacing:0.12em; text-transform:uppercase; color:var(--ink-500); margin-bottom:4px; }
.cert .value { font-family:var(--font-mono); font-size:14px; color:var(--navy-900); margin-bottom:14px; }
.cert .row { display:grid; grid-template-columns:1fr 1fr; gap:18px; }
.cert .sig {
border-top:1px dashed rgba(14,31,51,0.2);
padding-top:12px; margin-top:6px;
font-family:var(--font-mono); font-size:10.5px; color:var(--ink-500);
line-height:1.5; word-break:break-all;
}
/* ---------- Sections ---------- */
section.block { padding:96px 0; }
section.tinted { background:var(--cream-200); position:relative; }
section.tinted::before, section.tinted::after {
content:''; position:absolute; left:0; right:0; height:1px; background:var(--gold-500); opacity:0.4;
}
section.tinted::before { top:0; } section.tinted::after { bottom:0; }
.section-head { max-width:760px; margin-bottom:56px; }
.section-head .eyebrow {
font-size:11.5px; font-weight:700; letter-spacing:0.18em;
text-transform:uppercase; color:var(--gold-700); margin-bottom:14px; display:block;
}
.section-head h2 {
font-family:var(--font-display);
font-size:clamp(32px, 3.6vw, 46px);
font-weight:500; line-height:1.05; letter-spacing:-0.022em;
color:var(--navy-950); margin:0 0 14px;
}
.section-head p { font-size:18px; line-height:1.55; color:var(--ink-700); margin:0; max-width:580px; }
/* ---------- Value grid ---------- */
.value-grid {
display:grid; grid-template-columns:repeat(3, 1fr); gap:1px;
background:var(--border-1); border:1px solid var(--border-1);
border-radius:14px; overflow:hidden;
}
.value-grid .item { background:var(--cream-50); padding:32px 28px; transition:background 150ms; }
.value-grid .item:hover { background:var(--cream-100); }
.value-grid .icon-wrap {
width:40px; height:40px; border-radius:8px;
background:var(--cream-200); border:1px solid var(--border-1);
display:flex; align-items:center; justify-content:center;
margin-bottom:18px; color:var(--navy-800);
}
.value-grid h3 {
font-family:var(--font-display); font-weight:700; font-size:18px;
color:var(--navy-950); margin:0 0 6px; letter-spacing:-0.01em;
}
.value-grid h3 + .accent-bar { width:22px; height:2px; background:var(--gold-500); margin-bottom:12px; }
.value-grid p { margin:0; font-size:14.5px; color:var(--ink-700); line-height:1.55; }
/* ---------- Flow ---------- */
.flow { display:grid; grid-template-columns:repeat(5, 1fr); gap:16px; list-style:none; padding:0; margin:0; }
.flow .step {
background:var(--cream-50); border:1px solid var(--border-1);
border-radius:12px; padding:28px 22px 24px;
position:relative; box-shadow:var(--shadow-sm);
}
.flow .num {
font-family:var(--font-display); font-weight:900; font-size:56px;
color:var(--gold-500); line-height:1; margin-bottom:14px;
letter-spacing:-0.04em; font-variant-numeric:lining-nums;
}
.flow .step h3 {
font-family:var(--font-display); font-weight:700; font-size:16px;
color:var(--navy-950); margin:0 0 6px; letter-spacing:-0.01em; line-height:1.25;
}
.flow .step p { font-size:13.5px; color:var(--ink-700); line-height:1.5; margin:0; }
.flow .step code { font-family:var(--font-mono); font-size:0.92em; padding:1px 5px; background:var(--cream-200); border-radius:4px; color:var(--navy-900); }
/* ---------- Code section ---------- */
.code-card {
background:var(--navy-950); border-radius:14px; overflow:hidden;
box-shadow:var(--shadow-lg); border:1px solid var(--navy-900);
}
.code-tabs {
display:flex; background:var(--navy-900);
border-bottom:1px solid rgba(245,241,232,0.08);
padding:0 8px;
}
.code-tabs button {
background:transparent; border:0;
color:rgba(245,241,232,0.55);
font-family:var(--font-body); font-weight:500; font-size:13px;
padding:14px 18px; cursor:pointer;
border-bottom:2px solid transparent;
transition:color 120ms;
}
.code-tabs button:hover { color:rgba(245,241,232,0.85); }
.code-tabs button.active { color:var(--cream-50); border-bottom-color:var(--gold-500); }
.code-tabs .install {
margin-left:auto; padding:14px 18px;
font-family:var(--font-mono); font-size:12px;
color:var(--gold-400);
}
pre.code {
margin:0; padding:24px 28px;
font-family:var(--font-mono); font-size:13.5px;
line-height:1.7; color:var(--cream-50); overflow-x:auto;
}
pre.code .c { color:rgba(245,241,232,0.45); }
pre.code .k { color:var(--gold-400); }
pre.code .s { color:#d4b985; }
pre.code .n { color:#a6b7cf; }
pre.code .f { color:var(--cream-50); }
pre.code .p { color:rgba(245,241,232,0.55); }
.code-section { display:grid; grid-template-columns:1fr 1fr; gap:56px; align-items:start; }
.code-section .pitch h3 { font-family:var(--font-display); font-weight:700; font-size:36px; line-height:1.1; letter-spacing:-0.022em; color:var(--navy-950); margin:14px 0 12px; }
.code-section .pitch p { font-size:16px; color:var(--ink-700); line-height:1.55; margin:0 0 16px; }
.code-section .pitch ul { list-style:none; padding:0; margin:24px 0 0; }
.code-section .pitch li { display:flex; align-items:start; gap:12px; padding:8px 0; font-size:14.5px; color:var(--ink-700); }
.code-section .pitch li::before { content:'\2713'; color:var(--gold-600); font-weight:700; flex-shrink:0; margin-top:1px; }
/* ---------- Sovereign ---------- */
.sov { display:grid; grid-template-columns:1fr 1fr; gap:24px; }
.sov .panel { background:var(--cream-50); border:1px solid var(--border-1); border-radius:14px; padding:32px 32px 28px; }
.sov .panel.dark { background:var(--navy-950); color:var(--cream-50); border:1px solid var(--navy-900); }
.sov .panel h3 { font-family:var(--font-display); font-weight:700; font-size:19px; margin:0 0 4px; letter-spacing:-0.015em; color:inherit; }
.sov .panel .sub { font-size:13px; color:var(--ink-500); margin-bottom:22px; }
.sov .panel.dark .sub { color:rgba(245,241,232,0.55); }
.sov ul { list-style:none; padding:0; margin:0; display:flex; flex-wrap:wrap; gap:8px; }
.sov li {
font-size:13px; padding:6px 13px; border-radius:999px;
background:var(--cream-100); border:1px solid var(--border-1); color:var(--ink-700);
}
.sov .panel.dark li {
background:var(--navy-900); border-color:rgba(245,241,232,0.15); color:rgba(245,241,232,0.9);
}
.sov .panel.dark li.no::before { content:'\2715 '; color:rgba(245,241,232,0.45); margin-right:2px; }
.sov .panel .footnote { font-size:13px; color:var(--ink-500); margin:18px 0 0; line-height:1.55; }
.sov .panel.dark .footnote { color:rgba(245,241,232,0.6); }
/* ---------- Agent-friendly section ---------- */
.agent-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:24px; }
@media (max-width: 880px) { .agent-grid { grid-template-columns:1fr; } }
/* ---------- Tier comparison ---------- */
.tier-grid { display:grid; grid-template-columns:repeat(3, 1fr); gap:20px; }
@media (max-width: 880px) { .tier-grid { grid-template-columns:1fr; } }
.tier-card {
background:var(--cream-50); border:1px solid var(--border-1);
border-radius:14px; padding:28px 26px;
display:flex; flex-direction:column; gap:6px;
position:relative;
}
.tier-card.featured {
box-shadow:0 0 0 2px var(--gold-500) inset, var(--shadow-sm);
}
.tier-cap {
display:inline-block; font-size:10.5px; font-weight:700;
letter-spacing:0.18em; text-transform:uppercase; color:var(--ink-500);
margin-bottom:6px;
}
.tier-cap.gold { color:var(--gold-700); }
.tier-price { display:flex; align-items:baseline; gap:8px; margin:0 0 12px; }
.tier-price .price-num {
font-family:var(--font-display); font-weight:700; font-size:28px;
color:var(--navy-950); letter-spacing:-0.02em; line-height:1.1;
}
.tier-price .price-sub { font-size:13.5px; color:var(--ink-500); }
.tier-pitch { font-size:14px; color:var(--ink-700); line-height:1.55; margin:0 0 16px; min-height:42px; }
.tier-card ul { list-style:none; padding:0; margin:0; display:flex; flex-direction:column; gap:7px; }
.tier-card li {
font-size:13.5px; color:var(--ink-700); padding-left:20px; position:relative; line-height:1.45;
}
.tier-card li::before {
content:'\2713'; position:absolute; left:0; top:0;
color:var(--gold-700); font-weight:700;
}
/* Launch-special discount treatment on the live tier cards. Matches
the buy page's diagonal ribbon + slashed-price visual so the
landing page and the buy page tell the same pricing story. */
.tier-card.has-launch {
position:relative;
clip-path:polygon(0 -20px, 100% -20px, 100% 100%, 0 100%);
}
.tier-launch-ribbon {
position:absolute; top:14px; right:-44px;
background:var(--gold-500); color:var(--navy-950);
font-family:var(--font-display); font-weight:700; font-size:10.5px;
letter-spacing:0.14em; text-transform:uppercase;
padding:4px 50px; transform:rotate(35deg);
box-shadow:0 2px 6px rgba(14,31,51,0.15);
z-index:2; pointer-events:none;
}
.tier-launch-meta {
font-size:11.5px; color:var(--gold-700); font-weight:600;
margin-bottom:6px;
}
.tier-price-original {
font-family:var(--font-display); font-weight:500; font-size:14px;
color:var(--ink-500); margin:0 0 4px;
text-decoration:line-through; text-decoration-color:rgba(14,31,51,0.4);
}
.tier-card .tier-features-list { margin-top:4px; }
/* ---------- Install ---------- */
.install-grid { display:grid; grid-template-columns:1fr 1fr; gap:24px; }
.install-card { background:var(--cream-50); border:1px solid var(--border-1); border-radius:14px; padding:28px; }
.install-card.featured { box-shadow:0 0 0 1px var(--gold-500) inset, var(--shadow-sm); }
.install-card .cap {
display:inline-block; font-size:10.5px; font-weight:700;
letter-spacing:0.18em; text-transform:uppercase; color:var(--gold-700);
margin-bottom:12px;
}
.install-card h3 { font-family:var(--font-display); font-weight:700; font-size:22px; color:var(--navy-950); margin:0 0 8px; letter-spacing:-0.015em; }
.install-card p { font-size:14.5px; color:var(--ink-700); margin:0 0 16px; line-height:1.55; }
.cmd-card {
background:var(--navy-950); color:var(--cream-50);
border-radius:10px; padding:14px 16px;
font-family:var(--font-mono); font-size:13px;
display:flex; align-items:center; justify-content:space-between; gap:12px;
}
.cmd-card .copy {
background:rgba(245,241,232,0.10); color:var(--cream-50);
border:0; padding:6px 10px; border-radius:6px;
font-family:var(--font-body); font-size:11.5px; cursor:pointer;
transition:background 120ms;
}
.cmd-card .copy:hover { background:rgba(245,241,232,0.20); }
.install-card ol { padding-left:20px; margin:8px 0 0; color:var(--ink-700); font-size:14.5px; line-height:1.7; }
.install-card ol code { font-family:var(--font-mono); font-size:0.9em; padding:2px 5px; background:var(--cream-200); border-radius:4px; }
/* ---------- FAQ ---------- */
.faq { display:grid; grid-template-columns:1fr 1fr; gap:24px 56px; }
.faq h3 { font-family:var(--font-display); font-weight:700; font-size:17px; color:var(--navy-950); margin:0 0 6px; letter-spacing:-0.01em; }
.faq p { font-size:14.5px; color:var(--ink-700); line-height:1.55; margin:0 0 6px; }
.faq code { font-family:var(--font-mono); font-size:0.9em; padding:1px 5px; background:var(--cream-200); border-radius:4px; color:var(--navy-900); }
/* ---------- Footer ---------- */
footer.site {
background:var(--navy-950); color:var(--cream-300);
padding:56px 0 36px; border-top:1px solid var(--gold-500);
}
footer.site .top { display:flex; justify-content:space-between; flex-wrap:wrap; gap:36px; margin-bottom:36px; }
footer.site .col h5 {
font-family:var(--font-body); font-size:11.5px; font-weight:700;
letter-spacing:0.18em; text-transform:uppercase; color:var(--gold-400);
margin:0 0 14px;
}
footer.site .col a { display:block; color:rgba(245,241,232,0.7); padding:4px 0; font-size:14px; }
footer.site .col a:hover { color:var(--cream-50); }
footer.site .brand-block { max-width:320px; }
footer.site .brand {
display:flex; align-items:center; gap:10px;
font-family:var(--font-display); font-weight:500; font-size:15px;
letter-spacing:0.30em; text-transform:uppercase;
color:var(--cream-50); margin-bottom:14px;
}
footer.site .brand img { width:32px; height:32px; }
footer.site .tag { font-size:13.5px; line-height:1.55; color:rgba(245,241,232,0.65); margin:0; }
footer.site .bottom {
border-top:1px solid rgba(245,241,232,0.10);
padding-top:24px;
display:flex; justify-content:space-between; flex-wrap:wrap; gap:12px;
font-size:12.5px; color:rgba(245,241,232,0.45);
}
footer.site .bottom a { color:rgba(245,241,232,0.6); }
/* ---------- Creators slider (full-width, auto-advance) ---------- */
.case-section { padding-bottom: 96px; }
.case-slider {
position:relative; width:100%; overflow:hidden;
margin-top:24px;
background: var(--cream-50);
border-top: 1px solid var(--border-1);
border-bottom: 1px solid var(--border-1);
}
.case-track {
display:flex;
width:100%;
transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
will-change: transform;
}
.case-slide {
flex: 0 0 100%; min-width: 100%;
display:grid; grid-template-columns: 1.1fr 1fr;
align-items:stretch;
min-height: 460px;
}
.case-slide .photo {
position:relative; overflow:hidden;
background: var(--cream-200);
border-right: 1px solid var(--border-1);
}
.case-slide .photo img {
width:100%; height:100%; object-fit:cover; object-position:center;
display:block;
}
.case-slide .photo .photo-fallback {
position:absolute; inset:0;
display:flex; align-items:center; justify-content:center;
flex-direction:column; gap:14px;
color: var(--ink-500);
background: linear-gradient(135deg, var(--cream-200) 0%, var(--cream-300) 100%);
text-align:center; padding: 24px;
}
.case-slide .photo .photo-fallback svg { width: 96px; height: 96px; opacity: 0.5; }
.case-slide .photo .photo-fallback .label {
font-size: 11.5px; font-weight: 700; letter-spacing: 0.16em;
text-transform: uppercase; color: var(--gold-700);
}
.case-slide .photo .photo-fallback .filename {
font-family: var(--font-mono); font-size: 11px;
color: var(--ink-500); padding: 4px 8px;
background: var(--cream-50); border: 1px solid var(--border-1);
border-radius: 5px;
}
.case-slide .body {
padding: 56px 56px 48px;
display:flex; flex-direction:column; justify-content:center; gap:18px;
background: var(--cream-50);
}
.case-slide .eyebrow-tag {
font-size:11.5px; font-weight:700; letter-spacing:0.18em;
text-transform:uppercase; color:var(--gold-700);
display:inline-flex; align-items:center; gap:10px;
}
.case-slide .eyebrow-tag::before {
content:''; display:inline-block; width:28px; height:1px; background:var(--gold-500);
}
.case-slide .name {
font-family: var(--font-display); font-weight:700; font-size: 28px;
color: var(--navy-950); letter-spacing:-0.02em; line-height:1.1;
margin: 0;
}
.case-slide .role {
font-size: 15px; color: var(--ink-500); line-height:1.5;
margin: 0;
}
.case-slide .product-block {
background: var(--cream-100);
border: 1px solid var(--border-1);
border-radius: 10px;
padding: 16px 18px;
display:flex; align-items:flex-start; gap:14px;
}
.case-slide .product-block .product-icon {
width: 22px; height: 22px; flex-shrink:0; margin-top: 2px; color: var(--gold-700);
}
.case-slide .product-block .product-name {
font-family: var(--font-display); font-weight:700; font-size: 16px;
color: var(--navy-950); letter-spacing:-0.01em; margin: 0 0 4px;
}
.case-slide .product-block .product-tag {
font-size: 13.5px; color: var(--ink-700); margin: 0; line-height: 1.5;
}
.case-slide blockquote {
margin: 0; padding: 0 0 0 18px;
font-family: var(--font-body); font-style: italic;
font-size: 17px; line-height: 1.55; color: var(--ink-700);
border-left: 3px solid var(--gold-500);
}
.case-slide .stats {
display:flex; gap: 32px;
padding-top: 18px; border-top: 1px dashed rgba(14,31,51,0.12);
font-size: 12px; color: var(--ink-500); letter-spacing:0.04em;
}
.case-slide .stats .stat-num {
font-family: var(--font-display); font-weight:700; font-size: 22px;
color: var(--navy-900); letter-spacing:-0.015em; display:block; margin-bottom: 2px;
}
/* Slider controls */
.case-controls {
display:flex; justify-content:center; align-items:center; gap: 8px;
padding: 24px 0 0;
}
.case-dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--border-2); border: 0; padding: 0;
cursor: pointer; transition: all 200ms;
}
.case-dot.active {
background: var(--navy-800);
width: 28px; border-radius: 4px;
}
.case-dot:hover { background: var(--navy-700); }
.case-section .footnote {
text-align:center; font-size:12px; color: var(--ink-500);
margin-top: 18px; letter-spacing:0.04em;
}
/* ---------- Responsive ---------- */
@media (max-width: 980px) {
.hero-grid { grid-template-columns:1fr; gap:48px; }
.cert { transform:none; max-width:100%; margin:0 auto; }
.value-grid { grid-template-columns:repeat(2, 1fr); }
.flow { grid-template-columns:repeat(2, 1fr); }
.code-section, .sov, .install-grid, .faq { grid-template-columns:1fr; }
header.site nav { display:none; }
section.block { padding:64px 0; }
/* Case slider: stack photo above content on narrow screens */
.case-slide { grid-template-columns: 1fr; min-height: auto; }
.case-slide .photo { min-height: 280px; border-right: 0; border-bottom: 1px solid var(--border-1); }
.case-slide .body { padding: 32px 24px 28px; }
.case-slide .name { font-size: 22px; }
.case-slide blockquote { font-size: 15px; }
}
</style>
</head>
<body>
<header class="site">
<div class="wrap inner">
<a class="brand" href="#top">
<img src="assets/keysat-mark.svg" alt="">
<span>Keysat</span>
</a>
<nav>
<a href="#why">Why</a>
<a href="#creators">Creators</a>
<a href="#how">How it works</a>
<a href="#integrate">Integrate</a>
<a href="#agents">Agents</a>
<a href="#tiers">Pricing</a>
<a href="#install">Install</a>
<a href="https://docs.keysat.xyz">Docs</a>
</nav>
<a href="https://licensing.keysat.xyz/buy/keysat" class="btn primary cta">Buy License</a>
</div>
</header>
<section class="hero" id="top">
<div class="wrap hero-grid">
<div>
<div class="eyebrow">Software licensing for independent creators</div>
<h1>Self-hosted licensing for software creators.</h1>
<p class="lede">
Keysat is a self-hosted licensing server. Buyers pay you for the software you create, and their license can be verified offline. Your licensing key, customer list, and payment rails all live on your hardware &mdash; no SaaS, no middleman, no platform risk.
</p>
<div class="cta-row">
<a class="btn primary lg" href="#install">Install Keysat <span class="arrow">&rarr;</span></a>
<a class="btn secondary lg" href="#how">See how it works</a>
</div>
<div class="trust">
<span>
<i data-lucide="server" style="width:14px;height:14px"></i>
Runs on <a href="https://start9.com" target="_blank" rel="noopener" style="color:inherit; text-decoration:underline; text-underline-offset:2px;">Start9</a>
</span>
<span class="dot"></span>
<span><i data-lucide="bitcoin" style="width:14px;height:14px"></i> Pays via BTCPay</span>
<span class="dot"></span>
<span><i data-lucide="wifi-off" style="width:14px;height:14px"></i> Verifies offline</span>
</div>
</div>
<div>
<div class="cert" role="img" aria-label="Sample license certificate">
<div class="seal">&#8383;</div>
<div class="stamp">&mdash; Certificate of License &mdash;</div>
<h4>Sample Product</h4>
<div class="sub">Issued under default policy &middot; single seat &middot; 1 year</div>
<div class="field">License key</div>
<div class="value">KS-9F2A-7C41-XK22-6D8E</div>
<div class="row">
<div>
<div class="field">Issued</div>
<div class="value" style="font-size:13px">2026-04-22</div>
</div>
<div>
<div class="field">Expires</div>
<div class="value" style="font-size:13px">2027-04-22</div>
</div>
</div>
<div class="sig">Ed25519 &middot; mz7q8r4t1v&hellip;h3k2pXq9wL &middot; &check; verified offline</div>
</div>
</div>
</div>
</section>
<section class="block tinted" id="why">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">What this enables</span>
<h2>A complete sell-your-software stack, sovereign end-to-end.</h2>
<p>Keysat is the licensing layer. Your hardware holds the keys and controls payment rails. No third party can mint, revoke, or read your sales records.</p>
</div>
<div class="value-grid">
<div class="item">
<div class="icon-wrap"><i data-lucide="key-round"></i></div>
<h3>You own the signing key</h3><div class="accent-bar"></div>
<p>The Ed25519 keypair lives on your hardware. Every license is signed by it. There's no third party who could mint or revoke licenses.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="wifi-off"></i></div>
<h3>Offline verification</h3><div class="accent-bar"></div>
<p>Your software verifies licenses against an embedded public key. No network call. Customer apps work even if your Keysat goes offline.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="ticket"></i></div>
<h3>Sell however you want</h3><div class="accent-bar"></div>
<p>One-time purchases for the whole app. Free / paid splits. Trials. Recurring renewals. Time-limited licenses, multi-seat licenses, comp keys for press. The licensing layer is a primitive — you decide the business model.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="zap"></i></div>
<h3>Payment channels you control</h3><div class="accent-bar"></div>
<p>BTCPay Server on your own server manages bitcoin and lightning payments. Funds land in your wallet without intermediaries.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="tag"></i></div>
<h3>Discount &amp; comp codes</h3><div class="accent-bar"></div>
<p>Percent-off, fixed-sats-off, or free-license codes (no payment required). Run launch promos, comp keys for press, track partner campaigns.</p>
</div>
<div class="item">
<div class="icon-wrap"><i data-lucide="wrench"></i></div>
<h3>SDKs in your language</h3><div class="accent-bar"></div>
<p>Rust, TypeScript, Python, Go &mdash; wire-compatible offline verifiers. Five lines of code in your app and you're verifying real signatures.</p>
</div>
</div>
</div>
</section>
<section class="block case-section" id="creators">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Real creators can now build real software</span>
<h2>Empowering the next generation of software creators.</h2>
<p>AI software tools now allow specialists to finally ship the tools they&rsquo;ve always wanted for themselves, without needing any coding background. Keysat is the licensing layer that lets them get paid for what they made.</p>
</div>
</div>
<div class="case-slider" id="case-slider" aria-label="Independent creators using Keysat">
<div class="case-track" id="case-track">
<!-- ===== Slide 1: Tax accountant ===== -->
<article class="case-slide" data-slide="0">
<div class="photo">
<img src="assets/case-marisol.jpg" alt="Marisol Vargas, solo CPA" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="photo-fallback" style="display:none">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 22a8 8 0 0116 0"/></svg>
<div class="label">Photo placeholder</div>
<div>Save a photoreal portrait at:</div>
<div class="filename">assets/case-marisol.jpg</div>
</div>
</div>
<div class="body">
<div class="eyebrow-tag">Solo CPA</div>
<h3 class="name">Marisol Vargas</h3>
<p class="role">12 years specializing in small-business tax. Built her own software because the existing tools didn&rsquo;t fit her workflow.</p>
<div class="product-block">
<svg class="product-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="12" y2="14"/></svg>
<div>
<div class="product-name">SchedulCalc</div>
<p class="product-tag">Schedule C + 1099 reconciler for solo accountants. Replaces a $1,200/yr software stack with one focused tool.</p>
</div>
</div>
<blockquote>&ldquo;I worked with bloated tax software for twelve years. With AI I built one that fits how I actually think &mdash; in three months, on weekends. Now Keysat lets me sell it to the other solo CPAs who were stuck with the same bad tools.&rdquo;</blockquote>
</div>
</article>
<!-- ===== Slide 2: Powerlifting coach ===== -->
<article class="case-slide" data-slide="1">
<div class="photo">
<img src="assets/case-tomas.jpg" alt="Tomas Kovac, powerlifting coach" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="photo-fallback" style="display:none">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 22a8 8 0 0116 0"/></svg>
<div class="label">Photo placeholder</div>
<div>Save a photoreal portrait at:</div>
<div class="filename">assets/case-tomas.jpg</div>
</div>
</div>
<div class="body">
<div class="eyebrow-tag">Powerlifting coach</div>
<h3 class="name">Tomas Kovac</h3>
<p class="role">10 years as a fitness coach. Built RPE-based programming software because off-the-shelf apps were gym-membership specific and lacking in functionality.</p>
<div class="product-block">
<svg class="product-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="6" y1="6" x2="6" y2="18"/><line x1="18" y1="6" x2="18" y2="18"/><line x1="6" y1="12" x2="18" y2="12"/><circle cx="6" cy="9" r="1.5"/><circle cx="6" cy="15" r="1.5"/><circle cx="18" cy="9" r="1.5"/><circle cx="18" cy="15" r="1.5"/></svg>
<div>
<div class="product-name">BarbellTracker</div>
<p class="product-tag">RPE-based programming and autoregulation for one-on-one coaches. Built around how strength athletes actually train.</p>
</div>
</div>
<blockquote>&ldquo;I knew exactly what coaches needed because I was the coach. AI handled the engineering &mdash; I handled the design. Now Keysat lets me sell it to the other coaches who recognize how it should actually work.&rdquo;</blockquote>
</div>
</article>
<!-- ===== Slide 3: Homeschooling parent ===== -->
<article class="case-slide" data-slide="2">
<div class="photo">
<img src="assets/case-hana.jpg" alt="Hana Brennan, homeschooling parent" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="photo-fallback" style="display:none">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="8" r="4"/><path d="M4 22a8 8 0 0116 0"/></svg>
<div class="label">Photo placeholder</div>
<div>Save a photoreal portrait at:</div>
<div class="filename">assets/case-hana.jpg</div>
</div>
</div>
<div class="body">
<div class="eyebrow-tag">Homeschooling mom</div>
<h3 class="name">Hana Brennan</h3>
<p class="role">Three kids, three different curriculum tracks. Built her own week-planner because every existing tool assumed one student.</p>
<div class="product-block">
<svg class="product-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2 4h7a3 3 0 013 3v13a2 2 0 00-2-2H2zM22 4h-7a3 3 0 00-3 3v13a2 2 0 012-2h8z"/></svg>
<div>
<div class="product-name">SeasonsOfLearning</div>
<p class="product-tag">Multi-grade week planner for homeschool families with kids on different tracks. Mixes curricula across ages.</p>
</div>
</div>
<blockquote>&ldquo;I sketched the workflow on a notepad over a weekend. Two weeks later my kids&rsquo; school days were running on it. Now Keysat lets me sell it to the other homeschool families who were trying to bend single-student tools to fit their week.&rdquo;</blockquote>
</div>
</article>
</div>
</div>
<div class="wrap">
<div class="case-controls" id="case-controls" role="tablist" aria-label="Case study navigation">
<button class="case-dot active" data-target="0" aria-label="Show case 1"></button>
<button class="case-dot" data-target="1" aria-label="Show case 2"></button>
<button class="case-dot" data-target="2" aria-label="Show case 3"></button>
</div>
<div class="footnote">Illustrative examples of the kinds of creators Keysat is built for. Real Keysat creators &mdash; come be one of them.</div>
</div>
</section>
<section class="block" id="how">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">How it works</span>
<h2>Five steps, end to end.</h2>
<p>From install to first sale in an afternoon. No cloud account to create, no API keys to copy.</p>
</div>
<ol class="flow">
<li class="step"><div class="num">01</div><h3>Install on your Start9</h3><p>One click from <code>registry.keysat.xyz</code> in the StartOS marketplace. (Sideload the <code>.s9pk</code> directly if you prefer.)</p></li>
<li class="step"><div class="num">02</div><h3>Connect BTCPay</h3><p>One click in the StartOS Actions tab. Authorize once on BTCPay's consent page; Keysat registers a webhook automatically.</p></li>
<li class="step"><div class="num">03</div><h3>Define products + policies</h3><p>Declare a product, set its price in sats, define a policy (duration, seat cap, trial, entitlements).</p></li>
<li class="step"><div class="num">04</div><h3>Embed your public key</h3><p>Copy your Keysat public key into your app. Add the SDK. Five lines of code verifies a signature at startup.</p></li>
<li class="step"><div class="num">05</div><h3>Share your purchase URL</h3><p>Buyers hit your public URL, pay for your software, and get a signed license that can be verified offline.</p></li>
</ol>
</div>
</section>
<section class="block tinted" id="integrate">
<div class="wrap">
<div class="code-section">
<div class="pitch">
<span class="eyebrow" style="color:var(--gold-700); font-size:11.5px; font-weight:700; letter-spacing:0.18em; text-transform:uppercase;">For developers and AI agents</span>
<h3>Five lines, in the language you or your AI agents already write.</h3>
<p>Keysat licenses are Ed25519-signed and Crockford base32-encoded. Verification is pure-function &mdash; no network, no daemon, no shared state. Hand the docs to your coding agent and tell it to wire licensing into your software; the integration is small enough to fit in one prompt.</p>
<ul>
<li>Wire-compatible across SDKs (TypeScript, Rust, Python, Go)</li>
<li>Public key embedded at compile time</li>
<li>Returns product, policy, expiry, entitlements</li>
<li>Source-available &mdash; agents can read the verifier source directly</li>
</ul>
</div>
<div class="code-card">
<div class="code-tabs">
<button class="active" data-lang="ts">TypeScript</button>
<button data-lang="rs">Rust</button>
<button data-lang="py">Python</button>
<button data-lang="go">Go</button>
<span class="install" id="install-cmd">npm install @keysat/licensing-client</span>
</div>
<pre class="code" id="code-ts"><span class="k">import</span> { <span class="f">Verifier</span>, <span class="f">PublicKey</span> } <span class="k">from</span> <span class="s">'@keysat/licensing-client'</span>
<span class="k">const</span> verifier = <span class="k">new</span> <span class="f">Verifier</span>(
<span class="f">PublicKey</span>.<span class="f">fromPem</span>(ISSUER_PEM)
)
<span class="k">const</span> ok = verifier.<span class="f">verify</span>(licenseKeyFromUser)
console.<span class="f">log</span>(<span class="s">'licensed:'</span>, ok.productId, ok.expires)</pre>
<pre class="code" id="code-rs" style="display:none"><span class="c">// Cargo.toml</span>
<span class="c">// licensing-client = "0.1"</span>
<span class="k">use</span> licensing_client::{<span class="f">Verifier</span>, <span class="f">PublicKeyPem</span>};
<span class="k">let</span> pk = <span class="f">PublicKeyPem</span>::from_str(ISSUER_PEM)<span class="p">?</span>;
<span class="k">let</span> verifier = <span class="f">Verifier</span>::new(pk);
<span class="k">let</span> ok = verifier.verify(&amp;license_key)<span class="p">?</span>;
println!(<span class="s">"licensed: {}"</span>, ok.product_id);</pre>
<pre class="code" id="code-py" style="display:none"><span class="k">from</span> keysat_licensing_client <span class="k">import</span> Verifier, PublicKey
verifier = <span class="f">Verifier</span>(<span class="f">PublicKey</span>.<span class="f">from_pem</span>(ISSUER_PEM))
ok = verifier.<span class="f">verify</span>(license_key_from_user)
<span class="k">print</span>(<span class="s">"licensed for"</span>, ok.product_id)</pre>
<pre class="code" id="code-go" style="display:none"><span class="k">import</span> keysat <span class="s">"github.com/keysat-xyz/licensing-client-go"</span>
payload, err := keysat.<span class="f">ParseAndVerify</span>(licenseKey, ISSUER_PEM)
<span class="k">if</span> err != <span class="k">nil</span> { <span class="f">log</span>.<span class="f">Fatal</span>(err) }
fmt.<span class="f">Printf</span>(<span class="s">"licensed for %s, expires %s\n"</span>,
payload.ProductSlug, payload.ExpiresAt)</pre>
</div>
</div>
</div>
</section>
<section class="block tinted" id="agents">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Agent-friendly</span>
<h2>Built so you can hand operations to an AI agent.</h2>
<p>Keysat issues, monetizes, and manages licenses through a clean self-service admin UI. It also exposes every operator action through a documented HTTP API with bounded-scope credentials &mdash; so if you'd rather have an agent do the day-to-day work, you can give it the keys safely.</p>
</div>
<div class="agent-grid">
<div class="install-card">
<div class="cap">OpenAPI 3.1</div>
<h3>Discovery built in</h3>
<p><code>GET /v1/openapi.json</code> returns a curated, stable spec. Drop the URL into Claude (Code or Computer Use), an OpenAI Custom GPT, or Perplexity&rsquo;s Comet agent browser &mdash; the agent learns the endpoints automatically.</p>
</div>
<div class="install-card">
<div class="cap">Scoped API keys</div>
<h3>Least-privilege by design</h3>
<p>Generate a key, pick a role (read-only, license-issuer, support, or full-admin), hand it to the agent. Revoke in 30 seconds without rotating your master credential. Operator-only actions stay behind the master key.</p>
</div>
<div class="install-card">
<div class="cap">Stable error codes + webhooks</div>
<h3>React, don't poll</h3>
<p>HMAC-signed event deliveries on every license / invoice / subscription state change. Every error response carries a stable machine-readable code (<code>tier_cap</code>, <code>not_found</code>, <code>license_revoked</code>) so agents branch deterministically.</p>
</div>
</div>
<div style="margin-top:32px; text-align:center">
<a class="btn ghost" href="https://docs.keysat.xyz/agent.html">Agent integration guide &rarr;</a>
</div>
</div>
</section>
<section class="block" id="tiers">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Pricing</span>
<h2>Three tiers. Free forever for solo creators.</h2>
<p>Every Keysat instance ships at a highly functional Creator tier; pay only when you outgrow it.</p>
</div>
<!--
Tier cards. Static markup is the FALLBACK shape (rendered on
every page load, kept conservative so it stays correct even when
offline). On load, `loadLiveTiers()` below fetches the master
Keysat's product + policies and re-renders this grid with the
live prices, marketing bullets, hidden-entitlement filtering, and
any active launch-special featured discount applied — matching
what the buy page renders.
-->
<div class="tier-grid" id="tier-grid-live">
<div class="tier-card">
<div class="tier-cap">Creator</div>
<div class="tier-price"><span class="price-num">Free</span><span class="price-sub">forever</span></div>
<p class="tier-pitch">Solo creator selling one-time or perpetual licenses for a focused catalog.</p>
<ul>
<li>Up to 5 products</li>
<li>Up to 5 tiers per product</li>
<li>Up to 10 active discount codes</li>
<li>BTCPay (Bitcoin / Lightning) payments</li>
<li>All 4 SDKs &middot; full wire format</li>
<li>Webhooks, audit log, recovery, analytics opt-in</li>
</ul>
</div>
<div class="tier-card featured">
<div class="tier-cap gold">Pro</div>
<div class="tier-price"><span class="price-num">250k sats</span><span class="price-sub">/ year</span></div>
<p class="tier-pitch">For creators monetizing seriously &mdash; multiple products, subscriptions, both Bitcoin and card buyers.</p>
<ul>
<li>Unlimited products, tiers, and codes</li>
<li>Recurring subscriptions &middot; trials, grace, auto-renew</li>
<li>Zaprite payment gateway &mdash; cards, Apple Pay, bank transfers, plus Bitcoin</li>
<li>In-place tier upgrades (proration handled)</li>
<li>Everything in Creator</li>
</ul>
</div>
<div class="tier-card">
<div class="tier-cap">Patron</div>
<div class="tier-price"><span class="price-num">500k sats</span><span class="price-sub">/ year</span></div>
<p class="tier-pitch">Perpetual license + direct one-on-one support, for creators who want Keysat to keep getting better.</p>
<ul>
<li>Everything in Pro</li>
<li>Perpetual license (one-time, never renews)</li>
<li>Direct one-on-one support</li>
<li>&ldquo;Patron&rdquo; badge in your admin dashboard</li>
<li>Listed on the Patrons page on keysat.xyz</li>
<li>Early access to release-candidate builds</li>
</ul>
</div>
</div>
</div>
</section>
<section class="block tinted" id="install">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Install</span>
<h2>From the marketplace, or sideload directly.</h2>
<p>Either path leaves you in the same place: a running Keysat on your Start9, ready to connect BTCPay.</p>
</div>
<div class="install-grid">
<div class="install-card featured">
<span class="cap">Recommended</span>
<h3>From the marketplace</h3>
<p>Add the Keysat marketplace to your Start9, then click Install.</p>
<div class="cmd-card">
<span>https://registry.keysat.xyz</span>
<button class="copy" data-copy="https://registry.keysat.xyz">Copy</button>
</div>
<p style="margin-top:16px; font-size:13.5px; color:var(--ink-500)">StartOS dashboard &rarr; Marketplace &rarr; Add &rarr; paste the URL.</p>
<p style="margin-top:14px; font-size:13.5px; color:var(--ink-700)"><strong>Then activate your license:</strong></p>
<a href="https://licensing.keysat.xyz/buy/keysat" class="btn primary" style="display:inline-flex; margin-top:8px; width:100%; justify-content:center;">Buy a license &rarr;</a>
</div>
<div class="install-card">
<span class="cap">Alternative</span>
<h3>Sideload</h3>
<p>If you'd rather not add the marketplace:</p>
<ol>
<li>Download <code>keysat_x86_64.s9pk</code> from <a href="https://github.com/keysat-xyz/keysat/releases/latest">GitHub releases</a>.</li>
<li>StartOS dashboard &rarr; Sideload &rarr; drag the file in.</li>
<li>Click Install.</li>
</ol>
</div>
</div>
</div>
</section>
<section class="block" id="faq">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Common questions</span>
<h2>What people ask.</h2>
</div>
<div class="faq">
<div>
<h3>What happens if Keysat the project disappears?</h3>
<p>Your software keeps working. The public key is embedded in your app at compile time, and offline verification doesn't depend on us. The wire format is documented; you can reimplement the verifier in any language in an afternoon.</p>
</div>
<div>
<h3>Can I do free or comped licenses?</h3>
<p>Yes. Define a discount code with type <code>free_license</code> and the buyer redeems it at checkout without paying. Useful for press, beta testers, partners, and giveaways.</p>
</div>
<div>
<h3>What&rsquo;s the difference between the three tiers?</h3>
<p>Creator is free forever, capped at 5 products / 5 policies per product / 10 active discount codes &mdash; plenty for a solo creator selling one-time or perpetual licenses. Pro lifts every cap and unlocks recurring subscriptions plus the Zaprite payment gateway (cards, Apple Pay, bank transfers, in addition to Bitcoin), billed annually. Patron is everything in Pro, sold as a one-time perpetual license instead of an annual subscription, plus direct one-on-one support and a public supporter badge &mdash; you&rsquo;re funding development. See the tier cards above for the full breakdown.</p>
</div>
<div>
<h3>Do I have to use Bitcoin?</h3>
<p>BTCPay (Bitcoin / Lightning) is the default and available on every tier. On Pro or Patron you can additionally connect Zaprite, which adds card / Apple Pay / bank transfer / more &mdash; with payments still landing in your wallet. Pick whichever your buyers prefer; you can switch the active provider at any time.</p>
</div>
</div>
</div>
</section>
<footer class="site">
<div class="wrap">
<div class="top">
<div class="brand-block">
<div class="brand"><img src="assets/keysat-mark.svg" alt=""><span>Keysat</span></div>
<p class="tag">Software licensing for independent creators. Self-hosted, sovereign, source-available.</p>
</div>
<div class="col">
<h5>Product</h5>
<a href="#why">Why Keysat</a>
<a href="#how">How it works</a>
<a href="#install">Install</a>
<a href="https://registry.keysat.xyz">Marketplace</a>
</div>
<div class="col">
<h5>Developers</h5>
<a href="#integrate">Integration</a>
<a href="https://docs.keysat.xyz">SDKs</a>
<a href="https://docs.keysat.xyz/wire-format">Wire format</a>
<a href="https://github.com/keysat-xyz">GitHub</a>
</div>
<div class="col">
<h5>Contact</h5>
<a href="mailto:licensing@keysat.xyz">licensing@keysat.xyz</a>
<a href="https://docs.keysat.xyz/changelog">Changelog</a>
<a href="https://github.com/keysat-xyz/keysat/issues">Issues</a>
</div>
</div>
<div class="bottom">
<span>&copy; Keysat. Source-available; not open-source.</span>
<span>Runs on Start9 &middot; Pays via BTCPay &middot; Verifies offline</span>
</div>
</div>
</footer>
<script src="https://unpkg.com/lucide@latest"></script>
<script>
lucide.createIcons();
// Tab switching for code samples
const installCmds = {
ts: 'npm install @keysat/licensing-client',
rs: 'cargo add licensing-client',
py: 'pip install keysat-licensing-client',
go: 'go get github.com/keysat-xyz/licensing-client-go',
};
document.querySelectorAll('.code-tabs button').forEach(btn => {
btn.addEventListener('click', () => {
const lang = btn.dataset.lang;
document.querySelectorAll('.code-tabs button').forEach(b => b.classList.toggle('active', b === btn));
['ts','rs','py','go'].forEach(l => {
document.getElementById('code-' + l).style.display = l === lang ? 'block' : 'none';
});
document.getElementById('install-cmd').textContent = installCmds[lang];
});
});
// Copy buttons
document.querySelectorAll('.copy[data-copy]').forEach(btn => {
btn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(btn.dataset.copy);
const original = btn.textContent;
btn.textContent = 'Copied';
setTimeout(() => { btn.textContent = original; }, 1400);
} catch (e) {}
});
});
// Case-study slider — auto-advance with pause on hover, dot indicators
(function() {
const track = document.getElementById('case-track');
const slider = document.getElementById('case-slider');
const dots = document.querySelectorAll('.case-dot');
if (!track || !slider) return;
const slides = track.querySelectorAll('.case-slide');
const SLIDE_COUNT = slides.length;
const ADVANCE_MS = 9000; // ~9s per slide — long enough to read
let current = 0;
let timer = null;
function go(i) {
current = ((i % SLIDE_COUNT) + SLIDE_COUNT) % SLIDE_COUNT;
track.style.transform = 'translateX(-' + (current * 100) + '%)';
dots.forEach((d, di) => d.classList.toggle('active', di === current));
}
function next() { go(current + 1); }
function start() {
if (timer) clearInterval(timer);
timer = setInterval(next, ADVANCE_MS);
}
function stop() {
if (timer) clearInterval(timer);
timer = null;
}
// Pause on hover; resume on leave.
slider.addEventListener('mouseenter', stop);
slider.addEventListener('mouseleave', start);
// Also pause when the tab isn't visible — saves CPU.
document.addEventListener('visibilitychange', () => {
if (document.hidden) stop(); else start();
});
// Manual nav via dots: jump and reset the timer.
dots.forEach(dot => {
dot.addEventListener('click', () => {
const t = parseInt(dot.dataset.target, 10) || 0;
go(t);
start();
});
});
start();
})();
</script>
<!--
Live tier-card render. Fetches the master Keysat's product + policies
on page load and re-renders the #tier-grid-live grid with whatever
the operator configured — prices, marketing bullets, hidden
entitlements, and any active featured (launch-special) discount.
Mirrors the buy page's tier-picker treatment so the landing page
and /buy/keysat tell exactly the same pricing story.
Failure mode: if the fetch errors (offline visitor, daemon down,
CORS hiccup), the static fallback grid renders as it does today.
This script only mutates the DOM on success.
-->
<script>
(function () {
const KEYSAT_API = 'https://licensing.keysat.xyz';
const PRODUCT_SLUG = 'keysat';
const fmtSats = (n) => {
if (n === 0) return 'Free';
if (n >= 1_000_000 && n % 100_000 === 0) return (n / 1_000_000) + 'M sats';
if (n >= 1_000 && n % 1_000 === 0) return (n / 1_000) + 'k sats';
return Number(n).toLocaleString('en-US') + ' sats';
};
const cadenceSuffix = (p) => {
if (!p.is_recurring) return '';
const d = p.renewal_period_days || 0;
if (d === 7) return '/ wk';
if (d === 30) return '/ mo';
if (d === 90) return '/ qtr';
if (d === 180) return '/ 6mo';
if (d === 365) return '/ year';
if (d > 0) return '/ ' + d + 'd';
return '';
};
const discountLabel = (fd) => {
if (!fd) return '';
if (fd.kind === 'percent') return Math.round(fd.amount / 100) + '% OFF';
if (fd.kind === 'free_license') return 'FREE';
if (fd.kind === 'set_price') return 'LIMITED PRICE';
return 'LAUNCH SPECIAL';
};
function renderCard(pol, catalog) {
const card = document.createElement('div');
card.className = 'tier-card' + (pol.highlighted ? ' featured' : '');
const fd = pol.featured_discount;
if (fd) card.classList.add('has-launch');
const cap = document.createElement('div');
cap.className = 'tier-cap' + (pol.highlighted ? ' gold' : '');
cap.textContent = pol.name;
card.appendChild(cap);
if (fd) {
const ribbon = document.createElement('div');
ribbon.className = 'tier-launch-ribbon';
ribbon.textContent = discountLabel(fd);
card.appendChild(ribbon);
const remaining = fd.remaining_uses;
if (typeof remaining === 'number' && remaining > 0) {
const meta = document.createElement('div');
meta.className = 'tier-launch-meta';
meta.textContent = 'Limited: ' + remaining + ' remaining';
card.appendChild(meta);
}
const orig = document.createElement('div');
orig.className = 'tier-price-original';
orig.textContent = fmtSats(pol.price_sats);
card.appendChild(orig);
}
// Headline price. Free policies just say "Free"; paid policies
// show the discounted price when a featured discount applies.
const priceWrap = document.createElement('div');
priceWrap.className = 'tier-price';
const priceNum = document.createElement('span');
priceNum.className = 'price-num';
const priceSub = document.createElement('span');
priceSub.className = 'price-sub';
if (pol.price_sats === 0) {
priceNum.textContent = 'Free';
priceSub.textContent = 'forever';
} else {
const effective = fd ? fd.discounted_price_sats : pol.price_sats;
priceNum.textContent = fmtSats(effective);
const suffix = cadenceSuffix(pol);
priceSub.textContent = pol.is_recurring && suffix ? suffix : '';
if (!pol.is_recurring) priceSub.textContent = '';
}
priceWrap.appendChild(priceNum);
if (priceSub.textContent) priceWrap.appendChild(priceSub);
card.appendChild(priceWrap);
if (pol.description) {
const pitch = document.createElement('p');
pitch.className = 'tier-pitch';
pitch.textContent = pol.description;
card.appendChild(pitch);
}
// Merge marketing bullets + (visible) entitlements into a single
// <ul> in the operator-controlled order. Identical pattern to the
// buy page's tier-features list.
const hidden = new Set(pol.hidden_entitlements || []);
const entItems = (pol.entitlements || [])
.filter((slug) => !hidden.has(slug))
.map((slug) => {
const entry = catalog[slug];
return entry && entry.name ? entry.name : slug;
});
const bullets = pol.marketing_bullets || [];
const order = (pol.marketing_bullets_position === 'below')
? entItems.concat(bullets)
: bullets.concat(entItems);
if (order.length > 0) {
const list = document.createElement('ul');
list.className = 'tier-features-list';
order.forEach((text) => {
const li = document.createElement('li');
li.textContent = text;
list.appendChild(li);
});
card.appendChild(list);
}
return card;
}
async function loadLiveTiers() {
const grid = document.getElementById('tier-grid-live');
if (!grid) return;
try {
const [productR, policiesR] = await Promise.all([
fetch(KEYSAT_API + '/v1/products/' + PRODUCT_SLUG, { cache: 'no-cache' }),
fetch(KEYSAT_API + '/v1/products/' + PRODUCT_SLUG + '/policies', { cache: 'no-cache' }),
]);
if (!productR.ok || !policiesR.ok) return; // leave static fallback
const productJ = await productR.json();
const policiesJ = await policiesR.json();
const product = productJ.product || productJ;
const catalog = {};
(product.entitlements_catalog || []).forEach((e) => { catalog[e.slug] = e; });
const policies = (policiesJ.policies || []).filter((p) => !p.is_trial);
if (policies.length === 0) return;
// Empty the grid + render the live cards.
while (grid.firstChild) grid.removeChild(grid.firstChild);
policies.forEach((p) => grid.appendChild(renderCard(p, catalog)));
} catch (_) {
// Silent fallback. The static cards stay rendered.
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', loadLiveTiers);
} else {
loadLiveTiers();
}
})();
</script>
</body>
</html>