---
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>
