Compare commits

...
10 Commits
Author SHA1 Message Date
nobswebdev 3a0291d450 Broaden hero headline for general crypto merchants. 2026-09-19 16:43:46 +02:00
nobswebdev 7871f9beb7 Tweak managed setup and contact section copy. 2026-09-19 15:28:58 +02:00
nobswebdev 624a3e4bbd Add Get started section for self-hosted and managed setup.
Replace How it works with tier cards, nav link, and managed setup pricing copy.
2026-09-19 15:18:28 +02:00
nobswebdev cd3262d016 Describe NullCart buyer shop as working without JavaScript. 2026-09-19 14:18:22 +02:00
nobswebdev 5f4e16bbb0 Fold privacy pillars into Privacy by design and tidy features grid.
Remove the standalone privacy section, pair e-commerce with privacy in the two-column layout, and drop unused pillar styles.
2026-09-19 14:00:07 +02:00
nobswebdev 1acb669f61 Highlight shop blog in e-commerce features on promo site. 2026-09-19 13:47:07 +02:00
nobswebdev ca4d4b5682 Add community Instances section and refresh hero styling.
List hosted shops with local logos and clearnet or Tor links; remove hero and lead-card gradient decorations while keeping lead card accents.
2026-09-18 15:26:29 +02:00
nobswebdev 88d6d9ddef add hero mockup 2026-09-12 21:09:40 +02:00
nobswebdev ebfe68c132 Add Onion-Location header for Tor Browser discovery.
Mirror NullCart's ONION_HOSTNAME env and nginx clearnet header so visitors on HTTPS see ".onion available".
2026-09-11 16:27:36 +02:00
nobswebdev 169bbb8bad remove redundant step from setup 2026-09-10 13:29:21 +02:00
8 changed files with 278 additions and 306 deletions
+1
View File
@@ -1,2 +1,3 @@
COMPOSE_PROJECT_NAME=nullcart_promo_prod
CLEARNET_DOMAIN=nullcart.net
ONION_HOSTNAME=samplexxxxxxxxx.onion
+4 -2
View File
@@ -114,12 +114,14 @@ Save and exit the editor. Optional — run once manually to verify:
./deploy/scripts/show-onion.sh
```
Update onion links in `index.html` if needed, then redeploy:
Copy the hostname into `.env.prod` as `ONION_HOSTNAME`, then recreate nginx:
```bash
./deploy/scripts/deploy.sh
docker compose --env-file .env.prod -f docker-compose.prod.yml up -d --force-recreate nginx
```
Tor Browser will then show ".onion available" when visitors open the clearnet site over HTTPS.
### 9. Updates
```bash
Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

