diff --git a/README.md b/README.md
index d5d03c6..6e3a04b 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,23 @@
> **Self-hosted pantry management system** โ Track your food inventory, scan barcodes, get AI-powered recipe suggestions, and reduce waste.
-๐ **Website:** [evershelfproject.dadaloop.it](https://evershelfproject.dadaloop.it/)
+---
+
+
+
+### ๐ Try the live demo โ no installation required!
+
+**[โถ Open Live Demo](https://evershelfproject.dadaloop.it/demo)**
+ ยท
+[๐ Project Website](https://evershelfproject.dadaloop.it/)
+ ยท
+[๐ Wiki](docs/wiki/Home.md)
+
+*The demo runs with mock pantry data. AI features are fully enabled. All write operations are safely sandboxed.*
+
+
+
+---
[](LICENSE)
[](https://www.php.net/)
diff --git a/docs/wiki/API-Reference.md b/docs/wiki/API-Reference.md
new file mode 100644
index 0000000..27a63e2
--- /dev/null
+++ b/docs/wiki/API-Reference.md
@@ -0,0 +1,332 @@
+# ๐ API Reference
+
+EverShelf exposes a single PHP endpoint: **`api/index.php`**. All actions are selected via the `action` query parameter.
+
+> **Full OpenAPI 3.1 spec:** [`docs/openapi.yaml`](https://github.com/dadaloop82/EverShelf/blob/main/docs/openapi.yaml)
+
+---
+
+## Base URL
+
+```
+https://your-server/api/index.php?action=ACTION_NAME
+```
+
+GET requests pass parameters as query params; POST requests send JSON in the body.
+
+---
+
+## Rate Limits
+
+| Tier | Limit | Applies to |
+|------|-------|-----------|
+| Standard | 120 req/min | All general endpoints |
+| AI | 15 req/min | `gemini_*`, `generate_recipe*` |
+| Strict | 5 req/min | `report_error` |
+
+Exceeded limits return HTTP 429 with `{"error": "rate_limit_exceeded"}`.
+
+---
+
+## Products
+
+### `search_barcode` โ GET
+Search for a product in the local database by barcode.
+
+| Param | Type | Description |
+|-------|------|-------------|
+| `barcode` | string | EAN/UPC barcode |
+
+### `lookup_barcode` โ GET
+Look up a barcode on Open Food Facts (external call).
+
+| Param | Type | Description |
+|-------|------|-------------|
+| `barcode` | string | EAN/UPC barcode |
+
+### `product_save` โ POST
+Create or update a product. Pass `id` to update.
+
+```json
+{
+ "id": 42,
+ "name": "Pasta Barilla",
+ "brand": "Barilla",
+ "category": "pasta",
+ "unit": "g",
+ "default_quantity": 500,
+ "barcode": "8076800105988"
+}
+```
+
+### `product_get` โ GET
+Get product details by `id`.
+
+### `product_delete` โ POST
+Delete a product by `id`.
+
+### `products_list` โ GET
+List all products.
+
+### `products_search` โ GET
+Search products by name (`?q=pasta`).
+
+---
+
+## Inventory
+
+### `inventory_list` โ GET
+List all inventory items with product details, grouped.
+
+**Response:**
+```json
+{
+ "inventory": [
+ {
+ "id": 1,
+ "product_id": 42,
+ "name": "Pasta Barilla",
+ "quantity": 2,
+ "unit": "pz",
+ "location": "dispensa",
+ "expiry_date": "2027-03-01",
+ "opened_at": null,
+ "vacuum_sealed": 0
+ }
+ ]
+}
+```
+
+### `inventory_add` โ POST
+Add a product to inventory.
+
+```json
+{
+ "product_id": 42,
+ "quantity": 3,
+ "location": "dispensa",
+ "expiry_date": "2027-03-01",
+ "vacuum_sealed": false
+}
+```
+
+**Locations:** `dispensa`, `frigo`, `freezer`, `altro`
+
+### `inventory_use` โ POST
+Consume inventory. Set `use_all: true` to consume all stock at a location.
+
+```json
+{
+ "product_id": 42,
+ "quantity": 1,
+ "location": "dispensa"
+}
+```
+
+```json
+{
+ "product_id": 42,
+ "use_all": true,
+ "location": "__all__",
+ "notes": "Buttato"
+}
+```
+
+### `inventory_update` โ POST
+Update an inventory entry by `id`.
+
+### `inventory_delete` โ POST
+Remove an inventory entry by `id`.
+
+### `inventory_summary` โ GET
+Returns item counts per location.
+
+```json
+{
+ "dispensa": 12,
+ "frigo": 5,
+ "freezer": 8
+}
+```
+
+---
+
+## Transactions (Log)
+
+### `transactions_list` โ GET
+Returns the operation log.
+
+| Param | Type | Default | Description |
+|-------|------|---------|-------------|
+| `limit` | int | 50 | Results per page |
+| `offset` | int | 0 | Pagination offset |
+
+### `transaction_undo` โ POST
+Undo a transaction within 24 hours.
+
+```json
+{ "id": 873 }
+```
+
+**Response on success:**
+```json
+{ "success": true, "name": "Tonno all'olio d'oliva" }
+```
+
+**Error cases:**
+```json
+{ "error": "...", "already_undone": true }
+{ "error": "...", "too_old": true }
+```
+
+### `stats` โ GET
+Returns waste and consumption statistics for the last 30 days.
+
+---
+
+## AI / Gemini
+
+All AI endpoints require `GEMINI_API_KEY` to be configured. Rate limit: 15 req/min.
+
+### `gemini_expiry` โ POST
+Read an expiry date from a product photo.
+
+```json
+{ "image": "data:image/jpeg;base64,..." }
+```
+
+### `gemini_identify` โ POST
+Identify a product from a photo.
+
+```json
+{ "image": "data:image/jpeg;base64,..." }
+```
+
+### `gemini_chat` โ POST
+Chat with the AI kitchen assistant.
+
+```json
+{ "message": "Cosa posso fare con la pasta?", "history": [] }
+```
+
+### `generate_recipe` โ POST
+Generate a recipe based on current inventory.
+
+```json
+{ "persons": 2, "meal": "dinner", "preferences": {} }
+```
+
+### `generate_recipe_stream` โ POST
+Same as `generate_recipe` but streams output via Server-Sent Events.
+
+### `gemini_product_hint` โ POST
+Get AI storage location + shelf-life hint for a new product.
+
+### `gemini_shopping_enrich` โ POST
+Enrich shopping suggestions with practical tips.
+
+### `gemini_anomaly_explain` โ POST
+Get a plain-language explanation for a specific inventory anomaly.
+
+---
+
+## Shopping List (Bring!)
+
+Requires `BRING_EMAIL` and `BRING_PASSWORD` in `.env`.
+
+### `bring_list` โ GET
+Get the current Bring! shopping list.
+
+### `bring_add` โ POST
+Add items to the Bring! list.
+
+```json
+{ "items": ["Latte", "Pane"] }
+```
+
+### `bring_remove` โ POST
+Remove an item from the Bring! list.
+
+```json
+{ "name": "Latte" }
+```
+
+### `smart_shopping` โ GET
+Get smart shopping predictions based on consumption history.
+
+---
+
+## Settings
+
+### `get_settings` โ GET
+Returns current settings as **boolean flags only** (no raw key values):
+
+```json
+{
+ "gemini_key_set": true,
+ "bring_configured": false,
+ "tts_enabled": false,
+ "scale_enabled": true,
+ "demo_mode": false,
+ "settings_token_set": true
+}
+```
+
+### `save_settings` โ POST
+Update server configuration. If `SETTINGS_TOKEN` is set, requires header:
+
+```
+X-Settings-Token: your_token
+```
+
+```json
+{
+ "gemini_api_key": "...",
+ "bring_email": "...",
+ "scale_enabled": true,
+ "scale_gateway_url": "ws://127.0.0.1:8765"
+}
+```
+
+---
+
+## Error Reporting
+
+### `report_error` โ POST
+Submit an automatic error report (creates a GitHub Issue).
+
+```json
+{
+ "type": "uncaught-error",
+ "message": "...",
+ "stack": "...",
+ "context": {}
+}
+```
+
+Only creates an issue if:
+- The client is running the latest released version
+- The fingerprint hasn't been seen in the last 24 hours
+
+---
+
+## Anomaly Detection
+
+### `inventory_anomalies` โ GET
+Returns inventory rows where stored quantity significantly differs from transaction history.
+
+### `dismiss_anomaly` โ POST
+Dismiss an anomaly banner without changing inventory.
+
+---
+
+## Scale Integration
+
+### `scale_relay` (SSE) โ GET
+Relays BLE scale readings from the gateway to the browser via Server-Sent Events (avoids HTTPSโWS mixed-content issues).
+
+### `scale_ping` โ GET
+Check if the Scale Gateway is reachable.
+
+### `scale_discover` โ GET
+Scan the local LAN for a running Scale Gateway instance.
diff --git a/docs/wiki/Android-Kiosk.md b/docs/wiki/Android-Kiosk.md
new file mode 100644
index 0000000..89b73ae
--- /dev/null
+++ b/docs/wiki/Android-Kiosk.md
@@ -0,0 +1,141 @@
+# ๐บ Android Kiosk App
+
+The EverShelf Kiosk app turns any Android tablet into a dedicated, locked-down kitchen display running EverShelf full-screen.
+
+---
+
+## Download
+
+**[โฌ Download latest APK](https://github.com/dadaloop82/EverShelf/releases/latest/download/evershelf-kiosk.apk)**
+
+> Current version: **v1.5.0** โ requires Android 7.0+
+
+---
+
+## What it does
+
+- Displays the EverShelf web app in a **full-screen WebView** (no browser chrome)
+- **Locks the screen** with Android's `startLockTask` โ home, recents, and back buttons are blocked
+- Runs the **Scale Gateway** app in the background automatically on startup
+- Provides a **native TTS bridge** so Cooking Mode reads steps aloud via Android TextToSpeech
+- Auto-detects your EverShelf server on the LAN with a **smart discovery scanner**
+- Reports errors and install failures back to the developer automatically
+
+---
+
+## Setup Wizard (6 steps)
+
+The wizard runs automatically on first launch.
+
+### Step 1 โ Language
+Select the app and web interface language (Italian, English, German).
+
+### Step 2 โ Welcome
+Overview of what the wizard will configure.
+
+### Step 3 โ Permissions
+Grant camera, microphone, and storage permissions needed by the web app.
+
+The button transforms from "Concedi permessi" to **"โ
Permessi concessi โ Continua โ"** (green) once all permissions are granted.
+
+### Step 4 โ Server URL
+Enter your EverShelf server URL (e.g. `https://192.168.1.100/dispensa`).
+
+**Or tap "Rileva automaticamente"** to let the wizard scan your LAN:
+- 60 parallel threads, TCP pre-check, ports 80/443/8080/8443
+- Only scans your actual Wi-Fi/Ethernet subnet (VPN and cellular interfaces ignored)
+- Real-time feedback as hosts are tested
+
+### Step 5 โ Scale Gateway
+If you have a BLE smart scale, install and configure the Scale Gateway:
+1. Tap **"Installa Gateway"** โ the APK is downloaded from GitHub and installed via `PackageInstaller`
+2. If installation fails, a diagnostic dialog shows: status code, error message, APK size, Android version, and device model โ plus a "Riprova" button
+3. On success, the wizard automatically writes `scale_enabled=true` and `scale_gateway_url=ws://127.0.0.1:8765` to your EverShelf server
+
+### Step 6 โ Screensaver
+Choose whether the screen should go dark after inactivity.
+
+### Summary
+All done โ the web app loads in full-screen kiosk mode.
+
+---
+
+## Exiting Kiosk Mode
+
+Tap the **โ** button in the header (top-left). A confirmation dialog appears.
+
+---
+
+## Hard Refresh
+
+Tap the **โป** button in the header to clear the WebView cache and reload the latest version of the web app.
+
+---
+
+## Update Notifications
+
+Every 6 hours the app checks GitHub releases. If a newer version is available, a banner appears with a one-tap download and install flow.
+
+---
+
+## Native TTS Bridge
+
+When Cooking Mode reads recipe steps, the kiosk app:
+1. Intercepts the TTS call from the web app via a JavaScript bridge
+2. Uses the Android `TextToSpeech` engine directly
+3. Falls back to the browser Web Speech API if the bridge is unavailable
+
+No internet connection required for TTS. No extra voice packs to install.
+
+---
+
+## SSL / Self-signed Certificates
+
+The WebView accepts self-signed certificates automatically. No configuration needed for local HTTPS servers.
+
+---
+
+## Troubleshooting
+
+### "Impossibile installare il gateway"
+- Make sure "Install from unknown sources" is enabled for the kiosk app in Android Settings โ Apps โ Special app access
+- Check that there is enough free storage (the APK is ~15 MB)
+- The diagnostic dialog shows the exact failure code โ include it when opening an issue
+
+### "Server non trovato" during auto-discovery
+- Make sure your tablet and server are on the same Wi-Fi network
+- Ensure the server is not on a VPN-only interface
+- Try entering the URL manually
+
+### Screen pinning / back button not working
+- Screen pinning requires the app to be set as Device Owner or the user to confirm the pin prompt
+- Some Android skins (Samsung, Xiaomi) may require additional accessibility permissions
+
+### App crashes on startup
+- Force-stop the app, clear its data (Settings โ Apps โ EverShelf Kiosk โ Clear data), and relaunch
+
+---
+
+## Building from Source
+
+```bash
+cd evershelf-kiosk
+./gradlew assembleRelease
+# APK: app/build/outputs/apk/release/app-release.apk
+```
+
+Requires Android Studio or JDK 17+ with the Android SDK.
+
+---
+
+## Permissions
+
+| Permission | Purpose |
+|-----------|---------|
+| `INTERNET` | Load the EverShelf web app |
+| `CAMERA` | Barcode scanning and AI photo identification |
+| `RECORD_AUDIO` | Voice input in AI chat |
+| `WAKE_LOCK` | Keep the screen on |
+| `REQUEST_INSTALL_PACKAGES` | Install the Scale Gateway APK |
+| `ACCESS_WIFI_STATE` | LAN auto-discovery |
+| `REORDER_TASKS` | Bring app to foreground after gateway launch |
diff --git a/docs/wiki/Configuration.md b/docs/wiki/Configuration.md
new file mode 100644
index 0000000..0281ec9
--- /dev/null
+++ b/docs/wiki/Configuration.md
@@ -0,0 +1,157 @@
+# โ๏ธ Configuration
+
+EverShelf is configured via a `.env` file in the project root. Copy `.env.example` to `.env` and edit it โ the app reads this file on every API call.
+
+**Never commit `.env` to Git.** It is already in `.gitignore`.
+
+---
+
+## Full `.env` Reference
+
+```ini
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# AI โ Google Gemini
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# Your Gemini API key (required for all AI features)
+# Get one free at: https://aistudio.google.com/app/apikey
+GEMINI_API_KEY=
+
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Shopping List โ Bring! Integration
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# Your Bring! account credentials
+# Leave blank to disable Bring! integration
+BRING_EMAIL=
+BRING_PASSWORD=
+
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Text-to-Speech (for Cooking Mode)
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# URL to a TTS endpoint (e.g. Home Assistant event endpoint)
+TTS_URL=
+
+# Bearer token for the TTS endpoint
+TTS_TOKEN=
+
+# Set to true to enable server-side TTS (the browser Web Speech API is always used as fallback)
+TTS_ENABLED=false
+
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Security
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# Protect the save_settings endpoint with a token
+# If set, the Settings UI will prompt for this value before saving
+# Validated with hash_equals() to prevent timing attacks
+SETTINGS_TOKEN=
+
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Demo / Public Mode
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# Set to true to block ALL write operations at the PHP router level
+# Useful for public demos or read-only kiosk deployments
+# Also activatable per-request via ?demo=1 URL parameter
+DEMO_MODE=false
+
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+# Scale Gateway
+# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+
+# Enable the BLE scale integration
+SCALE_ENABLED=false
+
+# WebSocket URL of the Scale Gateway app running on the same device
+# Default for Android kiosk: ws://127.0.0.1:8765
+SCALE_GATEWAY_URL=ws://127.0.0.1:8765
+```
+
+---
+
+## Settings UI
+
+Most settings can also be configured from the browser via **Settings โ โ๏ธ**:
+
+| Setting | `.env` key | Notes |
+|---------|-----------|-------|
+| Gemini API key | `GEMINI_API_KEY` | Stored server-side, never exposed to browser |
+| Bring! email | `BRING_EMAIL` | โ |
+| Bring! password | `BRING_PASSWORD` | โ |
+| TTS URL | `TTS_URL` | โ |
+| TTS token | `TTS_TOKEN` | โ |
+| TTS enabled | `TTS_ENABLED` | โ |
+| Scale enabled | `SCALE_ENABLED` | โ |
+| Scale gateway URL | `SCALE_GATEWAY_URL` | โ |
+| Settings token | `SETTINGS_TOKEN` | Write-only; current value never shown |
+
+> **Security note:** `get_settings` returns only **boolean flags** (`gemini_key_set: true/false`), never raw key values. Raw values are only accessible server-side.
+
+---
+
+## Protecting Settings with a Token
+
+If your EverShelf instance is accessible from untrusted networks, set `SETTINGS_TOKEN` to a strong random string:
+
+```bash
+# Generate a strong token
+openssl rand -hex 32
+```
+
+```ini
+SETTINGS_TOKEN=a3f9b2c1d4e5...
+```
+
+Users will be prompted for this token before any Settings save. If the token doesn't match, the request is rejected with HTTP 403.
+
+---
+
+## Demo Mode
+
+Two ways to enable demo mode:
+
+1. **Permanent:** Set `DEMO_MODE=true` in `.env`
+2. **Per-session:** Append `?demo=1` to any URL (e.g. `https://evershelfproject.dadaloop.it/demo`)
+
+In demo mode:
+- All POST/write API calls return success without touching the database
+- A "DEMO" badge appears in the header
+- Gemini AI is treated as available (mock responses)
+- Bring! write operations are silently no-op'd
+- A mock pantry with sample data is loaded
+
+---
+
+## API Rate Limiting
+
+EverShelf applies file-based rate limiting to protect AI endpoints:
+
+| Tier | Limit | Endpoints |
+|------|-------|-----------|
+| Standard | 120 req/min | All general endpoints |
+| AI | 15 req/min | `gemini_*`, `generate_recipe` |
+| Strict | 5 req/min | `report_error` |
+
+Rate limit state is stored in `data/rate_limits/`. To reset, delete the files in that directory.
+
+---
+
+## Database
+
+EverShelf uses **SQLite** stored at `data/evershelf.db`. The file is created automatically on first run.
+
+Schema migrations run automatically whenever `database.php` is loaded โ no manual migration steps needed.
+
+To back up the database:
+
+```bash
+cp data/evershelf.db data/backups/evershelf-$(date +%Y%m%d).db
+```
+
+Or use the included `backup.sh`:
+
+```bash
+./backup.sh
+```
diff --git a/docs/wiki/Contributing.md b/docs/wiki/Contributing.md
new file mode 100644
index 0000000..c91f846
--- /dev/null
+++ b/docs/wiki/Contributing.md
@@ -0,0 +1,164 @@
+# ๐ค Contributing
+
+Contributions of all kinds are welcome โ bug fixes, new features, translations, documentation improvements.
+
+---
+
+## Getting Started
+
+### 1. Fork and clone
+
+```bash
+git clone https://github.com/YOUR_USERNAME/EverShelf.git
+cd EverShelf
+```
+
+### 2. Create a branch
+
+```bash
+git checkout -b feature/my-feature
+# or
+git checkout -b fix/my-bug-fix
+```
+
+### 3. Set up a local server
+
+```bash
+# Option A: PHP built-in server
+php -S localhost:8080
+
+# Option B: Docker
+docker compose up -d
+```
+
+Open `http://localhost:8080` in your browser.
+
+### 4. Make your changes
+
+The app has **no build step**. Edit files directly and refresh the browser.
+
+Key files:
+- `assets/js/app.js` โ all frontend logic
+- `assets/css/style.css` โ all styles
+- `api/index.php` โ all API endpoints
+- `api/database.php` โ SQLite schema and migrations
+- `translations/*.json` โ i18n strings
+
+### 5. Test
+
+```bash
+# Check PHP syntax
+php -l api/index.php
+php -l api/database.php
+
+# Check JS syntax
+node --check assets/js/app.js
+```
+
+There are no automated JS tests yet โ manual testing in the browser is the current approach. If you add a feature, test the full flow: add, use, undo.
+
+### 6. Commit
+
+Use [Conventional Commits](https://www.conventionalcommits.org/):
+
+```bash
+git commit -m "feat(inventory): add bulk delete"
+git commit -m "fix(scale): handle BLE disconnect during countdown"
+git commit -m "docs: update kiosk setup guide"
+git commit -m "chore: bump version to 1.8.0"
+```
+
+Types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`
+
+Scopes: `inventory`, `ai`, `shopping`, `cooking`, `scale`, `kiosk`, `gateway`, `webapp`, `api`, `db`
+
+### 7. Push and open a PR
+
+```bash
+git push origin feature/my-feature
+```
+
+Open a Pull Request against the `develop` branch (not `main`).
+
+---
+
+## Branch Strategy
+
+| Branch | Purpose |
+|--------|---------|
+| `main` | Production โ auto-deployed, never commit directly |
+| `develop` | Integration branch โ all PRs target here |
+| `feature/*` | New features |
+| `fix/*` | Bug fixes |
+
+CI auto-merges `develop โ main` on every push to `develop`.
+
+---
+
+## CI / CD Pipeline
+
+GitHub Actions runs on every push to `develop` and `main`:
+
+1. **PHP lint** โ `php -l` on all PHP files
+2. **JS syntax check** โ `node --check assets/js/app.js`
+3. **Translation validation** โ checks that all language files have the same keys
+4. **Docker build** โ verifies the Docker image builds successfully
+5. **Android build** โ (on tagged commits) builds Kiosk and Scale Gateway APKs
+
+---
+
+## Adding Translations
+
+See the full guide in [Translations](Translations).
+
+Short version:
+1. Copy `translations/it.json` โ `translations/xx.json`
+2. Translate all values
+3. Add `'xx'` to `SUPPORTED_LANGUAGES` in `app.js`
+4. Open a PR
+
+---
+
+## Reporting Bugs
+
+Open an issue on GitHub. Include:
+- Steps to reproduce
+- Expected vs. actual behaviour
+- Browser/OS version
+- Any console errors (F12 โ Console)
+
+---
+
+## Code Style
+
+- **PHP:** PSR-12, 4-space indent, type hints where practical
+- **JavaScript:** ES2020+, `async/await`, no frameworks, 4-space indent
+- **CSS:** BEM-ish class names, CSS custom properties for theming
+- **SQL:** parameterized queries (PDO), no raw string interpolation
+
+---
+
+## Adding a New API Endpoint
+
+1. Add a `case 'my_action':` to the router in `api/index.php`
+2. Implement `function myAction(PDO $db): void`
+3. Add the endpoint to `docs/openapi.yaml`
+4. Add translations for any new UI strings to all 3 language files
+
+---
+
+## Security
+
+If you find a security vulnerability, **do not open a public issue**. Email [evershelfproject@gmail.com](mailto:evershelfproject@gmail.com) directly.
+
+Relevant resources:
+- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
+- All SQL must use PDO prepared statements
+- Never expose API keys in API responses (boolean flags only)
+- Use `hash_equals()` for token comparison
+
+---
+
+## License
+
+By contributing you agree that your code will be licensed under the [MIT License](https://github.com/dadaloop82/EverShelf/blob/main/LICENSE).
diff --git a/docs/wiki/FAQ.md b/docs/wiki/FAQ.md
new file mode 100644
index 0000000..be1e574
--- /dev/null
+++ b/docs/wiki/FAQ.md
@@ -0,0 +1,164 @@
+# โ FAQ & Troubleshooting
+
+---
+
+## Installation
+
+### The app shows a blank page after setup
+
+- Open the browser console (F12 โ Console) and check for errors
+- Make sure PHP is running and `api/index.php` is reachable: visit `https://your-server/dispensa/api/index.php?action=get_settings` โ it should return JSON
+- Check your web server error log: `tail -f /var/log/apache2/error.log`
+
+### Camera doesn't work / barcode scanner won't open
+
+Camera access requires **HTTPS**. On plain HTTP, browsers block `getUserMedia()`.
+
+- Set up HTTPS with Let's Encrypt, Caddy, or a self-signed certificate
+- On Android, you can also add a security exception in Chrome: `chrome://flags/#allow-insecure-localhost`
+
+### "Permission denied" error for the data directory
+
+```bash
+chmod 755 data/
+chown -R www-data:www-data data/
+```
+
+### Docker container exits immediately
+
+```bash
+docker compose logs evershelf
+```
+
+Usually a permission issue on the mounted `data/` volume. Try:
+
+```bash
+docker compose down
+rm -rf data/
+mkdir data/
+docker compose up -d
+```
+
+---
+
+## AI Features
+
+### AI features don't work / "AI non disponibile"
+
+1. Check that `GEMINI_API_KEY` is set in `.env`
+2. Verify the key is valid at [aistudio.google.com](https://aistudio.google.com)
+3. Check that you haven't exceeded the free tier quota (15 req/min, 1500 req/day)
+4. Look for errors in the PHP error log
+
+### Recipe generation stops midway
+
+This is usually a Gemini API timeout. The app streams results via SSE โ if the server PHP timeout is too low, the stream is cut short. Increase `max_execution_time` in `php.ini`:
+
+```ini
+max_execution_time = 120
+```
+
+---
+
+## Shopping List (Bring!)
+
+### "Bring! non configurato" message in the shopping tab
+
+Add your Bring! credentials to `.env`:
+
+```ini
+BRING_EMAIL=your@email.com
+BRING_PASSWORD=yourpassword
+```
+
+### Items aren't syncing to Bring!
+
+- Verify your credentials are correct by logging into [getbring.com](https://web.getbring.com/)
+- Check for rate-limit errors in the PHP error log โ Bring! has API limits
+
+---
+
+## Scale Integration
+
+### Scale readings don't appear in EverShelf
+
+1. Confirm the gateway app is running and shows the WebSocket URL
+2. Check the Gateway URL in EverShelf Settings matches exactly
+3. Make sure both the Android device and the EverShelf server are on the same network
+4. Look at the scale status indicator (โ๏ธ) in the header โ "disconnected" means no WebSocket connection
+
+### Scale shows weight but form doesn't auto-fill
+
+- The auto-fill only triggers for products with unit `g` or `ml`
+- Make sure you tapped "โ๏ธ Leggi bilancia" first to activate the scale modal
+- The weight must stabilize (stay within 10g) for the countdown to start
+
+### Bluetooth scale not appearing in the gateway app
+
+- Wake up the scale (step on it or press its button)
+- Make sure Bluetooth and Location permissions are granted to the gateway app (Location is required by Android for BLE scanning)
+- Restart the gateway app
+
+---
+
+## Kiosk App
+
+### Setup wizard can't find my server
+
+- Make sure the tablet is on the same Wi-Fi network as the server
+- Try entering the URL manually instead of using auto-discovery
+- Check that the server responds on the expected port (80/443/8080/8443)
+
+### Gateway install fails with an error dialog
+
+The dialog shows the exact failure code. Common causes:
+
+| Code | Cause | Fix |
+|------|-------|-----|
+| `STATUS_FAILURE` (1) | Generic install failure โ often OEM restriction | Enable "Install from unknown sources" for the kiosk app in Android Settings |
+| `STATUS_FAILURE_CONFLICT` (3) | Signature mismatch with existing install | Uninstall the old gateway app, then retry |
+| `STATUS_FAILURE_STORAGE` (6) | Not enough storage | Free up space on the device |
+
+### Exit button (โ) is not visible
+
+The โ button is injected into the header by the kiosk app. If the web app's header is covered or the page failed to load, try the hard refresh (โป) button. If neither is visible, triple-tap the page title area to access the developer settings.
+
+### App is stuck in kiosk mode after a crash
+
+Restart the device. Screen pinning is released on reboot.
+
+---
+
+## General
+
+### The version shown in the app is outdated
+
+The version is cached by the browser. Do a hard refresh:
+- Desktop: `Ctrl+Shift+R` / `Cmd+Shift+R`
+- Android: tap the โป button (kiosk) or clear site data in Chrome settings
+
+### Transactions are missing from the log
+
+The log shows the last 50 entries by default. Tap "Carica altri" to load more. Entries older than the database creation date won't appear.
+
+### "Can only undo transactions within 24 hours"
+
+The undo window is 24 hours. For older operations, manually correct the inventory via the Edit function on the affected product.
+
+### Error reports keep creating duplicate GitHub issues
+
+EverShelf uses a fingerprint to deduplicate โ the same error from the same device won't create a new issue within 24 hours. If you're seeing duplicates, check the `data/rate_limits/` folder and clear old files.
+
+---
+
+## Getting Help
+
+- **Open an issue:** [github.com/dadaloop82/EverShelf/issues](https://github.com/dadaloop82/EverShelf/issues)
+- **Email:** [evershelfproject@gmail.com](mailto:evershelfproject@gmail.com)
+- **Try the demo:** [evershelfproject.dadaloop.it/demo](https://evershelfproject.dadaloop.it/demo)
+
+When reporting a bug, include:
+1. EverShelf version (shown in the header as `v1.x.x`)
+2. Browser and OS
+3. Steps to reproduce
+4. Any error messages from the browser console (F12)
diff --git a/docs/wiki/Features.md b/docs/wiki/Features.md
new file mode 100644
index 0000000..ed08c6e
--- /dev/null
+++ b/docs/wiki/Features.md
@@ -0,0 +1,219 @@
+# โจ Features
+
+A complete walkthrough of EverShelf's features.
+
+---
+
+## ๐ฆ Inventory Management
+
+### Adding Products
+
+- Tap **โ** to open the add form
+- Search by name or scan a barcode
+- Select storage location: Pantry, Fridge, Freezer, or a custom location
+- Enter quantity and expiry date (or let AI estimate it)
+- Mark as vacuum-sealed or opened for adjusted shelf-life calculation
+
+### Barcode Scanning
+
+Tap the barcode icon to open the camera scanner (QuaggaJS). The app:
+1. Checks your local database first
+2. Falls back to [Open Food Facts](https://world.openfoodfacts.org/) for unknown barcodes
+3. Pre-fills the product form with name, brand, category
+
+### AI Product Identification
+
+Point the camera at any product โ Gemini identifies it and:
+- Shows matching products **already in your pantry** first
+- Suggests a new product entry with pre-filled fields
+- Provides a storage location hint and estimated shelf-life
+
+### Storage Locations
+
+| Location | Icon | Notes |
+|----------|------|-------|
+| Pantry | ๐ | Room temperature |
+| Fridge | โ๏ธ | Refrigerated |
+| Freezer | ๐ง | Frozen |
+| Custom | ๐ฆ | Any name you choose |
+
+### Opened Product Tracking
+
+When you partially use a product and mark it as "opened":
+- Shelf-life is recalculated from the opening date
+- Uses AI (Gemini) + per-category rules (e.g. fish: 2 days, milk: 3 days)
+- Whole sealed packages always keep their original manufacturer expiry
+- Products with mixed whole + fractional units show as two separate entries
+
+### Vacuum-Sealed Support
+
+Mark any product as vacuum-sealed to extend its estimated expiry date (typically 2โ3ร the normal shelf-life).
+
+---
+
+## ๐ค AI Features (Google Gemini)
+
+All AI features require a `GEMINI_API_KEY` in `.env`. They degrade gracefully when the key is missing or quota is exceeded.
+
+### Expiry Date Reading
+
+Photograph the label on a product โ Gemini extracts the expiry date and fills the field automatically.
+
+### Product Identification
+
+Camera-based identification with pantry matching. See [Adding Products](#adding-products) above.
+
+### Storage & Shelf-life Hint
+
+When adding a new product, a background Gemini call suggests:
+- Optimal storage location
+- Estimated shelf-life in days
+
+Shown as an inline AI badge next to the expiry estimate. Does not block the form.
+
+### Recipe Generation
+
+Tap **๐ณ Ricette** โ **Genera ricetta** to get a recipe using:
+- Ingredients about to expire (prioritised)
+- What's currently in your pantry
+- Your language preference
+
+Recipes stream live via Server-Sent Events so results appear as they are generated.
+
+### AI Chat Assistant
+
+Open **๐ฌ Chat** to ask questions like:
+- "Cosa posso fare con le uova e la pasta?"
+- "Quanti giorni dura il prosciutto cotto aperto in frigo?"
+- "Suggeriscimi uno spuntino veloce"
+
+The assistant knows your current inventory.
+
+### Shopping Suggestions with Tips
+
+Smart shopping predictions include a short AI-generated practical tip per item (e.g. "Buy the 2 kg bag โ it freezes well").
+
+### Anomaly Explanation
+
+When the dashboard shows a suspicious quantity banner, tap **๐ค Spiega** to get a plain-language explanation of why the discrepancy likely occurred and what to do about it.
+
+### Model Fallback
+
+All AI endpoints try `gemini-2.5-flash` first and automatically fall back to `gemini-2.0-flash` if unavailable.
+
+---
+
+## ๐ Shopping List (Bring! Integration)
+
+Configure `BRING_EMAIL` and `BRING_PASSWORD` in `.env` to enable.
+
+### Features
+
+- **View and manage** your Bring! list inside EverShelf
+- **Auto-add on depletion** โ when stock hits zero, the product is added to Bring! automatically
+- **Auto-remove on scan** โ scanning a product in removes it from the shopping list
+- **Generic names** โ products are grouped by type ("Latte", "Panna da cucina") not brand, keeping the list clean
+- **Auto-migration** โ items already on Bring! are silently renamed to their generic name on list load
+- **Catalog coverage** โ 100+ product types mapped to Bring! catalog keys for icons and categories in the Bring! app
+- **AI fallback** โ unknown product types use Gemini to determine the best generic name
+
+---
+
+## ๐ณ Cooking Mode
+
+Start cooking mode from any recipe by tapping **โถ Avvia cottura**.
+
+### Features
+
+- **Step-by-step guidance** โ fullscreen, distraction-free interface
+- **Text-to-Speech** โ each step is read aloud automatically when you navigate; supports:
+ - Browser Web Speech API (default)
+ - Native Android TTS (kiosk app)
+ - Custom REST endpoint (e.g. Home Assistant)
+- **Built-in timers** โ automatic timer suggestions based on recipe text; 10-second vocal countdown warning before expiry
+- **Ingredient tracking** โ mark ingredients as used; leftover quantities prompt a "move to another location" flow
+- **Recipe completion** โ "Buon appetito!" spoken on the last step
+
+---
+
+## ๐ Dashboard
+
+### Inventory Overview
+
+Three stat cards at the top show item counts for Pantry, Fridge, and Freezer with animated skeleton loading while data fetches.
+
+### Expiry Alerts Banner
+
+Priority-sorted notifications for:
+- Expired products (with safety assessment โ green โ
safe, amber ๐ check, red ๐ซ danger)
+- Products expiring within 3 days
+
+Actions per item: Use, Throw away, Edit, Dismiss. Swipe or tap arrows to navigate.
+
+### Anomaly Banner
+
+Highlights suspicious quantities (e.g. "You have 0 eggs but used 12 this month"). Actions:
+- One-tap correction to the suggested quantity
+- Inline edit with free-form quantity
+- "๐ค Spiega" for AI explanation
+- Dismiss (with current quantity shown: "La quantitร รจ giusta (2 pz)")
+
+### Anti-Waste Report
+
+Shows your waste rate vs. the national average with an estimated annual kg of food wasted.
+
+### Quick Recipe Bar
+
+One-tap recipe suggestion using the ingredients closest to expiry.
+
+---
+
+## ๐ฑ Progressive Web App (PWA)
+
+EverShelf is installable as a PWA on any device:
+
+1. Open in Chrome/Safari/Edge
+2. Tap **"Add to Home Screen"** (browser menu)
+3. Launch from the home screen like a native app
+
+Features:
+- Offline-capable shell (assets cached)
+- Full-screen mode on mobile
+- Multi-device: all data syncs via the shared server
+
+---
+
+## ๐ Update Notifications
+
+When a new EverShelf release is published on GitHub, a small pill appears in the header. Click it to see the changelog. Checked on load and every 30 minutes.
+
+---
+
+## ๐ Multi-language
+
+The app auto-detects your browser language. Supported: ๐ฎ๐น Italian, ๐ฌ๐ง English, ๐ฉ๐ช German.
+
+Change the language in **Settings โ Language**.
+
+See [Translations](Translations) to add a new language.
+
+---
+
+## โฉ Transaction History & Undo
+
+**Settings โ Storico** shows all inventory operations (adds, uses, throws).
+
+- Any operation within the **last 24 hours** shows a red โฉ undo button
+- Tapping โฉ shows a 5-second countdown confirmation before reversing the transaction
+- The original stock is restored and a counter-transaction is logged
+
+---
+
+## ๐ Security Features
+
+- API keys never exposed to the browser (`get_settings` returns boolean flags only)
+- `save_settings` protected by optional `SETTINGS_TOKEN` (validated with `hash_equals`)
+- `DEMO_MODE=true` blocks all write operations at the PHP router level
+- Parameterized SQL queries (PDO prepared statements) throughout
+- Input validation on all inventory operations (quantity bounds, location whitelist)
+- See [Configuration](Configuration) for details
diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md
new file mode 100644
index 0000000..f529c6a
--- /dev/null
+++ b/docs/wiki/Home.md
@@ -0,0 +1,93 @@
+# ๐ EverShelf Wiki
+
+Welcome to the **EverShelf** project wiki โ your complete reference for installation, configuration, features, and development.
+
+---
+
+## ๐ Try it now
+
+> **[โถ Live Demo](https://evershelfproject.dadaloop.it/demo)** โ no installation, no login, full AI enabled
+> **[๐ Project Website](https://evershelfproject.dadaloop.it/)**
+
+---
+
+## ๐ Wiki Contents
+
+| Page | Description |
+|------|-------------|
+| [Installation](Installation) | Docker, manual setup, HTTPS, web server config |
+| [Configuration](Configuration) | `.env` reference โ all options explained |
+| [Features](Features) | Complete feature documentation |
+| [API Reference](API-Reference) | All REST endpoints, parameters, and responses |
+| [Android Kiosk](Android-Kiosk) | Tablet kiosk app setup and usage |
+| [Scale Gateway](Scale-Gateway) | BLE smart scale integration |
+| [Translations](Translations) | Adding and editing language files |
+| [Contributing](Contributing) | Development workflow and PR process |
+| [FAQ & Troubleshooting](FAQ) | Common issues and solutions |
+
+---
+
+## โจ What is EverShelf?
+
+EverShelf is a **self-hosted pantry management system** that runs entirely on your own server. It:
+
+- Tracks food inventory across multiple storage locations (pantry, fridge, freezer, custom)
+- Scans barcodes and uses **Google Gemini AI** to identify products from photos
+- Suggests recipes based on what's in your pantry โ especially items about to expire
+- Predicts what you'll need to buy before you run out
+- Integrates with the **Bring!** shopping list app
+- Supports a **BLE smart scale** for weight-based tracking
+- Runs as a **Progressive Web App** installable on any device
+- Optionally pairs with a dedicated **Android kiosk tablet app**
+
+All data stays on your server. No cloud, no subscriptions.
+
+---
+
+## ๐ What's New
+
+### v1.7.1 (2026-05-04)
+- Destructive actions ("Butta tutto", "Finisci tutto") now require a **5-second countdown confirmation** before executing
+- History undo button โฉ is now clearly visible (red tint, larger)
+- Undo confirmation uses the in-app modal instead of the native browser `confirm()`
+
+### v1.7.0 (2026-05-04)
+- Smart auto-discovery rewrite (kiosk)
+- Gateway auto-pre-configuration after install
+- ErrorReporter init at setup start
+- Graceful Bring! no-key state
+- Use-quantity guard with shake animation
+- Demo mode (`?demo=1`)
+
+โ See the full [CHANGELOG](https://github.com/dadaloop82/EverShelf/blob/main/CHANGELOG.md)
+
+---
+
+## ๐ฆ Repository Structure
+
+```
+EverShelf/
+โโโ index.html # Single-page application entry point
+โโโ manifest.json # PWA manifest
+โโโ .env.example # Configuration template
+โโโ api/
+โ โโโ index.php # Main API router
+โ โโโ database.php # SQLite schema + migrations
+โ โโโ cron_smart_shopping.php # Background predictions job
+โโโ assets/
+โ โโโ css/style.css
+โ โโโ js/app.js
+โ โโโ img/
+โโโ translations/ # i18n JSON files (it, en, de)
+โโโ docs/openapi.yaml # OpenAPI 3.0 spec
+โโโ evershelf-kiosk/ # Android kiosk app (Kotlin)
+โโโ evershelf-scale-gateway/ # Android BLE gateway app (Kotlin)
+```
+
+---
+
+## ๐ License
+
+MIT โ free to use, modify, and distribute. See [LICENSE](https://github.com/dadaloop82/EverShelf/blob/main/LICENSE).
+
+**Author:** Stimpfl Daniel โ [evershelfproject@gmail.com](mailto:evershelfproject@gmail.com)
diff --git a/docs/wiki/Installation.md b/docs/wiki/Installation.md
new file mode 100644
index 0000000..5ce28c2
--- /dev/null
+++ b/docs/wiki/Installation.md
@@ -0,0 +1,233 @@
+# ๐ฆ Installation
+
+EverShelf runs on any server with PHP 8.0+ and SQLite. Docker is the recommended approach for the fastest setup.
+
+---
+
+## Prerequisites
+
+| Requirement | Minimum | Notes |
+|-------------|---------|-------|
+| PHP | 8.0+ | Extensions: `pdo_sqlite`, `curl`, `mbstring`, `json` |
+| Web server | Apache 2.4+ or Nginx | Apache `.htaccess` included |
+| SQLite | 3.x | Bundled with PHP on most distros |
+| HTTPS | Recommended | Required for camera access on mobile browsers |
+| RAM | 256 MB | 512 MB+ recommended if using AI features |
+
+---
+
+## Option A: Docker (recommended)
+
+The fastest way to get started.
+
+```bash
+# 1. Clone the repository
+git clone https://github.com/dadaloop82/EverShelf.git
+cd EverShelf
+
+# 2. Create your configuration
+cp .env.example .env
+nano .env # set GEMINI_API_KEY and other options
+
+# 3. Start
+docker compose up -d
+
+# 4. Open in browser
+# โ http://localhost:8080
+```
+
+The Docker image:
+- Uses PHP-Apache on Debian Bookworm slim
+- Auto-creates the `data/` directory with correct permissions
+- Exposes port `8080` by default (configurable in `docker-compose.yml`)
+- Persists data in a named Docker volume
+
+### Changing the port
+
+Edit `docker-compose.yml`:
+
+```yaml
+ports:
+ - "8080:80" # change 8080 to your desired host port
+```
+
+### Using HTTPS with Docker
+
+Add a reverse proxy (e.g. Traefik, Caddy, or Nginx Proxy Manager) in front of the container for automatic TLS.
+
+---
+
+## Option B: Manual (Apache)
+
+```bash
+# 1. Clone into your web root
+git clone https://github.com/dadaloop82/EverShelf.git /var/www/html/dispensa
+cd /var/www/html/dispensa
+
+# 2. Create configuration
+cp .env.example .env
+nano .env
+
+# 3. Set permissions on the data directory
+chmod 755 data/
+chown -R www-data:www-data data/
+```
+
+Make sure `mod_rewrite` is enabled:
+
+```bash
+sudo a2enmod rewrite
+sudo systemctl restart apache2
+```
+
+Apache virtual host (or add to `.htaccess` which is already included):
+
+```apache
+
+ ServerName evershelf.local
+ DocumentRoot /var/www/html/dispensa
+
+
+ AllowOverride All
+ Require all granted
+
+
+ # Hide sensitive paths
+
+ Require all denied
+
+
+ SSLEngine on
+ SSLCertificateFile /etc/ssl/certs/evershelf.crt
+ SSLCertificateKeyFile /etc/ssl/private/evershelf.key
+
+```
+
+---
+
+## Option C: Manual (Nginx)
+
+```nginx
+server {
+ listen 443 ssl;
+ server_name evershelf.local;
+ root /var/www/html/dispensa;
+ index index.html;
+
+ ssl_certificate /etc/ssl/certs/evershelf.crt;
+ ssl_certificate_key /etc/ssl/private/evershelf.key;
+
+ location /api/ {
+ try_files $uri $uri/ =404;
+ location ~ \.php$ {
+ fastcgi_pass unix:/run/php/php8.2-fpm.sock;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ include fastcgi_params;
+ }
+ }
+
+ # Block sensitive files
+ location ~ /\.env { deny all; }
+ location ~ /data/ { deny all; }
+ location ~ /backup\.sh { deny all; }
+
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+}
+```
+
+---
+
+## HTTPS Setup
+
+Camera and microphone access (barcode scanning, voice) **require HTTPS** on all modern mobile browsers.
+
+### Self-signed certificate (local network)
+
+```bash
+openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
+ -keyout /etc/ssl/private/evershelf.key \
+ -out /etc/ssl/certs/evershelf.crt \
+ -subj "/CN=evershelf.local" \
+ -addext "subjectAltName=IP:192.168.1.100,DNS:evershelf.local"
+```
+
+Android will show a certificate warning โ tap "Advanced โ Proceed" once. The kiosk app accepts self-signed certificates automatically.
+
+### Let's Encrypt (public server)
+
+```bash
+sudo apt install certbot python3-certbot-apache
+sudo certbot --apache -d evershelf.yourdomain.com
+```
+
+### Caddy (automatic TLS)
+
+```
+evershelf.yourdomain.com {
+ root * /var/www/html/dispensa
+ php_fastcgi unix//run/php/php8.2-fpm.sock
+ file_server
+ respond /data/* 403
+ respond /.env 403
+}
+```
+
+---
+
+## Cron Job (optional)
+
+For smart shopping predictions to stay up to date:
+
+```bash
+# Edit crontab
+crontab -e
+
+# Add (runs every 5 minutes)
+*/5 * * * * php /var/www/html/dispensa/api/cron_smart_shopping.php >> /var/www/html/dispensa/data/cron.log 2>&1
+```
+
+---
+
+## Backup (optional)
+
+```bash
+# Edit crontab
+crontab -e
+
+# Daily backup at 3 AM
+0 3 * * * /var/www/html/dispensa/backup.sh
+```
+
+The `backup.sh` script copies `data/evershelf.db` to `data/backups/` with a timestamp.
+
+---
+
+## Updating
+
+```bash
+cd /var/www/html/dispensa
+git pull origin main
+# Database migrations run automatically on next page load
+```
+
+With Docker:
+
+```bash
+docker compose pull
+docker compose up -d
+```
+
+---
+
+## Post-installation
+
+Once the app is running, open it in your browser and:
+
+1. Go to **Settings** (โ๏ธ icon in the header)
+2. Enter your **Gemini API key** (get one free at [aistudio.google.com](https://aistudio.google.com/app/apikey))
+3. Optionally configure Bring!, TTS, and scale settings
+4. Add your first product via the โ button or barcode scan
+
+See [Configuration](Configuration) for the full list of settings.
diff --git a/docs/wiki/Scale-Gateway.md b/docs/wiki/Scale-Gateway.md
new file mode 100644
index 0000000..6fd9728
--- /dev/null
+++ b/docs/wiki/Scale-Gateway.md
@@ -0,0 +1,155 @@
+# โ๏ธ Scale Gateway
+
+The EverShelf Scale Gateway is an Android app that bridges a Bluetooth LE smart scale to EverShelf, enabling automatic weight-based inventory tracking.
+
+---
+
+## How it works
+
+```
+Smart Scale
+ โ (Bluetooth LE)
+ โผ
+Android device (Scale Gateway app)
+ โ (WebSocket โ ws://127.0.0.1:8765)
+ โผ
+EverShelf Server (scale_relay.php โ SSE relay)
+ โ (Server-Sent Events)
+ โผ
+EverShelf Web App (auto-fills weight in add/use forms)
+```
+
+The Gateway runs a local WebSocket server on port **8765**. The EverShelf server proxies scale readings to the browser via SSE, avoiding HTTPSโWS mixed-content issues.
+
+---
+
+## Download
+
+**[โฌ Download latest APK](https://github.com/dadaloop82/EverShelf/releases/latest/download/evershelf-scale-gateway.apk)**
+
+> Current version: **v2.1.0** โ requires Android 7.0+
+
+---
+
+## Supported Scales
+
+| Protocol | BLE Service | Notes |
+|----------|------------|-------|
+| Bluetooth SIG Weight Scale | `0x181D` / char `0x2A9D` | Most compatible |
+| Bluetooth SIG Body Composition | `0x181B` / char `0x2A9C` | Weight + body fat |
+| Generic fallback | Any notifiable characteristic | Auto-heuristic for 100+ models |
+
+**Verified compatible models:**
+- Xiaomi Mi Body Composition Scale 2
+- Renpho Smart Body Fat Scale
+- Any scale supported by [openScale](https://github.com/oliexdev/openScale/wiki/Supported-scales)
+
+---
+
+## Setup
+
+### 1. Install
+
+Download and install the APK. You may need to enable "Install from unknown sources" in Android Settings.
+
+> **Kiosk users:** the Setup Wizard installs the gateway automatically in Step 5.
+
+### 2. Launch the app
+
+The gateway server starts immediately. Note the **Gateway URL** shown (e.g. `ws://192.168.1.100:8765`).
+
+### 3. Configure EverShelf
+
+In EverShelf **Settings โ Scale**:
+- Enable scale integration
+- Enter the Gateway URL (or let auto-discovery find it)
+
+> **Kiosk users:** this is done automatically during setup.
+
+### 4. Connect your scale
+
+Tap **"Cerca Bilance Bluetooth"** (Find Bluetooth Scales). Make sure your scale is powered on. Tap it in the list to pair and connect.
+
+---
+
+## Using the Scale in EverShelf
+
+When scale integration is enabled:
+
+1. Open the **Add** or **Use** form for any product with unit `g` or `ml`
+2. A **"โ๏ธ Leggi bilancia"** button appears
+3. Tap it โ a live weight display appears with a stability indicator
+4. Step on or place the product on the scale
+5. When the reading stabilizes, a **5-second countdown** starts
+6. The weight auto-fills the quantity field and the form submits
+
+### Thresholds and de-duplication
+
+- **10g threshold** โ readings that haven't changed enough between products are ignored to prevent stale readings
+- **12-second server-side dedup** โ a second scale-triggered deduction of the same product within 12 seconds is rejected (guards against BLE multi-fire)
+- **ml conversion** โ when the product unit is `ml`, the weight in grams is accepted and a hint is shown: "weight in grams โ will be converted to ml"
+
+---
+
+## Scale Status Indicator
+
+The header of the EverShelf web app shows a real-time scale status icon (โ๏ธ):
+
+| State | Meaning |
+|-------|---------|
+| โ๏ธ green | Connected and ready |
+| โ๏ธ amber | Searching / reconnecting |
+| โ๏ธ grey | Disconnected |
+| โ๏ธ red | Error |
+
+---
+
+## Update Notifications
+
+Every 6 hours the gateway app checks GitHub releases. If a newer version is available, a banner appears with a one-tap download and install.
+
+---
+
+## Troubleshooting
+
+### Scale not appearing in the Bluetooth list
+- Make sure BLE is enabled on the Android device
+- Step on/shake the scale to wake it up (most scales enter sleep mode quickly)
+- Some scales only advertise while someone stands on them
+
+### Weight not appearing in EverShelf
+- Confirm the Gateway URL in EverShelf Settings matches the URL shown in the gateway app
+- Check that the Android device and the EverShelf server are on the same network
+- Tap "Disconnetti / Riconnetti" in the gateway app to refresh the WebSocket connection
+
+### "Mixed content" error in browser
+- Make sure you are accessing EverShelf over HTTPS (not plain HTTP)
+- The SSE relay (`scale_relay.php`) handles the HTTPโWS bridging โ ensure the relay script is reachable
+
+---
+
+## Building from Source
+
+```bash
+cd evershelf-scale-gateway
+./gradlew assembleRelease
+# APK: app/build/outputs/apk/release/app-release.apk
+```
+
+Requires Android Studio or JDK 17+ with the Android SDK.
+
+---
+
+## BLE Protocol Details
+
+The gateway uses the following GATT profile order:
+
+1. **Weight Scale** (`0x181D`) โ standard weight only
+2. **Body Composition** (`0x181B`) โ weight + additional metrics
+3. **Generic fallback** โ subscribes to all notifiable characteristics and applies a heuristic parser that handles byte-order variations used by the majority of consumer smart scales
+
+Weight values are extracted in kg, converted to grams, and broadcast over WebSocket as:
+
+```json
+{ "weight_g": 1234, "stable": true, "unit": "g" }
+```
diff --git a/docs/wiki/Translations.md b/docs/wiki/Translations.md
new file mode 100644
index 0000000..15d70bb
--- /dev/null
+++ b/docs/wiki/Translations.md
@@ -0,0 +1,143 @@
+# ๐ Translations
+
+EverShelf uses JSON translation files in the `translations/` folder. The app auto-detects the browser language on load and falls back to English.
+
+---
+
+## Currently Supported Languages
+
+| Language | File | Status |
+|----------|------|--------|
+| ๐ฎ๐น Italian | `translations/it.json` | โ
Complete (base language) |
+| ๐ฌ๐ง English | `translations/en.json` | โ
Complete |
+| ๐ฉ๐ช German | `translations/de.json` | โ
Complete |
+
+---
+
+## Adding a New Language
+
+### 1. Copy the base file
+
+```bash
+cp translations/it.json translations/fr.json
+```
+
+### 2. Translate all values
+
+Open `fr.json` in your editor and translate every **value** (leave the **keys** unchanged).
+
+```json
+{
+ "app": {
+ "name": "EverShelf",
+ "loading": "Chargement..." โ translate this
+ },
+ "nav": {
+ "title": "๐ EverShelf", โ keep emoji, translate text
+ "home": "Accueil"
+ }
+}
+```
+
+**Rules:**
+- Never change the key names (left side of `:`)
+- Keep `{placeholder}` tokens unchanged โ they are replaced at runtime
+ - Example: `"toast.added": "Added {name} to {location}"` โ keep `{name}` and `{location}`
+- Keep HTML tags if present (rare): ``, `
`
+- Keep emojis (they are part of the UX design)
+- Plurals: some keys have `_one` / `_many` variants โ translate both
+
+### 3. Register the language in the app
+
+Open `assets/js/app.js` and find the `SUPPORTED_LANGUAGES` constant (near the top):
+
+```js
+const SUPPORTED_LANGUAGES = ['it', 'en', 'de'];
+```
+
+Add your language code:
+
+```js
+const SUPPORTED_LANGUAGES = ['it', 'en', 'de', 'fr'];
+```
+
+### 4. Add the language to `translations/` badge list
+
+Update the `README.md` badge:
+
+```markdown
+[](translations/)
+```
+
+### 5. Test
+
+Open the app with `?lang=fr` in the URL to force your language:
+
+```
+http://localhost:8080/?lang=fr
+```
+
+Check for missing keys โ they will show the raw key name in the UI (e.g. `nav.title`).
+
+### 6. Submit a PR
+
+Open a pull request with your new `translations/fr.json` and the updated `app.js` line. See [Contributing](Contributing).
+
+---
+
+## Translation Key Structure
+
+The file is a nested JSON object. Here are the main sections:
+
+| Section | Description |
+|---------|-------------|
+| `app` | General app strings |
+| `nav` | Navigation labels |
+| `btn` | Button labels |
+| `locations` | Storage location names |
+| `categories` | Product category names |
+| `dashboard` | Dashboard section titles |
+| `inventory` | Inventory page strings |
+| `use` | Use/consume form strings |
+| `add` | Add product form strings |
+| `scan` | Barcode scanner strings |
+| `recipes` | Recipe page strings |
+| `cooking` | Cooking mode strings |
+| `shopping` | Shopping list strings |
+| `log` | Transaction log strings |
+| `settings` | Settings page strings |
+| `scale` | Scale integration strings |
+| `toast` | Toast notification messages |
+| `error` | Error messages |
+| `confirm` | Confirmation dialog strings |
+
+---
+
+## Updating Existing Translations
+
+If a new feature adds keys to `it.json` (the base), you need to add the same keys to `en.json` and `de.json`.
+
+The CI pipeline validates that all language files contain the same keys โ a missing key will fail the build.
+
+To check locally:
+
+```bash
+node -e "
+const it = require('./translations/it.json');
+const en = require('./translations/en.json');
+// flatten and compare keys...
+"
+```
+
+Or just open a PR โ CI will flag any missing keys automatically.
+
+---
+
+## Language Detection Order
+
+1. `?lang=xx` URL parameter (forces a specific language)
+2. `localStorage.getItem('lang')` (last manually selected language)
+3. `navigator.language` / `navigator.languages` (browser preference)
+4. Fallback: `en`
+
+Users can change the language in **Settings โ Language**.