diff --git a/.ai/rules/index.md b/.ai/rules/index.md
index 999bf21..8813282 100644
--- a/.ai/rules/index.md
+++ b/.ai/rules/index.md
@@ -7,3 +7,5 @@ Before planning or editing, find the row whose globs match the file's path and r
| config/livewire-material.php | .ai/rules/config.md |
| resources/css/material-scheme.* | .ai/rules/css.md |
| resources/views/livewire/share-download.blade.php | .ai/rules/livewire.md |
+| tests/Screenshots/** | .ai/rules/screenshots.md |
+| website/** | .ai/rules/website.md |
diff --git a/.ai/rules/screenshots.md b/.ai/rules/screenshots.md
new file mode 100644
index 0000000..41e3285
--- /dev/null
+++ b/.ai/rules/screenshots.md
@@ -0,0 +1,9 @@
+---
+paths:
+ - 'tests/Screenshots/**'
+---
+
+# Screenshots
+
+## Screenshots come from composer screenshots, before a release
+Run `composer screenshots` whenever the interface changes and before a release; it builds assets and runs tests/Screenshots (not part of any test suite or CI), publishing WebP files to website/img/screenshots. Demo data (DemoData) and the clock are fixed so runs are reproducible. Traps: Pest only starts its browser for a test whose body calls `visit(` after whitespace; Livewire's temporary-upload cleanup must stay off under the frozen clock or it deletes the selected files; the in-process server's random port is shown as https://files.example.com and the QR redrawn for it; upload_max_filesize/post_max_size are set to 4G by the script so the admin settings hint does not show the machine's PHP limit.
diff --git a/.dockerignore b/.dockerignore
index f6ce8d5..784bd04 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -25,6 +25,9 @@ Dockerfile
# Testing
tests
+
+# Website (published separately)
+website
phpunit.xml
.phpunit.cache
diff --git a/.gitignore b/.gitignore
index 90d3d5f..446c387 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,5 @@ yarn-error.log
**/caddy
frankenphp
frankenphp-worker.php
+
+/tests/Browser/Screenshots
diff --git a/composer.json b/composer.json
index f714b1a..a83b47c 100644
--- a/composer.json
+++ b/composer.json
@@ -84,6 +84,11 @@
],
"pre-package-uninstall": [
"Illuminate\\Foundation\\ComposerScripts::prePackageUninstall"
+ ],
+ "screenshots": [
+ "Composer\\Config::disableProcessTimeout",
+ "npm run build",
+ "@php -d upload_max_filesize=4G -d post_max_size=4G vendor/bin/pest tests/Screenshots"
]
},
"extra": {
diff --git a/docs/plans/screenshots-and-website.md b/docs/plans/screenshots-and-website.md
new file mode 100644
index 0000000..53a0c8c
--- /dev/null
+++ b/docs/plans/screenshots-and-website.md
@@ -0,0 +1,255 @@
+# Screenshots and the SealShare website
+
+## Goal
+
+Two things that feed each other. First, one command — `composer screenshots` — produces every
+screenshot of SealShare from fixed demo data, desktop and phone, light and dark, ready for the web.
+Second, a static website at **sealshare.nonameweb.ch**, made the way mailifysms.nonameweb.ch is:
+hand-written HTML and CSS in `website/`, uploaded by hand. The site presents SealShare as what it
+is — software a company installs to run **its own upload platform**, so it exchanges files with
+customers securely without relying on an outside service — shows the screenshots, compares
+SealShare with hosted transfer services and with other self-hosted tools, and tells how to install
+it. It goes live with 2.0.0. The README gets a few of the same screenshots, and its encryption
+wording is corrected.
+
+## Context
+
+**MailifySMS, the model** (`../MailifySMS`):
+
+- `website/` holds `index.html`, `privacy_policy.html`, `terms_and_conditions.html`,
+ `css/theme.css` (a palette sampled from the app's screenshots), `css/device-frame.css` (a phone
+ bezel shared with the store canvases), self-hosted Poppins (`fonts/`, OFL) and `img/`
+ (`icon.png`, `hero.jpg`, `screenshots/{light,dark}/NN-name.png` at 540px).
+- `index.html`: sticky nav with a phone toggle, hero, "How it works", "Key features", a screenshot
+ gallery with a Light/Dark switch (`data-light`/`data-dark` on each `
`), FAQ accordion,
+ contact card (`surtic86@gmail.com`), footer (quick links, legal). Plausible:
+ `