AdGuard
+ networking +DNS based Ad Blocking
+diff --git a/containers/libreportal/frontend/css/ssh.css b/containers/libreportal/frontend/css/ssh.css index 965f3d6..4ea7d31 100644 --- a/containers/libreportal/frontend/css/ssh.css +++ b/containers/libreportal/frontend/css/ssh.css @@ -2,9 +2,7 @@ backup.css for the cards; this file only adds page chrome + the key list. */ .ssh-page { - max-width: 860px; - margin: 0 auto; - padding: 8px 4px 40px; + padding: 4px 2px 40px; } /* "Tools" group heading in the Admin (config) sidebar, above SSH Access. */ diff --git a/site/dist/assets/apps/adguard.svg b/site/dist/assets/apps/adguard.svg new file mode 100644 index 0000000..f6118fc --- /dev/null +++ b/site/dist/assets/apps/adguard.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/authelia.svg b/site/dist/assets/apps/authelia.svg new file mode 100644 index 0000000..9880b3b --- /dev/null +++ b/site/dist/assets/apps/authelia.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/bookstack.svg b/site/dist/assets/apps/bookstack.svg new file mode 100644 index 0000000..a6ad581 --- /dev/null +++ b/site/dist/assets/apps/bookstack.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/crowdsec.svg b/site/dist/assets/apps/crowdsec.svg new file mode 100644 index 0000000..fd4ffac --- /dev/null +++ b/site/dist/assets/apps/crowdsec.svg @@ -0,0 +1,32 @@ + diff --git a/site/dist/assets/apps/dashy.svg b/site/dist/assets/apps/dashy.svg new file mode 100644 index 0000000..ce68744 --- /dev/null +++ b/site/dist/assets/apps/dashy.svg @@ -0,0 +1,161 @@ + + diff --git a/site/dist/assets/apps/default.svg b/site/dist/assets/apps/default.svg new file mode 100644 index 0000000..343d2de --- /dev/null +++ b/site/dist/assets/apps/default.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/focalboard.svg b/site/dist/assets/apps/focalboard.svg new file mode 100644 index 0000000..b78e7e3 --- /dev/null +++ b/site/dist/assets/apps/focalboard.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/gitea.svg b/site/dist/assets/apps/gitea.svg new file mode 100644 index 0000000..11c6df8 --- /dev/null +++ b/site/dist/assets/apps/gitea.svg @@ -0,0 +1,4 @@ + diff --git a/site/dist/assets/apps/gluetun.svg b/site/dist/assets/apps/gluetun.svg new file mode 100644 index 0000000..a39521c --- /dev/null +++ b/site/dist/assets/apps/gluetun.svg @@ -0,0 +1,13 @@ + diff --git a/site/dist/assets/apps/grafana.svg b/site/dist/assets/apps/grafana.svg new file mode 100644 index 0000000..54be1e2 --- /dev/null +++ b/site/dist/assets/apps/grafana.svg @@ -0,0 +1,70 @@ + + + diff --git a/site/dist/assets/apps/headscale.svg b/site/dist/assets/apps/headscale.svg new file mode 100644 index 0000000..06f406a --- /dev/null +++ b/site/dist/assets/apps/headscale.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/invidious.svg b/site/dist/assets/apps/invidious.svg new file mode 100644 index 0000000..80e78a4 --- /dev/null +++ b/site/dist/assets/apps/invidious.svg @@ -0,0 +1,2 @@ + + diff --git a/site/dist/assets/apps/ipinfo.svg b/site/dist/assets/apps/ipinfo.svg new file mode 100644 index 0000000..656169c --- /dev/null +++ b/site/dist/assets/apps/ipinfo.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/jellyfin.svg b/site/dist/assets/apps/jellyfin.svg new file mode 100644 index 0000000..0e56a50 --- /dev/null +++ b/site/dist/assets/apps/jellyfin.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/jitsimeet.svg b/site/dist/assets/apps/jitsimeet.svg new file mode 100644 index 0000000..5a3526a --- /dev/null +++ b/site/dist/assets/apps/jitsimeet.svg @@ -0,0 +1,650 @@ + + + + diff --git a/site/dist/assets/apps/libreportal.svg b/site/dist/assets/apps/libreportal.svg new file mode 100644 index 0000000..a476796 --- /dev/null +++ b/site/dist/assets/apps/libreportal.svg @@ -0,0 +1,605 @@ + \ No newline at end of file diff --git a/site/dist/assets/apps/linkding.svg b/site/dist/assets/apps/linkding.svg new file mode 100644 index 0000000..089630d --- /dev/null +++ b/site/dist/assets/apps/linkding.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/mastodon.svg b/site/dist/assets/apps/mastodon.svg new file mode 100644 index 0000000..dd5075e --- /dev/null +++ b/site/dist/assets/apps/mastodon.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/nextcloud.svg b/site/dist/assets/apps/nextcloud.svg new file mode 100644 index 0000000..336aff5 --- /dev/null +++ b/site/dist/assets/apps/nextcloud.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/dist/assets/apps/ollama.svg b/site/dist/assets/apps/ollama.svg new file mode 100644 index 0000000..6bba73a --- /dev/null +++ b/site/dist/assets/apps/ollama.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/onlyoffice.svg b/site/dist/assets/apps/onlyoffice.svg new file mode 100644 index 0000000..364522c --- /dev/null +++ b/site/dist/assets/apps/onlyoffice.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/owncloud.svg b/site/dist/assets/apps/owncloud.svg new file mode 100644 index 0000000..cf650c7 --- /dev/null +++ b/site/dist/assets/apps/owncloud.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/pihole.svg b/site/dist/assets/apps/pihole.svg new file mode 100644 index 0000000..5bda461 --- /dev/null +++ b/site/dist/assets/apps/pihole.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/portainer.svg b/site/dist/assets/apps/portainer.svg new file mode 100644 index 0000000..45cf83a --- /dev/null +++ b/site/dist/assets/apps/portainer.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/prometheus.svg b/site/dist/assets/apps/prometheus.svg new file mode 100644 index 0000000..309d704 --- /dev/null +++ b/site/dist/assets/apps/prometheus.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/searxng.svg b/site/dist/assets/apps/searxng.svg new file mode 100644 index 0000000..2ddf53b --- /dev/null +++ b/site/dist/assets/apps/searxng.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/speedtest.svg b/site/dist/assets/apps/speedtest.svg new file mode 100644 index 0000000..2fd0d2b --- /dev/null +++ b/site/dist/assets/apps/speedtest.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/traefik.svg b/site/dist/assets/apps/traefik.svg new file mode 100644 index 0000000..a86b9b7 --- /dev/null +++ b/site/dist/assets/apps/traefik.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/trilium.svg b/site/dist/assets/apps/trilium.svg new file mode 100644 index 0000000..2ecb6e4 --- /dev/null +++ b/site/dist/assets/apps/trilium.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/unbound.svg b/site/dist/assets/apps/unbound.svg new file mode 100644 index 0000000..cfc5d8d --- /dev/null +++ b/site/dist/assets/apps/unbound.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/vaultwarden.svg b/site/dist/assets/apps/vaultwarden.svg new file mode 100644 index 0000000..41ca105 --- /dev/null +++ b/site/dist/assets/apps/vaultwarden.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/apps/wireguard.svg b/site/dist/assets/apps/wireguard.svg new file mode 100644 index 0000000..b778001 --- /dev/null +++ b/site/dist/assets/apps/wireguard.svg @@ -0,0 +1 @@ + diff --git a/site/dist/assets/favicon.ico b/site/dist/assets/favicon.ico new file mode 100644 index 0000000..622f2d3 Binary files /dev/null and b/site/dist/assets/favicon.ico differ diff --git a/site/dist/assets/libreportal.svg b/site/dist/assets/libreportal.svg new file mode 100644 index 0000000..a476796 --- /dev/null +++ b/site/dist/assets/libreportal.svg @@ -0,0 +1,605 @@ + \ No newline at end of file diff --git a/site/dist/assets/main.js b/site/dist/assets/main.js new file mode 100644 index 0000000..d9dda04 --- /dev/null +++ b/site/dist/assets/main.js @@ -0,0 +1,66 @@ +// LibrePortal site — interactions (mobile drawer, copy, scrollspy, reveal, app filter) +(() => { + // mobile drawer (same behaviour as the dashboard) + const menuToggle = document.getElementById('mobile-menu-toggle'); + const drawer = document.getElementById('mobile-drawer'); + if (menuToggle && drawer) { + menuToggle.addEventListener('click', () => { + const open = drawer.classList.toggle('mobile-open'); + document.body.style.overflow = open ? 'hidden' : ''; + }); + drawer.querySelectorAll('a').forEach((a) => + a.addEventListener('click', () => { drawer.classList.remove('mobile-open'); document.body.style.overflow = ''; })); + } + + // copy the install command + const cmdEl = document.querySelector('[data-install-cmd]'); + const CMD = cmdEl ? cmdEl.getAttribute('data-install-cmd') : ''; + document.querySelectorAll('.copy').forEach((btn) => { + btn.addEventListener('click', async () => { + try { await navigator.clipboard.writeText(CMD); } + catch { const t = document.createElement('textarea'); t.value = CMD; document.body.appendChild(t); t.select(); document.execCommand('copy'); t.remove(); } + btn.classList.add('done'); + const span = btn.querySelector('span'); const prev = span.textContent; span.textContent = 'Copied ✓'; + setTimeout(() => { btn.classList.remove('done'); span.textContent = prev; }, 1700); + }); + }); + + // scrollspy → light up the active topbar pill + const spies = [...document.querySelectorAll('.nav-item[data-spy]')]; + if (spies.length) { + const spyIO = new IntersectionObserver((entries) => { + entries.forEach((e) => { + if (e.isIntersecting) spies.forEach((s) => s.classList.toggle('nav-active', s.dataset.spy === e.target.id)); + }); + }, { rootMargin: '-45% 0px -50% 0px', threshold: 0 }); + spies.map((s) => document.getElementById(s.dataset.spy)).filter(Boolean).forEach((s) => spyIO.observe(s)); + } + + // reveal on scroll + const io = new IntersectionObserver((entries) => { + entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); + }, { threshold: 0.16, rootMargin: '0px 0px -8% 0px' }); + document.querySelectorAll('.reveal, .stagger').forEach((el) => io.observe(el)); + + // app category filter + const filters = document.querySelector('.app-filters'); + if (filters) { + const cards = [...document.querySelectorAll('.app-card')]; + const countEl = document.querySelector('.app-count'); + const update = (cat) => { + let shown = 0; + cards.forEach((c) => { + const match = cat === 'all' || (c.dataset.cats || '').split(' ').includes(cat); + c.classList.toggle('hidden', !match); + if (match) shown++; + }); + if (countEl) countEl.textContent = `${shown} app${shown === 1 ? '' : 's'}`; + }; + filters.addEventListener('click', (e) => { + const chip = e.target.closest('.chip'); + if (!chip) return; + filters.querySelectorAll('.chip').forEach((c) => c.classList.toggle('active', c === chip)); + update(chip.dataset.cat); + }); + } +})(); diff --git a/site/dist/assets/style.css b/site/dist/assets/style.css new file mode 100644 index 0000000..c187888 --- /dev/null +++ b/site/dist/assets/style.css @@ -0,0 +1,276 @@ +/* ============ NEBULA THEME (lifted from the dashboard) ============ */ +:root{ + --gradient-from:#1a1442; /* indigo */ + --gradient-mid:#1b2a5e; /* violet-blue */ + --gradient-to:#0f3b6e; /* ocean */ + --surface-solid:#0f1729; + + --accent:#00d4ff; + --accent-hover:#0099cc; + --accent-rgb:0,212,255; + --accent-soft:rgba(0,212,255,.15); + + --text-primary:#ffffff; + --text-secondary:rgba(255,255,255,.82); + --text-muted:rgba(255,255,255,.65); + --text-faint:rgba(255,255,255,.45); + --text-on-accent:#0a1426; + --text-rgb:255,255,255; + + --border:rgba(255,255,255,.16); + --border-strong:rgba(255,255,255,.26); + --border-subtle:rgba(255,255,255,.08); + + --card-bg:linear-gradient(155deg, rgba(255,255,255,.09) 0%, rgba(0,212,255,.05) 100%); + --card-border:rgba(255,255,255,.16); + --card-shadow:0 4px 18px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.06); + --card-shadow-hover:0 10px 32px rgba(0,212,255,.18), 0 4px 18px rgba(0,0,0,.40), inset 0 1px 0 rgba(255,255,255,.10); + + --topbar-bg:rgba(15,25,50,.40); + --console-bg:rgba(0,0,0,.40); + --surface-bg-solid:#0f1729; + + --font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + --font-mono:"SF Mono", "Monaco", "Menlo", "Ubuntu Mono", "Courier New", monospace; + + --maxw:1180px; + --ease:cubic-bezier(.22,.61,.36,1); +} + +*{box-sizing:border-box;margin:0;padding:0} +html{scroll-behavior:smooth} +body{font-family:var(--font-sans);background:var(--surface-solid);color:var(--text-primary); + line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility} + +/* ============ AURORA BACKDROP (from aurora-background.css) ============ */ +.cosmos{position:fixed;inset:0;z-index:-2;pointer-events:none;overflow:hidden; + background: + radial-gradient(ellipse at 20% 22%, var(--gradient-mid) 0%, transparent 55%), + radial-gradient(ellipse at 82% 74%, var(--gradient-to) 0%, transparent 55%), + linear-gradient(135deg, var(--gradient-from) 0%, var(--gradient-from) 40%, var(--gradient-mid) 100%);} +.cosmos::before{content:'';position:absolute;inset:-25%; + background:conic-gradient(from 0deg at 50% 50%, + rgba(var(--accent-rgb),0) 0deg, rgba(var(--accent-rgb),.18) 60deg, + rgba(var(--accent-rgb),.22) 130deg, rgba(var(--accent-rgb),.20) 200deg, + rgba(var(--accent-rgb),.18) 280deg, rgba(var(--accent-rgb),0) 360deg); + filter:blur(70px);animation:auroraSpin 38s linear infinite;} +.cosmos::after{content:'';position:absolute;inset:-10%; + background: + radial-gradient(circle at 18% 22%, rgba(var(--accent-rgb),.38) 0%, transparent 35%), + radial-gradient(circle at 78% 18%, rgba(var(--accent-rgb),.32) 0%, transparent 32%), + radial-gradient(circle at 30% 80%, rgba(var(--accent-rgb),.30) 0%, transparent 38%), + radial-gradient(circle at 84% 82%, rgba(var(--accent-rgb),.34) 0%, transparent 36%), + radial-gradient(circle at 50% 50%, rgba(var(--accent-rgb),.14) 0%, transparent 50%); + filter:blur(50px);animation:auroraDrift 22s ease-in-out infinite alternate;} +.stars{position:fixed;inset:0;z-index:-1;pointer-events:none} +.stars::before,.stars::after{content:'';position:absolute;inset:0;background-repeat:repeat} +.stars::before{background-image: + radial-gradient(1.5px 1.5px at 12px 18px, rgba(var(--text-rgb),.9), transparent 60%), + radial-gradient(1px 1px at 47px 92px, rgba(var(--accent-rgb),.85), transparent 60%), + radial-gradient(1.2px 1.2px at 110px 40px, rgba(var(--text-rgb),.75), transparent 60%), + radial-gradient(1px 1px at 165px 130px, rgba(var(--accent-rgb),.7), transparent 60%); + background-size:200px 200px;animation:auroraTwinkleA 4.5s ease-in-out infinite;} +.stars::after{background-image: + radial-gradient(1px 1px at 30px 60px, rgba(var(--accent-rgb),.8), transparent 60%), + radial-gradient(1.4px 1.4px at 88px 22px, rgba(var(--text-rgb),.7), transparent 60%), + radial-gradient(1px 1px at 140px 100px, rgba(var(--accent-rgb),.85), transparent 60%), + radial-gradient(1.2px 1.2px at 195px 70px, rgba(var(--text-rgb),.6), transparent 60%); + background-size:240px 240px;background-position:80px 50px;animation:auroraTwinkleB 6.5s ease-in-out infinite;} +@keyframes auroraSpin{to{transform:rotate(360deg)}} +@keyframes auroraDrift{0%{transform:translate(0,0) scale(1);opacity:.85}50%{transform:translate(-3%,4%) scale(1.08);opacity:1}100%{transform:translate(2%,-3%) scale(.95);opacity:.9}} +@keyframes auroraTwinkleA{0%,100%{opacity:.55}50%{opacity:1}} +@keyframes auroraTwinkleB{0%,100%{opacity:1}50%{opacity:.45}} + +/* ============ shared ============ */ +.grad{background:linear-gradient(100deg,#7ae9ff,#00d4ff 55%,#0aa6d6); + -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent} +.wrap{max-width:var(--maxw);margin:0 auto;padding:0 clamp(18px,5vw,48px)} +section{position:relative;z-index:1} +.eyebrow{font-family:var(--font-mono);font-size:.74rem;letter-spacing:.22em;text-transform:uppercase;color:var(--accent);margin-bottom:16px;display:block} +h2{font-size:clamp(2rem,4.6vw,3.2rem);line-height:1.08;letter-spacing:-.02em;font-weight:800} +.lead{color:var(--text-secondary);font-size:1.08rem;max-width:54ch;margin-top:18px;font-weight:400} +.pad{padding:clamp(80px,12vh,140px) 0} +.glass{background:var(--card-bg);border:1px solid var(--card-border);box-shadow:var(--card-shadow); + backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)} + +/* ============ TOPBAR — grafted from the dashboard (topbar.css) ============ */ +.topbar{display:flex;justify-content:space-between;align-items:center;padding:0 24px;height:60px; + position:fixed;top:0;left:0;right:0;z-index:1000;background:var(--topbar-bg); + border-bottom:1px solid var(--border);backdrop-filter:blur(12px) saturate(140%);-webkit-backdrop-filter:blur(12px) saturate(140%)} +.mobile-menu-toggle{display:none;background:none;border:none;color:var(--text-primary);cursor:pointer;padding:6px;margin-right:4px} +.topbar-left{display:flex;align-items:center;flex:0 0 auto} +.libreportal-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--text-primary);font-weight:700;font-size:1.05rem} +.libreportal-logo img{width:32px;height:32px} +.libreportal-logo b{color:var(--accent);font-weight:700} +.mobile-drawer{display:flex;align-items:center;flex:1;justify-content:space-between;gap:12px;min-width:0;margin-left:24px} +.topbar-nav{display:flex;gap:8px;align-items:center} +.topbar-nav .nav-item{background:rgba(var(--text-rgb),.10);border:1px solid rgba(var(--text-rgb),.20);border-radius:8px; + padding:10px 16px;font-size:14px;font-weight:500;color:var(--text-muted);text-decoration:none;display:flex;align-items:center;gap:8px; + transition:all .2s ease;cursor:pointer;min-height:42px;white-space:nowrap} +.topbar-nav .nav-item:hover{background:rgba(var(--text-rgb),.20);transform:translateY(-1px);color:var(--text-primary)} +.topbar-nav .nav-item.nav-active{background:var(--accent);color:var(--text-primary);border-color:var(--accent)} +.topbar-nav .nav-item.nav-active:hover{background:var(--accent-hover);border-color:var(--accent-hover)} +.topbar-nav .nav-item svg{width:16px;height:16px} +.topbar-controls{display:flex;align-items:center;gap:12px} +.tb-ghost{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border:1px solid var(--border);border-radius:8px; + background:rgba(var(--text-rgb),.05);color:var(--text-secondary);text-decoration:none;font-size:.85rem;font-weight:600;transition:all .2s} +.tb-ghost:hover{background:rgba(var(--text-rgb),.12);color:var(--text-primary)} +.tb-cta{display:inline-flex;align-items:center;gap:6px;padding:8px 18px;border-radius:8px;background:var(--accent); + color:var(--text-on-accent);text-decoration:none;font-size:.85rem;font-weight:700;transition:all .2s} +.tb-cta:hover{background:#22dbff;transform:translateY(-1px);box-shadow:0 8px 22px -8px rgba(var(--accent-rgb),.6)} +@media(max-width:768px){ + .topbar{padding:0 12px;gap:8px;justify-content:flex-start} + .mobile-menu-toggle{display:flex;align-items:center} + .mobile-drawer{position:fixed;top:60px;left:0;width:100vw;height:calc(100vh - 60px);flex-direction:column;align-items:stretch; + justify-content:flex-start;gap:0;padding:16px;margin-left:0;background:var(--surface-bg-solid);border-right:1px solid var(--border); + box-shadow:6px 0 24px rgba(0,0,0,.35);overflow-y:auto;transform:translateX(-100%);transition:transform .3s ease;z-index:101} + .mobile-drawer.mobile-open{transform:translateX(0)} + .mobile-drawer .topbar-nav{flex-direction:column;align-items:stretch;gap:6px;width:100%} + .mobile-drawer .topbar-nav .nav-item{width:100%;justify-content:flex-start} + .mobile-drawer .topbar-controls{flex-direction:column;align-items:stretch;gap:8px;width:100%;margin-top:auto;padding-top:16px; + border-top:1px solid rgba(var(--text-rgb),.12)} + .mobile-drawer .topbar-controls a{width:100%;justify-content:center} +} + +/* ============ hero ============ */ +header.hero{min-height:100vh;display:flex;flex-direction:column;justify-content:center;align-items:center; + text-align:center;padding:140px clamp(18px,5vw,48px) 90px;position:relative} +.badge{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:.72rem; + letter-spacing:.14em;text-transform:uppercase;color:var(--accent);padding:7px 15px;border-radius:999px; + border:1px solid rgba(var(--accent-rgb),.35);background:var(--accent-soft);margin-bottom:30px} +.badge .dot{width:6px;height:6px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent);animation:pulse 2.4s ease-in-out infinite} +@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}} +.hero-logo{width:64px;height:64px;margin-bottom:22px;filter:drop-shadow(0 6px 24px rgba(var(--accent-rgb),.45))} +h1{font-size:clamp(2.6rem,7vw,5.2rem);line-height:1.03;letter-spacing:-.03em;font-weight:800;max-width:15ch} +.sub{margin:26px auto 0;max-width:50ch;font-size:clamp(1.02rem,2vw,1.2rem);color:var(--text-secondary);font-weight:400} + +/* install portal */ +.portal{margin:46px auto 0;width:min(660px,100%);position:relative} +.portal::before{content:"";position:absolute;inset:-26px;border-radius:28px;z-index:-1; + background:radial-gradient(60% 80% at 50% 50%, rgba(var(--accent-rgb),.5), transparent 70%);filter:blur(22px);animation:breathe 6s ease-in-out infinite} +@keyframes breathe{0%,100%{opacity:.45;transform:scale(.98)}50%{opacity:.8;transform:scale(1.03)}} +.term{border:1px solid var(--card-border);border-radius:16px;overflow:hidden; + background:linear-gradient(155deg, rgba(255,255,255,.10) 0%, rgba(0,212,255,.06) 100%); + backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);box-shadow:var(--card-shadow-hover)} +.term-bar{display:flex;align-items:center;gap:7px;padding:12px 16px;border-bottom:1px solid var(--border-subtle);background:var(--console-bg)} +.term-bar i{width:11px;height:11px;border-radius:50%;display:block} +.term-bar i:nth-child(1){background:#ff6b5e}.term-bar i:nth-child(2){background:#ffc107}.term-bar i:nth-child(3){background:var(--accent)} +.term-bar em{margin-left:auto;font-family:var(--font-mono);font-size:.72rem;color:var(--text-faint);font-style:normal} +.term-body{display:flex;align-items:center;gap:14px;padding:22px 20px} +.term-body code{font-family:var(--font-mono);font-size:clamp(.82rem,2.3vw,1.02rem);color:var(--text-primary); + white-space:nowrap;overflow-x:auto;flex:1;text-align:left;scrollbar-width:none} +.term-body code::-webkit-scrollbar{display:none} +.term-body code .p{color:var(--accent)} +.term-body code .u{color:#e6f3ff} +.copy{flex:0 0 auto;display:inline-flex;align-items:center;gap:7px;font-family:var(--font-sans);font-weight:700; + font-size:.84rem;color:var(--text-on-accent);background:var(--accent);border:none;border-radius:9px;padding:9px 15px;cursor:pointer; + transition:transform .15s,background .25s,box-shadow .25s} +.copy:hover{transform:translateY(-1px);background:#22dbff;box-shadow:0 8px 22px -8px rgba(var(--accent-rgb),.7)} +.copy.done{background:#28a745;color:#fff} +.portal-foot{display:flex;align-items:center;justify-content:center;gap:16px;margin-top:16px;flex-wrap:wrap;font-size:.86rem;color:var(--text-faint)} +.portal-foot a{color:var(--text-secondary);text-decoration:none;border-bottom:1px dashed rgba(255,255,255,.3);transition:color .2s,border-color .2s} +.portal-foot a:hover{color:var(--accent);border-color:var(--accent)} +.hint{font-family:var(--font-mono);font-size:.78rem} +.scroll-cue{position:absolute;bottom:28px;left:50%;transform:translateX(-50%);color:var(--text-faint); + font-family:var(--font-mono);font-size:.66rem;letter-spacing:.2em;text-transform:uppercase; + display:flex;flex-direction:column;align-items:center;gap:8px;animation:bob 2.6s ease-in-out infinite} +.scroll-cue::after{content:"";width:1px;height:34px;background:linear-gradient(var(--text-faint),transparent)} +@keyframes bob{0%,100%{transform:translateX(-50%) translateY(0)}50%{transform:translateX(-50%) translateY(8px)}} + +/* ============ features ============ */ +.feat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:54px} +.card{border-radius:16px;padding:28px 26px;position:relative;overflow:hidden;transition:transform .4s var(--ease),border-color .4s,box-shadow .4s} +.card:hover{transform:translateY(-6px);border-color:rgba(var(--accent-rgb),.45);box-shadow:var(--card-shadow-hover)} +.card .ico{width:44px;height:44px;border-radius:12px;display:grid;place-items:center;margin-bottom:18px; + background:var(--accent-soft);border:1px solid rgba(var(--accent-rgb),.30)} +.card .ico svg{width:22px;height:22px;stroke:var(--accent);fill:none;stroke-width:1.6} +.card h3{font-size:1.24rem;margin-bottom:8px;font-weight:700;letter-spacing:-.01em} +.card p{color:var(--text-secondary);font-size:.96rem} + +/* ============ apps (data-driven) ============ */ +.apps-section{text-align:center} +.app-filters{display:flex;flex-wrap:wrap;justify-content:center;gap:10px;margin-top:40px} +.chip{font-family:var(--font-mono);font-size:.76rem;letter-spacing:.04em;padding:8px 15px;border-radius:999px; + border:1px solid var(--border);background:rgba(255,255,255,.05);color:var(--text-secondary);cursor:pointer;transition:all .2s} +.chip:hover{color:var(--text-primary);border-color:rgba(var(--accent-rgb),.45)} +.chip.active{background:var(--accent);color:var(--text-on-accent);border-color:var(--accent);font-weight:600} +.chip .n{opacity:.55;margin-left:6px} +.app-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(248px,1fr));gap:16px;margin-top:34px;text-align:left} +.app-card{border-radius:14px;padding:20px;display:flex;flex-direction:column;gap:11px;transition:transform .3s var(--ease),border-color .3s,box-shadow .3s} +.app-card.hidden{display:none} +.app-card:hover{transform:translateY(-4px);border-color:rgba(var(--accent-rgb),.4);box-shadow:var(--card-shadow-hover)} +.app-card-head{display:flex;align-items:center;gap:12px} +.app-ico{width:42px;height:42px;border-radius:10px;background:rgba(255,255,255,.06);padding:6px;object-fit:contain;border:1px solid var(--border-subtle);flex:0 0 auto} +.app-card h3{font-size:1.06rem;font-weight:700;line-height:1.2} +.app-cat{font-family:var(--font-mono);font-size:.64rem;letter-spacing:.1em;text-transform:uppercase;color:var(--accent)} +.app-card p{font-size:.9rem;color:var(--text-secondary);margin:0} +.app-count{margin-top:26px;color:var(--text-faint);font-size:.85rem;font-family:var(--font-mono)} + +/* ============ connect / courier ============ */ +.connect{display:grid;grid-template-columns:1.05fr .95fr;gap:60px;align-items:center} +.pill{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:.72rem; + letter-spacing:.1em;text-transform:uppercase;color:var(--text-secondary);padding:7px 14px;border-radius:999px; + border:1px solid var(--border);background:rgba(255,255,255,.04);margin-top:24px} +.connect ul{list-style:none;margin-top:26px;display:flex;flex-direction:column;gap:15px} +.connect li{display:flex;gap:13px;color:var(--text-secondary);font-size:1rem} +.connect li svg{flex:0 0 auto;width:21px;height:21px;stroke:var(--accent);fill:none;stroke-width:1.6;margin-top:3px} +.connect li b{color:var(--text-primary);font-weight:700} +.vault{position:relative;aspect-ratio:1;display:grid;place-items:center} +.vault svg{width:min(360px,82%);height:auto;overflow:visible} +.orbit{transform-origin:center;animation:spin 26s linear infinite} +@keyframes spin{to{transform:rotate(360deg)}} + +/* ============ promise ============ */ +.promise{text-align:center} +.pledges{display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin-top:52px} +.pledge{border-radius:14px;padding:28px 18px} +.pledge .big{font-size:2.4rem;font-weight:800;margin-bottom:8px;line-height:1} +.pledge p{font-size:.92rem;color:var(--text-secondary)} +.promise .link{display:inline-block;margin-top:40px;color:var(--accent);text-decoration:none;font-weight:700; + border-bottom:1px solid rgba(var(--accent-rgb),.45);padding-bottom:2px;transition:color .2s,border-color .2s} +.promise .link:hover{color:#fff;border-color:#fff} + +/* ============ final ============ */ +.final{text-align:center;padding:clamp(90px,14vh,160px) 0} +.final h2{max-width:18ch;margin-inline:auto} +.final .portal{margin-top:42px} + +/* ============ footer ============ */ +footer{border-top:1px solid var(--border-subtle);padding:54px 0 70px;margin-top:40px;background:rgba(10,16,32,.55);backdrop-filter:blur(8px)} +.foot-grid{display:flex;justify-content:space-between;gap:40px;flex-wrap:wrap;align-items:flex-start} +.foot-grid .libreportal-logo{margin-bottom:14px} +.foot-col h4{font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;color:var(--text-faint);margin-bottom:14px;font-family:var(--font-mono)} +.foot-col a{display:block;color:var(--text-secondary);text-decoration:none;font-size:.95rem;margin-bottom:9px;transition:color .2s} +.foot-col a:hover{color:var(--accent)} +.colophon{margin-top:46px;padding-top:26px;border-top:1px solid var(--border-subtle); + display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;color:var(--text-faint);font-size:.86rem} +.colophon a{color:var(--text-secondary);text-decoration:none} +.colophon a:hover{color:var(--accent)} + +/* ============ reveal ============ */ +.reveal{opacity:0;transform:translateY(28px);transition:opacity .9s var(--ease),transform .9s var(--ease)} +.reveal.in{opacity:1;transform:none} +.stagger>*{opacity:0;transform:translateY(20px);transition:opacity .7s var(--ease),transform .7s var(--ease)} +.stagger.in>*{opacity:1;transform:none} +.stagger.in>*:nth-child(2){transition-delay:.07s}.stagger.in>*:nth-child(3){transition-delay:.14s} +.stagger.in>*:nth-child(4){transition-delay:.21s}.stagger.in>*:nth-child(5){transition-delay:.28s}.stagger.in>*:nth-child(6){transition-delay:.35s} +.h-anim{opacity:0;transform:translateY(24px);animation:rise .95s var(--ease) forwards} +.d1{animation-delay:.05s}.d2{animation-delay:.16s}.d3{animation-delay:.28s}.d4{animation-delay:.4s}.d5{animation-delay:.52s} +@keyframes rise{to{opacity:1;transform:none}} + +/* ============ responsive ============ */ +@media(max-width:860px){ + .feat-grid{grid-template-columns:1fr 1fr} + .connect{grid-template-columns:1fr;gap:36px} + .vault{order:-1;max-width:320px;margin-inline:auto} + .pledges{grid-template-columns:1fr 1fr} +} +@media(max-width:540px){ + .feat-grid{grid-template-columns:1fr} + .term-body{flex-direction:column;align-items:stretch} + .copy{justify-content:center} +} +@media(prefers-reduced-motion:reduce){ + *{animation:none!important;transition:none!important} + .reveal,.stagger>*,.h-anim{opacity:1;transform:none} +} diff --git a/site/dist/index.html b/site/dist/index.html new file mode 100644 index 0000000..ff5a8cf --- /dev/null +++ b/site/dist/index.html @@ -0,0 +1,586 @@ + + +
+ + +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.
+ +bash <(curl -fsSL https://get.libreportal.org) init
+
+ No yak-shaving. LibrePortal wires up the boring, fiddly infrastructure so you can run the good stuff.
+ +Nextcloud, Vaultwarden, Jellyfin, Gitea and dozens more — picked from a menu, deployed clean.
+A reverse proxy with automatic Let's Encrypt certificates. HTTPS everywhere, configured for you.
+Containers run without root and with sane security defaults — CrowdSec on guard out of the box.
+Install, configure, back up and monitor everything from a clean web UI. No SSH archaeology.
+Send any app's traffic through gluetun. Keep the things that should be private, private.
+Nothing phones home. No accounts required to self-host. The software is yours, entirely.
+Pulled straight from the repo — 31 apps you can light up from the dashboard, and switch off just as easily.
+ +DNS based Ad Blocking
+Authentication & SSO
+Wiki/Knowledge Base
+Intrusion Prevention
+Dashboard Tool
+Project Management
+Git Repository Management
+VPN Container Router
+Metrics Visualizer
+WireGuard VPN Controller
+YouTube Frontend
+IP Information
+Media Server
+Video Conferencing
+WebUI Dashboard
+Bookmark Manager
+Social Network
+Household Money Management
+Self-hosted Cloud Storage
+Local AI Model Hosting
+Collaborative Office Suite
+Cloud Storage
+DNS-based Ad Blocking
+Monitoring and Alerting
+Search Engine
+Network Performance Monitoring
+Reverse Proxy
+Notes & Knowledge Management
+DNS Resolver
+Password Manager
+VPN Server
+31 apps
+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 sealed box.
+In plain language, so you can hold us to it.
+Every feature, free to self-host. Forever. No crippled edition.
Trackers. No telemetry, no phoning home, no accounts to run it.
Feature paywalls. You never pay to unlock — only for convenience.
What's open stays open. No rug-pulls, ever. AGPLv3.
bash <(curl -fsSL https://get.libreportal.org) init
+
+