landing page

This commit is contained in:
Trevi Awater
2026-03-31 12:46:39 +02:00
parent 2c68418057
commit e2e5f7d3b1
27 changed files with 9560 additions and 0 deletions

40
.github/workflows/deploy-landing.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Deploy Landing to Cloudflare Pages
on:
push:
branches: ["main"]
paths:
- "landing/**"
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: landing/package-lock.json
- name: Install dependencies
working-directory: landing
run: npm ci
- name: Build
working-directory: landing
run: npm run build
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy dist --project-name=pockegraph-app
workingDirectory: landing

24
landing/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store
# jetbrains setting folder
.idea/

43
landing/README.md Normal file
View File

@@ -0,0 +1,43 @@
# Astro Starter Kit: Minimal
```sh
npm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```text
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:4321` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

8
landing/astro.config.mjs Normal file
View File

@@ -0,0 +1,8 @@
// @ts-check
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';
export default defineConfig({
site: 'https://pocketgraph.app',
integrations: [sitemap()],
});

4850
landing/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

18
landing/package.json Normal file
View File

@@ -0,0 +1,18 @@
{
"name": "pocketgraph",
"type": "module",
"version": "0.0.1",
"engines": {
"node": ">=22.12.0"
},
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"@astrojs/sitemap": "^3.7.2",
"astro": "^6.1.2"
}
}

2973
landing/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
landing/public/demo.mp4 Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
landing/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

BIN
landing/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
landing/public/og.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -0,0 +1,21 @@
{
"name": "PocketGraph",
"short_name": "PocketGraph",
"icons": [
{
"src": "/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -0,0 +1,77 @@
---
const APP_STORE_URL = "https://apps.apple.com/nl/app/pocketgraph/id1604368926";
---
<footer>
<div class="section-inner footer-inner">
<div class="footer-brand">
<img src="/logo.png" alt="PocketGraph" class="footer-icon" />
<span class="footer-name">PocketGraph</span>
</div>
<div class="footer-links">
<a href={APP_STORE_URL} target="_blank" rel="noopener">App Store</a>
<a href="https://resoftware.nl/contact" target="_blank" rel="noopener">Support</a>
<a href="/privacy">Privacy Policy</a>
</div>
<p class="footer-copy mono">
&copy; {new Date().getFullYear()} Re: Software B.V. &middot; Neo4j&reg; is a registered trademark of Neo4j, Inc. &middot; Memgraph&reg; is a registered trademark of Memgraph Ltd.
</p>
</div>
</footer>
<style>
footer {
position: relative;
z-index: 1;
border-top: 1px solid rgba(226, 226, 226, 0.1);
padding: 2.5rem 0;
}
.section-inner {
max-width: 1120px;
margin: 0 auto;
padding: 0 2rem;
}
.footer-inner {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 1.5rem;
}
.footer-brand {
display: flex;
align-items: center;
gap: 10px;
}
.footer-icon {
width: 40px; height: 40px;
}
.footer-name {
font-weight: 700;
font-size: 0.95rem;
color: #e8e8e8;
}
.footer-links {
display: flex;
gap: 2rem;
}
.footer-links a {
font-size: 0.85rem;
color: #9a9a9a;
text-decoration: none;
transition: color 0.2s;
}
.footer-links a:hover { color: #e2e2e2; }
.footer-copy {
font-size: 0.7rem;
color: #333333;
letter-spacing: 0.02em;
width: 100%;
text-align: right;
}
.mono { font-family: 'IBM Plex Mono', monospace; }
@media (max-width: 900px) {
.footer-inner { flex-direction: column; align-items: flex-start; }
.footer-copy { text-align: left; }
}
</style>

View File

@@ -0,0 +1,34 @@
---
interface Props {
title?: string;
description?: string;
canonical?: string;
}
const {
title = 'PocketGraph — Neo4j & Memgraph Client',
description = 'Connect to your Neo4j or Memgraph database from iPhone and iPad. Full Cypher editor, graph visualization, and Bolt protocol — in your pocket.',
canonical,
} = Astro.props;
const siteUrl = 'https://pocketgraph.app';
const ogImage = `${siteUrl}/og.png`;
const url = canonical ? `${siteUrl}${canonical}` : siteUrl;
---
<title>{title}</title>
<meta name="description" content={description} />
<link rel="canonical" href={url} />
<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:url" content={url} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:site_name" content="PocketGraph" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={ogImage} />

View File

@@ -0,0 +1,78 @@
---
const APP_STORE_URL = "https://apps.apple.com/nl/app/pocketgraph/id1604368926";
---
<nav id="nav">
<div class="nav-inner">
<a href="/" class="nav-logo">
<img src="/logo.png" alt="PocketGraph icon" class="nav-icon" />
<span>PocketGraph</span>
</a>
<a href={APP_STORE_URL} class="nav-dl" target="_blank" rel="noopener">
Download
</a>
</div>
</nav>
<style>
#nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
padding: 0 2rem;
height: 80px;
display: flex;
align-items: center;
transition: background 0.3s, border-color 0.3s;
border-bottom: 1px solid transparent;
}
#nav.scrolled {
background: rgba(12, 12, 12, 0.9);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-color: rgba(226, 226, 226, 0.1);
}
.nav-inner {
max-width: 1120px;
width: 100%;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.nav-logo {
display: flex;
align-items: center;
gap: 14px;
text-decoration: none;
color: #e8e8e8;
font-weight: 700;
font-size: 1.25rem;
letter-spacing: -0.02em;
margin-left: 0.75rem;
}
.nav-icon {
width: 56px; height: 56px;
display: block;
}
.nav-dl {
font-family: 'Syne', sans-serif;
font-weight: 600;
font-size: 0.85rem;
letter-spacing: 0.04em;
text-transform: uppercase;
text-decoration: none;
color: #e2e2e2;
border: 1px solid #e2e2e2;
padding: 0.45rem 1.1rem;
border-radius: 2px;
transition: background 0.2s, color 0.2s;
}
.nav-dl:hover { background: #e2e2e2; color: #0c0c0c; }
</style>
<script is:inline>
const nav = document.getElementById('nav');
window.addEventListener('scroll', () => {
nav?.classList.toggle('scrolled', window.scrollY > 20);
}, { passive: true });
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,233 @@
---
import Nav from '../components/Nav.astro';
import Meta from '../components/Meta.astro';
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<Meta
title="Privacy Policy — PocketGraph"
description="Privacy policy for PocketGraph, the Neo4j & Memgraph client for iPhone and iPad."
canonical="/privacy"
/>
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/png" sizes="96x96" href="/favicon-96x96.png" />
<link rel="icon" href="/favicon.ico" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/site.webmanifest" />
<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=Syne:wght@400;500;600;700;800&family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,500;1,300&display=swap" rel="stylesheet" />
<script src="https://rybbit.resoftware.work/api/script.js" data-site-id="d2f58ef3c722" defer></script>
</head>
<body>
<Nav />
<main>
<div class="content">
<p class="mono label">// legal</p>
<h1>Privacy Policy</h1>
<p class="meta mono">Re: Software B.V. &middot; Last updated: March 2026</p>
<section>
<h2>Overview</h2>
<p>PocketGraph is a graph database client for iPhone and iPad. We take your privacy seriously. This policy explains what data we collect, why, and how it is used.</p>
<p>The short version: <strong>PocketGraph does not collect, store, or transmit any of your database credentials or query data.</strong> All connections are made directly from your device to your database.</p>
</section>
<section>
<h2>Data We Do Not Collect</h2>
<ul>
<li>Database hostnames, ports, usernames, or passwords</li>
<li>Cypher queries you write or execute</li>
<li>Query results or any data stored in your database</li>
<li>Graph visualizations or schemas</li>
</ul>
<p>This data never leaves your device and is never sent to Re: Software or any third party.</p>
</section>
<section>
<h2>Data Stored on Your Device</h2>
<p>PocketGraph stores the following locally on your device only:</p>
<ul>
<li>Connection profiles (host, port, username) — stored in the iOS Keychain</li>
<li>Query history — stored locally, never synced externally</li>
<li>App preferences and settings</li>
</ul>
<p>This data is protected by iOS system-level encryption and is not accessible to Re: Software.</p>
</section>
<section>
<h2>Analytics</h2>
<p>This website (pocketgraph.app) uses <a href="https://rybbit.io" target="_blank" rel="noopener">Rybbit</a>, a privacy-friendly analytics tool, to collect anonymised page view data. No cookies are set, no personal data is collected, and no cross-site tracking occurs.</p>
<p>The PocketGraph app itself does not include any analytics SDK.</p>
</section>
<section>
<h2>Network Connections</h2>
<p>PocketGraph connects directly from your device to your Neo4j or Memgraph database using the Bolt protocol over SSL/TLS. No traffic is routed through Re: Software servers. Your database credentials are used solely to authenticate with your own database.</p>
</section>
<section>
<h2>Purchases</h2>
<p>PocketGraph is sold as a one-time purchase through the Apple App Store. All payment processing is handled by Apple. Re: Software does not receive or store any payment or billing information.</p>
</section>
<section>
<h2>Children</h2>
<p>PocketGraph is not directed at children under 13 and we do not knowingly collect data from children.</p>
</section>
<section>
<h2>Changes to This Policy</h2>
<p>If this policy changes materially, we will update the date at the top of this page. Continued use of the app constitutes acceptance of the updated policy.</p>
</section>
<section>
<h2>Contact</h2>
<p>Questions about this policy? Reach us at <a href="https://resoftware.nl/contact" target="_blank" rel="noopener">resoftware.nl/contact</a>.</p>
</section>
</div>
</main>
<footer>
<div class="section-inner footer-inner">
<a href="/" class="footer-back mono">&larr; Back to PocketGraph</a>
<p class="footer-copy mono">&copy; {new Date().getFullYear()} Re: Software B.V.</p>
</div>
</footer>
<style>
:root {
--bg: #0c0c0c;
--text: #e8e8e8;
--muted: #9a9a9a;
--border: rgba(226, 226, 226, 0.1);
--font-sans: 'Syne', sans-serif;
--font-mono: 'IBM Plex Mono', monospace;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--font-sans);
font-size: 16px;
line-height: 1.6;
}
/* MAIN */
main {
max-width: 1120px;
margin: 0 auto;
padding: 5rem 2rem 6rem;
}
.label {
font-size: 0.75rem;
color: var(--muted);
letter-spacing: 0.08em;
text-transform: uppercase;
margin-bottom: 1rem;
}
h1 {
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 800;
letter-spacing: -0.03em;
color: #fff;
margin-bottom: 0.5rem;
}
.meta {
font-size: 0.8rem;
color: var(--muted);
margin-bottom: 3.5rem;
}
section {
margin-bottom: 2.5rem;
padding-bottom: 2.5rem;
border-bottom: 1px solid var(--border);
}
section:last-of-type { border-bottom: none; }
h2 {
font-size: 1.1rem;
font-weight: 700;
letter-spacing: -0.02em;
color: #fff;
margin-bottom: 0.75rem;
}
p {
color: #aaaaaa;
font-size: 0.95rem;
line-height: 1.75;
margin-bottom: 0.75rem;
}
p:last-child { margin-bottom: 0; }
strong { color: var(--text); font-weight: 600; }
ul {
list-style: none;
margin: 0.5rem 0 0.75rem 0;
}
ul li {
color: #aaaaaa;
font-size: 0.95rem;
line-height: 1.75;
padding-left: 1.25rem;
position: relative;
}
ul li::before {
content: '';
position: absolute;
left: 0;
color: var(--muted);
}
a {
color: var(--text);
text-underline-offset: 3px;
}
a:hover { color: #fff; }
.mono { font-family: var(--font-mono); }
/* FOOTER */
footer {
border-top: 1px solid var(--border);
padding: 2rem 0;
}
.section-inner { max-width: 1120px; margin: 0 auto; padding: 0 2rem; }
.footer-inner {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 1rem;
}
.footer-back {
font-size: 0.8rem;
color: var(--muted);
text-decoration: none;
letter-spacing: 0.02em;
transition: color 0.2s;
}
.footer-back:hover { color: var(--text); }
.footer-copy {
font-size: 0.7rem;
color: #333333;
letter-spacing: 0.02em;
}
</style>
</body>
</html>

5
landing/tsconfig.json Normal file
View File

@@ -0,0 +1,5 @@
{
"extends": "astro/tsconfigs/strict",
"include": [".astro/types.d.ts", "**/*"],
"exclude": ["dist"]
}