LibrePortal/site/src/index.njk
librelad c1863b3e00 feat(site): data-driven Eleventy marketing site
A nebula-themed marketing/get site under site/, matching the dashboard
(aurora background, glass cards, system fonts — no Google/third-party
calls). The app grid + category filters are generated from the repo:
scripts/gen-data.mjs reads each containers/<app>/<app>.config and emits the
data Eleventy renders. `npm run build` -> static site in dist/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-22 00:45:01 +01:00

163 lines
9.2 KiB
Plaintext

---
layout: layout.njk
---
<!-- ===== HERO ===== -->
<header class="hero" id="top">
<span class="badge h-anim d1"><span class="dot"></span>{{ site.version }} · early days, no telemetry ever</span>
<img class="hero-logo h-anim d2" src="assets/libreportal.svg" alt="">
<h1 class="h-anim d2">Your own private corner<br>of the <span class="grad">universe</span></h1>
<p class="sub h-anim d3">Self-host the apps you actually rely on — on your own server.
One command brings up a whole platform, and your data never leaves your orbit.</p>
<div class="portal h-anim d4" id="install">
<div class="term">
<div class="term-bar"><i></i><i></i><i></i><em>~ one command, your whole server</em></div>
<div class="term-body">
<code data-install-cmd="{{ site.installCmd }}"><span class="p">bash</span> &lt;(<span class="p">curl</span> -fsSL <span class="u">{{ site.installUrl }}</span>) init</code>
<button class="copy" aria-label="Copy install command">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>
<span>Copy</span>
</button>
</div>
</div>
<div class="portal-foot">
<span class="hint">curl·to·shell, on a privacy tool? <a href="{{ site.initUrl }}" target="_blank" rel="noopener">read it first →</a></span>
<span>·</span>
<a href="{{ site.repo }}" target="_blank" rel="noopener">or clone the repo</a>
</div>
</div>
<div class="scroll-cue">explore</div>
</header>
<!-- ===== FEATURES ===== -->
<section class="pad wrap" id="features">
<span class="eyebrow reveal">What comes online</span>
<h2 class="reveal">A whole platform, <span class="grad">handled for you</span></h2>
<p class="lead reveal">No yak-shaving. LibrePortal wires up the boring, fiddly infrastructure so you can run the good stuff.</p>
<div class="feat-grid stagger">
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><path d="M21 8 12 3 3 8l9 5 9-5Z"/><path d="m3 8 9 5v8M21 8l-9 5"/></svg></div>
<h3>One-click apps</h3>
<p>Nextcloud, Vaultwarden, Jellyfin, Gitea and dozens more — picked from a menu, deployed clean.</p>
</div>
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z"/><path d="m9 12 2 2 4-4"/></svg></div>
<h3>Traefik + auto-SSL</h3>
<p>A reverse proxy with automatic Let's Encrypt certificates. HTTPS everywhere, configured for you.</p>
</div>
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><path d="M3 7l9-4 9 4-9 4-9-4Z"/><path d="m3 12 9 4 9-4M3 17l9 4 9-4"/></svg></div>
<h3>Rootless Docker</h3>
<p>Containers run without root and with sane security defaults — CrowdSec on guard out of the box.</p>
</div>
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="14" rx="2"/><path d="M3 9h18M8 21h8"/></svg></div>
<h3>A real dashboard</h3>
<p>Install, configure, back up and monitor everything from a clean web UI. No SSH archaeology.</p>
</div>
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a15 15 0 0 1 0 18M12 3a15 15 0 0 0 0 18"/></svg></div>
<h3>Optional VPN routing</h3>
<p>Send any app's traffic through gluetun. Keep the things that should be private, private.</p>
</div>
<div class="card glass">
<div class="ico"><svg viewBox="0 0 24 24"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12Z"/><path d="m4 4 16 16"/></svg></div>
<h3>No telemetry</h3>
<p>Nothing phones home. No accounts required to self-host. The software is yours, entirely.</p>
</div>
</div>
</section>
<!-- ===== APPS (generated from the repo) ===== -->
<section class="pad wrap apps-section" id="apps">
<span class="eyebrow reveal">The constellation</span>
<h2 class="reveal">Dozens of apps, <span class="grad">one sky</span></h2>
<p class="lead reveal" style="margin-inline:auto">Pulled straight from the repo — {{ apps.length }} apps you can light up from the dashboard, and switch off just as easily.</p>
<div class="app-filters reveal">
<button class="chip active" data-cat="all">All <span class="n">{{ apps.length }}</span></button>
{%- for c in categories %}
<button class="chip" data-cat="{{ c.id }}">{{ c.name }} <span class="n">{{ c.count }}</span></button>
{%- endfor %}
</div>
<div class="app-grid reveal">
{%- for app in apps %}
{% include "app-card.njk" %}
{%- endfor %}
</div>
<p class="app-count">{{ apps.length }} apps</p>
</section>
<!-- ===== CONNECT / COURIER ===== -->
<section class="pad wrap" id="connect">
<div class="connect">
<div class="reveal">
<span class="eyebrow">LibrePortal Connect · optional</span>
<h2>We're the courier.<br><span class="grad">You hold the key.</span></h2>
<p class="lead">Reaching your server from your phone and keeping off-site backups are the fiddly bits.
Connect handles them — but works like a courier carrying a <b>sealed box</b>.</p>
<ul>
<li><svg viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></svg><span><b>Reach it anywhere.</b> No port-forwarding, no exposing your box to the internet.</span></li>
<li><svg viewBox="0 0 24 24"><rect x="4" y="10" width="16" height="11" rx="2"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></svg><span><b>Encrypted off-site backups.</b> We only ever hold a locked box — you keep the only key.</span></li>
<li><svg viewBox="0 0 24 24"><path d="m2 12 5-5 5 5-5 5-5-5Z" opacity=".5"/><path d="M12 12h10"/></svg><span><b>We never run your apps.</b> Your data and keys never leave your machine.</span></li>
</ul>
<span class="pill">every part is free to self-host, too — you pay only for convenience</span>
</div>
<div class="vault reveal">
<svg viewBox="0 0 200 200" aria-hidden="true">
<g class="orbit" opacity=".55">
<ellipse cx="100" cy="100" rx="92" ry="40" stroke="var(--accent)" stroke-width="1" fill="none" opacity=".5"/>
<circle cx="192" cy="100" r="3.2" fill="var(--accent)"/>
</g>
<g class="orbit" style="animation-duration:18s;animation-direction:reverse" opacity=".55">
<ellipse cx="100" cy="100" rx="40" ry="92" stroke="#7ae9ff" stroke-width="1" fill="none" opacity=".45"/>
<circle cx="100" cy="8" r="2.6" fill="#7ae9ff"/>
</g>
<g transform="translate(100 100)">
<rect x="-38" y="-30" width="76" height="62" rx="9" fill="rgba(10,16,32,.92)" stroke="url(#bg)" stroke-width="1.6"/>
<path d="M-38 -10 H38" stroke="url(#bg)" stroke-width="1.2" opacity=".5"/>
<rect x="-12" y="-4" width="24" height="20" rx="4" fill="none" stroke="var(--accent)" stroke-width="2"/>
<path d="M-6 -4 V-12 a6 6 0 0 1 12 0 V-4" fill="none" stroke="var(--accent)" stroke-width="2"/>
<circle cx="0" cy="6" r="2.4" fill="var(--accent)"/>
</g>
<defs><linearGradient id="bg" x1="-40" y1="-40" x2="40" y2="40" gradientUnits="userSpaceOnUse"><stop stop-color="#7ae9ff"/><stop offset="1" stop-color="#00d4ff"/></linearGradient></defs>
</svg>
</div>
</div>
</section>
<!-- ===== PROMISE ===== -->
<section class="pad wrap promise" id="promise">
<span class="eyebrow reveal">The promise</span>
<h2 class="reveal">Free software, <span class="grad">and it stays that way</span></h2>
<p class="lead reveal" style="margin-inline:auto">In plain language, so you can hold us to it.</p>
<div class="pledges stagger">
<div class="pledge glass"><div class="big grad">100%</div><p>Every feature, free to self-host. Forever. No crippled edition.</p></div>
<div class="pledge glass"><div class="big grad">0</div><p>Trackers. No telemetry, no phoning home, no accounts to run it.</p></div>
<div class="pledge glass"><div class="big grad">&empty;</div><p>Feature paywalls. You never pay to <em>unlock</em> — only for convenience.</p></div>
<div class="pledge glass"><div class="big grad">&infin;</div><p>What's open stays open. No rug-pulls, ever. AGPLv3.</p></div>
</div>
<a class="link reveal" href="{{ site.promiseUrl }}" target="_blank" rel="noopener">Read the full Promise →</a>
</section>
<!-- ===== FINAL CTA ===== -->
<section class="final wrap">
<span class="eyebrow reveal">Ready when you are</span>
<h2 class="reveal">Light up your corner<br>of the <span class="grad">universe.</span></h2>
<div class="portal reveal">
<div class="term">
<div class="term-bar"><i></i><i></i><i></i><em>~</em></div>
<div class="term-body">
<code data-install-cmd="{{ site.installCmd }}"><span class="p">bash</span> &lt;(<span class="p">curl</span> -fsSL <span class="u">{{ site.installUrl }}</span>) init</code>
<button class="copy" aria-label="Copy install command">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>
<span>Copy</span>
</button>
</div>
</div>
</div>
</section>