+125 -125
View File
@@ -196,43 +196,57 @@ a:hover {
}
.hero {
position: relative;
padding-block: clamp(3rem, 8vw, 6rem);
overflow: hidden;
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background:
radial-gradient(
ellipse 80% 60% at 50% -20%,
rgba(247, 143, 30, 0.18),
transparent
),
radial-gradient(
ellipse 50% 40% at 100% 50%,
rgba(0, 154, 218, 0.1),
transparent
);
pointer-events: none;
}
.hero__watermark {
position: absolute;
right: 12%;
top: 50%;
transform: translateY(-50%);
width: min(18rem, 40vw);
height: auto;
opacity: 0.06;
pointer-events: none;
.hero__inner {
position: relative;
display: flex;
flex-wrap: nowrap;
align-items: center;
justify-content: space-between;
gap: clamp(1rem, 2vw, 1.5rem);
}
.hero__content {
position: relative;
max-width: 42rem;
flex: 0 1 24rem;
min-width: 0;
max-width: 28rem;
}
.hero__visual {
position: relative;
z-index: 1;
flex: 0 1 46rem;
min-width: 0;
width: 100%;
max-width: 46rem;
height: auto;
}
@media (max-width: 64rem) {
.hero__inner {
flex-wrap: wrap;
width: min(100% - 2rem, var(--max-width));
}
.hero__content {
flex: 1 1 100%;
max-width: none;
}
.hero__highlights {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 0.75rem;
}
.hero__visual {
flex: 1 1 100%;
max-width: 30rem;
margin-inline: auto;
}
}
.hero__badge {
@@ -250,28 +264,28 @@ a:hover {
}
.hero h1 {
margin: 0 0 1.25rem;
font-size: clamp(2rem, 5vw, 3rem);
margin: 0 0 1rem;
font-size: clamp(2rem, 5vw, 2.85rem);
font-weight: 700;
line-height: 1.15;
letter-spacing: -0.03em;
}
.hero__highlights {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 0.75rem;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 0.5rem;
margin: 0;
padding: 0;
list-style: none;
}
.hero__highlights li {
padding: 0.375rem 0.75rem;
padding: 0.3rem 0.6rem;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
font-size: 0.875rem;
font-size: 0.8125rem;
font-weight: 500;
color: var(--text);
}
@@ -285,18 +299,12 @@ a:hover {
}
.hero__sub {
margin: 2rem 0 2rem;
font-size: clamp(1rem, 2vw, 1.125rem);
margin: 1.25rem 0 1.25rem;
font-size: 0.9375rem;
color: var(--muted);
line-height: 1.7;
line-height: 1.6;
}
.hero__actions-row {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 1rem 1.25rem;
}
.link-group {
display: flex;
@@ -353,50 +361,6 @@ a:hover {
margin-bottom: 0;
}
.pillars {
display: flex;
flex-wrap: wrap;
gap: 0.625rem;
margin: 0;
padding: 0;
list-style: none;
}
.privacy-features {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
gap: 0.625rem;
}
.pillar {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.5rem 0.875rem;
border: 1px solid var(--border);
border-radius: 999px;
background: var(--surface);
font-size: 0.8125rem;
font-weight: 500;
color: var(--text);
white-space: nowrap;
}
.pillar__dot {
width: 0.375rem;
height: 0.375rem;
border-radius: 50%;
flex-shrink: 0;
}
.pillar:nth-child(odd) .pillar__dot {
background: var(--brand);
}
.pillar:nth-child(even) .pillar__dot {
background: var(--accent);
}
.features-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -418,12 +382,6 @@ a:hover {
.feature-card--lead {
border-color: rgba(247, 143, 30, 0.4);
background: linear-gradient(
135deg,
rgba(247, 143, 30, 0.08),
rgba(0, 154, 218, 0.05),
var(--surface)
);
}
.feature-card__icon {
@@ -460,57 +418,46 @@ a:hover {
margin-top: 0.375rem;
}
.steps {
.tiers {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-columns: repeat(2, 1fr);
gap: 1.25rem;
margin: 0;
padding: 0;
list-style: none;
counter-reset: step;
}
@media (max-width: 48rem) {
.steps {
.tiers {
grid-template-columns: 1fr;
}
}
.step {
.tier-card {
display: flex;
flex-direction: column;
align-items: flex-start;
padding: 1.5rem;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--surface);
}
.step::before {
counter-increment: step;
content: counter(step);
display: flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
margin-bottom: 1rem;
border-radius: 50%;
background: var(--brand);
color: #fff;
font-size: 0.875rem;
font-weight: 700;
}
.step h3 {
margin: 0 0 0.5rem;
font-size: 1rem;
.tier-card h3 {
margin: 0 0 0.75rem;
font-size: 1.0625rem;
font-weight: 600;
}
.step p {
margin: 0;
.tier-card p {
flex: 1;
margin: 0 0 1.25rem;
font-size: 0.9375rem;
color: var(--muted);
}
.tier-card p strong {
color: var(--text);
font-weight: 600;
}
.roadmap {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -705,6 +652,59 @@ body:has(.lightbox:target) {
}
}
.instances-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
gap: 1.25rem;
margin: 0;
padding: 0;
list-style: none;
}
.instance-card {
display: flex;
flex-direction: column;
gap: 1rem;
height: 100%;
padding: 1.5rem;
border: 1px solid var(--border);
border-radius: var(--radius-lg);
background: var(--surface);
}
.instance-card__logo {
width: auto;
max-width: 10rem;
max-height: 2.5rem;
object-fit: contain;
object-position: left center;
}
.instance-card__body {
display: flex;
flex: 1;
flex-direction: column;
gap: 0.5rem;
}
.instance-card__name {
margin: 0;
font-size: 1.125rem;
font-weight: 600;
}
.instance-card__desc {
flex: 1;
margin: 0;
font-size: 0.9375rem;
color: var(--muted);
line-height: 1.5;
}
.instance-card .repo-links {
margin-top: 0.5rem;
}
.contact-card {
padding: 1.25rem 1.5rem;
border: 1px solid var(--border);
+138 -178
View File
@@ -38,10 +38,12 @@
</a>
<nav class="site-nav" aria-label="Main">
<a href="#features">Features</a>
<a href="#get-started">Get started</a>
<a href="#gallery">Gallery</a>
<a href="#demo">Live demo</a>
<a href="#source">Open source</a>
<a href="#roadmap">Roadmap</a>
<a href="#instances">Instances</a>
<a href="#contact">Contact</a>
</nav>
</div>
@@ -49,72 +51,25 @@
<main>
<section class="hero">
<div class="container">
<img
class="hero__watermark"
src="assets/favicon.png"
width="463"
height="539"
alt=""
aria-hidden="true"
/>
<div class="container hero__inner">
<div class="hero__content">
<p class="hero__badge">Free &amp; open source</p>
<h1>Self-hosted crypto shop</h1>
<ul class="hero__highlights" aria-label="Key features">
<li>Non-custodial</li>
<li>No JS</li>
<li>No accounts</li>
<li>Clearnet &amp; Tor onion</li>
</ul>
<h1>Self-hosted crypto shop for your business</h1>
<p class="hero__sub">
Complete, privacy-focused crypto e-commerce solution. Built with a
mission to support the crypto economy.
mission to grow the crypto economy.
</p>
<div class="hero__actions">
<div class="hero__actions-row">
<div class="link-group">
<span class="link-group__label">Repository</span>
<div class="repo-links">
<a
class="btn btn--primary"
href="https://git.nobswebdev.com/nobswebdev/nullcart"
target="_blank"
rel="noopener noreferrer"
>Clearnet</a
>
<a
class="btn btn--ghost"
href="http://4f5t7o2vgo4sdaliwax3isyxzfuxvbvkuwk4smfowutrvx2uneu4egyd.onion/nobswebdev/nullcart"
target="_blank"
rel="noopener noreferrer"
>Tor</a
>
</div>
</div>
<div class="link-group">
<span class="link-group__label">Live demo</span>
<div class="repo-links">
<a
class="btn btn--primary"
href="https://demo.nullcart.net/"
target="_blank"
rel="noopener noreferrer"
>Clearnet</a
>
<a
class="btn btn--ghost"
href="http://3jh2ltksxtsmjidik3st6dawfdcheo2cyv2vth2365af75zdxnwqqsyd.onion/"
target="_blank"
rel="noopener noreferrer"
>Tor</a
>
</div>
</div>
<a class="btn btn--ghost" href="#features">See features</a>
</div>
<a class="btn btn--primary" href="#features">See features</a>
</div>
</div>
<img
class="hero__visual"
src="assets/hero_mockup.png"
width="1024"
height="562"
alt="NullCart storefront on desktop and mobile"
/>
</div>
</section>
@@ -125,67 +80,12 @@
NullCart is a free, open-source crypto shop you host yourself. You
run it on your server with built-in non-custodial wallets - no
payment processors in the middle. Buyers pay in crypto without
accounts, and the storefront runs without JavaScript. You manage
accounts, and the storefront works without JavaScript. You manage
products and orders from a merchant CMS, on clearnet and Tor.
</p>
</div>
</section>
<section class="section" id="privacy">
<div class="container">
<h2 class="section__title">Privacy features</h2>
<p class="section__lead">
Privacy at every layer of the buyer experience.
</p>
<ul class="pillars privacy-features">
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Free and open
source
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> No JS on
buyer shop
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> No buyer
accounts
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Non-custodial
wallets
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Monero
supported
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Zero
third-party processors
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Own wallet
integration
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Encrypted
sensitive data
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Auto order
data wipe
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> Self-hosted
clearnet + onion
</li>
<li class="pillar">
<span class="pillar__dot" aria-hidden="true"></span> SimpleX
merchant alerts
</li>
</ul>
</div>
</section>
<section class="section section--border" id="features">
<div class="container">
<h2 class="section__title">Features</h2>
@@ -206,17 +106,61 @@
</svg>
<h3>Privacy by design</h3>
<p class="feature-card__lead">
Your buyers don't need accounts. Your shop doesn't hoard their
data.
Privacy at every layer of the buyer experience. Your buyers
don't need accounts, and your shop doesn't hoard their data.
</p>
<ul>
<li>Free and open source</li>
<li>Buyer shop works without JavaScript</li>
<li>Works in Tor Browser strictest security mode</li>
<li>
No buyer accounts - orders open with a private access token
</li>
<li>Non-custodial shop wallets</li>
<li>Monero supported</li>
<li>Encrypted sensitive data at rest</li>
<li>Automatic order data wipe</li>
<li>Self-hosted clearnet and Tor onion</li>
<li>SimpleX merchant alerts</li>
</ul>
</article>
<article class="feature-card">
<svg
class="feature-card__icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<rect x="3" y="3" width="18" height="18" rx="2" />
<path d="M3 9h18M9 21V9" />
</svg>
<h3>E-commerce features</h3>
<p class="feature-card__lead">
A full storefront for digital and physical goods - catalog,
cart, checkout, and fulfillment.
</p>
<ul>
<li>
No JavaScript on the buyer shop - no client-side scripts
Product catalog, categories, and variants with image galleries
</li>
<li>Works in Tor Browser strictest security mode</li>
<li>Shopping cart, checkout, and discount codes</li>
<li>
No sign-up or profiles - orders open with a private access
token
Auto-deliver digital goods and quote shipping for physical
orders
</li>
<li>
Order tracking, live payment status, and per-order buyer chat
</li>
<li>Custom branding - your logo, favicon, and shop name</li>
<li>
Shop blog for product spotlights, how-tos, and SEO-friendly
content
</li>
<li>
Prices shown in fiat, paid in crypto with live exchange rates
</li>
</ul>
</article>
@@ -249,42 +193,6 @@
</ul>
</article>
<article class="feature-card">
<svg
class="feature-card__icon"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
aria-hidden="true"
>
<rect x="3" y="3" width="18" height="18" rx="2" />
<path d="M3 9h18M9 21V9" />
</svg>
<h3>E-commerce features</h3>
<p class="feature-card__lead">
A full storefront for digital and physical goods - catalog,
cart, checkout, and fulfillment.
</p>
<ul>
<li>
Product catalog, categories, and variants with image galleries
</li>
<li>Shopping cart, checkout, and discount codes</li>
<li>
Auto-deliver digital goods and quote shipping for physical
orders
</li>
<li>
Order tracking, live payment status, and per-order buyer chat
</li>
<li>Custom branding - your logo, favicon, and shop name</li>
<li>
Prices shown in fiat, paid in crypto with live exchange rates
</li>
</ul>
</article>
<article class="feature-card">
<svg
class="feature-card__icon"
@@ -314,32 +222,42 @@
</div>
</section>
<section class="section">
<section class="section section--border" id="get-started">
<div class="container">
<h2 class="section__title">How it works</h2>
<h2 class="section__title">Get started</h2>
<p class="section__lead">
Three steps from zero to selling with crypto.
Launch on your own server: install from the guide, or get one-time
help if you are not comfortable with VPS and DNS yourself.
</p>
<ol class="steps">
<li class="step">
<h3>Deploy</h3>
<div class="tiers">
<article class="tier-card">
<h3>Self-hosted</h3>
<p>
Clone the repo, configure your server, and go live on HTTPS and
Tor.
Full NullCart is free and open source. Use the deployment guide
in the repository to install on your VPS with HTTPS and Tor. You
handle provisioning, DNS, and updates.
</p>
</li>
<li class="step">
<h3>Set up your shop</h3>
<p>Add products in the CMS and upload your branding.</p>
</li>
<li class="step">
<h3>Sell</h3>
<a
class="btn btn--primary"
href="https://git.nobswebdev.com/nobswebdev/nullcart"
target="_blank"
rel="noopener noreferrer"
>Repository</a
>
</article>
<article class="tier-card">
<h3>Managed setup</h3>
<p>
Buyers pay in crypto. You fulfill orders, chat when needed, and
get notified via SimpleX.
We will setup everything for you if you are not comfortable with
servers yourself or you want to hand over this task to us.
<strong>$25 USD</strong> paid in Monero covers our help to make
your shop up and running. VPS and domain are paid separately.
Optional upgrade to the latest version later:
<strong>$25 USD</strong> in Monero if you ever need it.
</p>
</li>
</ol>
<a class="btn btn--primary" href="#contact">Contact</a>
</article>
</div>
</div>
</section>
@@ -646,12 +564,52 @@
</div>
</section>
<section class="section section--border" id="instances">
<div class="container">
<h2 class="section__title">Instances</h2>
<p class="section__lead">NullCart shops hosted by the community.</p>
<ul class="instances-list">
<li>
<article class="instance-card">
<img
class="instance-card__logo"
src="assets/instances_logos/accsnake.png"
width="248"
height="40"
alt="Accsnake"
/>
<div class="instance-card__body">
<h3 class="instance-card__name">Accsnake</h3>
<p class="instance-card__desc">
Ready-made accounts merchant.
</p>
<div class="repo-links">
<a
class="btn btn--primary"
href="https://accsnake.com/"
target="_blank"
rel="noopener noreferrer"
>Clearnet</a
>
<a
class="btn btn--ghost"
href="http://itj7v5hhp6cohgkeuy6ijuxj7f3gak6may6glwx3z27l32lbfxktmtyd.onion/"
target="_blank"
rel="noopener noreferrer"
>Tor</a
>
</div>
</div>
</article>
</li>
</ul>
</div>
</section>
<section class="section section--border" id="contact">
<div class="container">
<h2 class="section__title">Contact</h2>
<p class="section__lead">
Questions, feedback, or collaboration - reach out.
</p>
<p class="section__lead">Need help? Have questions? Reach out.</p>
<div class="contact-grid">
<div class="contact-card">
<h3>Email</h3>
@@ -676,10 +634,12 @@
<p class="site-footer__copy">&copy; 2026 NullCart by nobswebdev.</p>
<nav class="site-footer__nav" aria-label="Footer">
<a href="#features">Features</a>
<a href="#get-started">Get started</a>
<a href="#gallery">Gallery</a>
<a href="#demo">Demo</a>
<a href="#source">Open source</a>
<a href="#roadmap">Roadmap</a>
<a href="#instances">Instances</a>
<a href="#contact">Contact</a>
</nav>
<div class="site-footer__links">
+2
View File
@@ -18,6 +18,8 @@ server {
ssl_certificate /etc/nginx/certs/live/${CLEARNET_DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/live/${CLEARNET_DOMAIN}/privkey.pem;
${ONION_LOCATION_HEADER}
root /usr/share/nginx/html;
index index.html;
+8 -1
View File
@@ -2,7 +2,14 @@
set -eu
export CLEARNET_DOMAIN
envsubst '${CLEARNET_DOMAIN}' \
if [ -n "${ONION_HOSTNAME:-}" ]; then
export ONION_LOCATION_HEADER="add_header Onion-Location \"http://${ONION_HOSTNAME}\$request_uri\";"
else
export ONION_LOCATION_HEADER=""
fi
envsubst '${CLEARNET_DOMAIN} ${ONION_LOCATION_HEADER}' \
< /etc/nginx/templates/conf.d/clearnet.conf.template \
> /etc/nginx/conf.d/clearnet.conf