diff --git a/README.md b/README.md
index baea0ac..07ce857 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,97 @@
# d4rk_divegear
Tauchausrüstung für QBox. Fork von [qbx_divegear](https://github.com/Qbox-project/qbx_divegear)
-mit Flaschengrößen, gesyncter Lampe, Tiefenanzeige und Ausrüstung, die bei
-Animationen nicht verschwindet.
+mit Flaschengrößen, gesyncter Lampe, Tiefen- und Druckanzeige — und Ausrüstung,
+die bei Animationen nicht mehr verschwindet.
+
+## HUD
+
+> Die Bilder sind maßstabsgetreue Renderings des Overlays aus denselben Werten wie
+> `nui/style.css` — keine In-Game-Aufnahmen. Erzeugt mit `docs/gen-hud-svg.mjs`.
+
+**Normal** — Luft, Tiefe, Fülldruck, Lampe an
+
+
+
+**Warnung** — Restluft unter der ersten Schwelle, Tiefe über `deepWarningDepth`,
+Druck unter `reservePressure`
+
+
+
+**Kritisch** — letzte Sekunden, der Ring pulsiert
+
+
+
+Das HUD erscheint nur mit angelegter Ausrüstung unter Wasser und blendet sich
+weich ein und aus.
## Features
- **Vier Flaschengrößen** — 5, 10, 15 und 20 Minuten. Die Restluft liegt in der
Item-Metadata und überlebt Ablegen, Relog, Resource-Restart und Tod. Der
Füllstand ist als Balken direkt im Inventar-Slot sichtbar.
-- **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste (Standard `L`).
- Andere Spieler sehen den Lichtkegel.
-- **NUI-HUD** — Restluft als Ring mit `mm:ss`, Tauchtiefe in Metern und
- Manometer in bar, mit Warnstufen. Nur sichtbar mit angelegter Ausrüstung
- unter Wasser, und ausblendbar für Unterwasser-Screenshots.
-- **Tiefenabhängiger Verbrauch** — tiefer tauchen kostet mehr Luft. Abschaltbar.
+- **Manometer in bar** — physikalisch korrekt aus dem Restanteil abgeleitet,
+ Reservedruck wird eingefärbt.
+- **Gesyncte Tauchlampe** — an der Ausrüstung, An/Aus per Taste. Andere Spieler
+ sehen den Lichtkegel, und zwar in die Richtung, in die tatsächlich geschaut wird.
+- **Tiefenanzeige** und **tiefenabhängiger Verbrauch** — tiefer tauchen kostet
+ mehr Luft. Abschaltbar.
- **Ausrüstung bleibt** — bei Emotes, Ragdoll, Kleiderwechsel und aufräumenden
Fremd-Scripts.
+- **HUD ausblendbar** für Unterwasser-Screenshots.
- **Serverseitige Prüfung** — die Restluft kann nur sinken, und nicht schneller
als physikalisch möglich.
## Abhängigkeiten
-`qbx_core` · `ox_lib` · `ox_inventory`
+[`qbx_core`](https://github.com/Qbox-project/qbx_core) ·
+[`ox_lib`](https://github.com/overextended/ox_lib) ·
+[`ox_inventory`](https://github.com/overextended/ox_inventory)
## Installation
-1. Resource nach `resources/` und in der `server.cfg` starten.
-2. Die Einträge aus [`items_for_ox_inventory.lua`](items_for_ox_inventory.lua) in
- `ox_inventory/data/items.lua` kopieren.
-3. Item-Icons nach `cdn.d4rkst3r.de/items/` legen (`diving_gear.png`,
- `diving_fill.png`, `diving_tank_small.png` … `diving_tank_xl.png`).
+1. Ordner nach `resources/` und in der `server.cfg` starten — **nach**
+ `ox_inventory` und `qbx_core`:
+
+ ```cfg
+ ensure d4rk_divegear
+ ```
+
+2. [`items_for_ox_inventory.lua`](items_for_ox_inventory.lua) komplett markieren
+ und in `ox_inventory/data/items.lua` innerhalb der `return { … }`-Klammer
+ einfügen. Sechs Items, nichts weiter anzupassen.
+
+3. Icons nach `cdn.d4rkst3r.de/items/` legen: `diving_gear.png`,
+ `diving_fill.png`, `diving_tank_small.png`, `diving_tank_medium.png`,
+ `diving_tank_large.png`, `diving_tank_xl.png`.
+
+Es gibt bewusst **keine** fertige `items.lua` zum Ersetzen — die würde die
+restlichen Items des Servers überschreiben.
+
+## Items
+
+| Item | Label | Laufzeit | Fülldruck | Gewicht |
+|---|---|---|---|---|
+| `diving_gear` | Tauchausrüstung | — | — | 5000 |
+| `diving_fill` | Pressluft-Kartusche | — | — | 1000 |
+| `diving_tank_small` | Tauchflasche (5 Min) | 300 s | 300 bar | 4000 |
+| `diving_tank_medium` | Tauchflasche (10 Min) | 600 s | 300 bar | 7000 |
+| `diving_tank_large` | Tauchflasche (15 Min) | 900 s | 300 bar | 10000 |
+| `diving_tank_xl` | Tauchflasche (20 Min) | 1200 s | 300 bar | 13000 |
+
+Dass alle Flaschen mit demselben Fülldruck starten, ist gewollt: in echt macht das
+Volumen die Laufzeit, nicht der Druck. Die große Flasche hält 300 bar einfach länger.
+
+### Metadata
+
+| Key | Bedeutung |
+|---|---|
+| `oxygen` | Restluft in Sekunden — die eigentliche Wahrheit |
+| `durability` | Füllstand in Prozent, nur für den Balken im Inventar-Slot |
+
+**Kein `decay = true` auf den Flaschen.** ox_inventory löscht Items mit `decay`,
+sobald `durability` 0 erreicht — die leere Flasche würde also verschwinden statt
+auffüllbar zu bleiben.
## Bedienung
@@ -38,46 +99,124 @@ Animationen nicht verschwindet.
|---|---|
| Ausrüstung an-/ausziehen | `diving_gear` benutzen |
| Flasche wählen | Menü beim Anlegen (entfällt bei nur einer Flasche) |
-| Flasche auffüllen | `diving_fill` benutzen, nicht unter Wasser |
-| Lampe an/aus | `L` (in den FiveM-Tastenbelegungen umlegbar) |
-| HUD ausblenden | Eigene Taste, standardmäßig unbelegt — in den FiveM-Tastenbelegungen unter "FiveM" setzen |
+| Flasche auffüllen | `diving_fill` benutzen — nicht unter Wasser, nicht bei voller Flasche |
+| Lampe an/aus | `L` |
+| HUD ein-/ausblenden | Eigene Taste, standardmäßig unbelegt |
-### HUD ausblenden für Screenshots
-
-Drei Wege, damit es unabhängig von der eingesetzten HUD-Resource funktioniert:
-
-1. **Automatisch** — das Tauch-HUD verschwindet mit dem Spiel-HUD. Greift bei
- allem, was `DisplayHud(false)` setzt (Screenshot-Scripts, HUD-Toggles,
- Cinematic-Modes). Abschaltbar über `hudFollowGameHud`.
-2. **Eigene Taste** — `hudToggleKey` in der Config, oder vom Spieler selbst belegt.
-3. **Export** — für andere Resources:
-
-```lua
-exports.d4rk_divegear:setHudVisible(false)
-local shown = exports.d4rk_divegear:isHudVisible()
-```
+Beide Tasten sind vom Spieler in den FiveM-Einstellungen unter *Tastenbelegung →
+FiveM* umlegbar.
## Konfiguration
-| Datei | Inhalt |
-|---|---|
-| [`config/client.lua`](config/client.lua) | Timings, Verbrauch, Tiefe, HUD, Lampe |
-| [`config/shared.lua`](config/shared.lua) | Flaschen und ihre Laufzeit, Sync-Intervall, Anti-Cheat-Grenze |
+### `config/shared.lua` — Client und Server
-Laufzeiten ändert man über `capacity` (Sekunden) in `config/shared.lua`. Wer den
-Tiefenfaktor (`maxDecayFactor`) hochdreht, muss `maxDrainPerSecond` mitziehen —
-sonst schreibt der Server ehrlichen Spielern in großer Tiefe Luft zurück.
+| Option | Default | Bedeutung |
+|---|---|---|
+| `tanks` | 4 Flaschen | `name`, `capacity` (Sekunden), `pressure` (bar) |
+| `syncIntervalSeconds` | `15` | Wie oft die Restluft an den Server gemeldet wird. Bei einem Crash gehen maximal so viele Sekunden verloren |
+| `maxDrainPerSecond` | `5.0` | Obergrenze der serverseitigen Prüfung |
+
+### `config/client.lua`
+
+**Ablauf**
+
+| Option | Default | Bedeutung |
+|---|---|---|
+| `putOnSuitTimeMs` / `takeOffSuitTimeMs` / `refillTankTimeMs` | `5000` | Dauer der Progressbars |
+| `decayRate` | `1.0` | Sekunden Luft pro echter Sekunde, vor dem Tiefenfaktor |
+| `warnAtSeconds` | `{60, 30, 10}` | Warnschwellen, jede einmal pro Tauchgang |
+| `removeGearOnDeath` | `true` | Ausrüstung beim Tod abnehmen. Die Flasche bleibt mit ihrer Restluft im Inventar |
+| `gearWatchdogIntervalMs` | `1000` | Prüfintervall, ob die Props noch am Ped hängen |
+| `maxTimeUnderwater` | `50.0` | Sekunden unter Wasser ohne Ausrüstung (Vanilla-Verhalten) |
+| `maxTimeUnderwaterOutOfAir` | `1.0` | Sekunden, sobald die Flasche leer ist |
+
+**Tiefe**
+
+| Option | Default | Bedeutung |
+|---|---|---|
+| `depthDecayEnabled` | `true` | Verbrauch steigt mit der Tiefe |
+| `depthDecayReference` | `30.0` | Faktor `1 + tiefe / referenz` |
+| `maxDecayFactor` | `4.0` | Deckel für den Faktor |
+| `deepWarningDepth` | `25.0` | Ab hier wird die Tiefenanzeige eingefärbt |
+
+**HUD**
+
+| Option | Default | Bedeutung |
+|---|---|---|
+| `hudEnabled` | `true` | Ohne HUD bleibt die Ausrüstung voll nutzbar |
+| `hudUpdateIntervalMs` | `250` | 4 Updates pro Sekunde |
+| `hudFollowGameHud` | `true` | Mit dem Spiel-HUD ausblenden |
+| `hudToggleKey` | `''` | Eigene Taste, leer = unbelegt |
+| `reservePressure` | `50` | Ab hier wird die Druckanzeige rot |
+
+**Lampe**
+
+| Option | Default | Bedeutung |
+|---|---|---|
+| `lampKey` | `'l'` | Vorbelegung |
+| `lampProp` | `'prop_cs_police_torch'` | Optik. Ungültiges Model → Warnung im Log, Licht funktioniert trotzdem |
+| `lampOffset` | `vec3(0, 0.15, 0)` | Position relativ zum Kopf-Bone |
+| `lampDirection` | `vec3(0, 1, 0)` | Richtung des Kegels, bone-relativ |
+| `lampColour` | `{225, 245, 255}` | RGB |
+| `lampDistance` / `lampBrightness` / `lampRoundness` / `lampRadius` / `lampFalloff` | `30/6/1/14/1.5` | Form des Lichtkegels |
+| `lampShadows` | `true` | `false` nutzt `DrawSpotLight` statt der Schattenvariante — flacher, aber spürbar schneller |
+| `lampDrawDistance` | `45.0` | Ab hier werden fremde Lampen nicht mehr gezeichnet |
+| `lampMaxLights` | `4` | Gleichzeitige Lampen, die nächstgelegenen gewinnen |
+
+> **Beim Balancing beachten:** `maxDrainPerSecond` (shared) muss über
+> `maxDecayFactor` (client) liegen. Sonst hält der Server den ehrlichen Verbrauch
+> in großer Tiefe für Cheat und schreibt Luft zurück.
+
+## Exports
+
+```lua
+exports.d4rk_divegear:setHudVisible(false) -- HUD ausblenden
+local shown = exports.d4rk_divegear:isHudVisible()
+```
+
+Gedacht für HUD- und Screenshot-Resources. Zusätzlich folgt das HUD automatisch
+`IsHudHidden()`, greift also bei allem was `DisplayHud(false)` setzt, ohne dass
+etwas angebunden werden muss.
+
+## Wie die Lampe synchron wird
+
+Licht ist in GTA nie synchronisiert — `DrawSpotLight` zeichnet nur lokal für den
+aktuellen Frame. Der Lampenzustand wird deshalb als Statebag repliziert
+(`LocalPlayer.state:set('divelight', …, true)`), und jeder Client zeichnet die
+Lampen aller Taucher in seiner Nähe selbst.
+
+Die Richtung kommt aus **zwei bone-relativen Punkten** am Kopf-Bone, nicht aus
+`GetEntityForwardVector`: beim Tauchen liegt der Ped waagerecht im Wasser, der
+Entity-Forward würde stur horizontal leuchten. Die Offsets von `GetPedBoneCoords`
+sind laut Native-Doku *"relative to the bone's rotation"*, die Differenz zweier
+solcher Punkte enthält also den Pitch.
## Entwicklung
```bash
-node ../.tools/lint.mjs # Lua-Lint (CfxLua-tauglich)
-node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen
+node ../.tools/lint.mjs # Lua-Lint, CfxLua-tauglich
+node ../.tools/nui-preview.mjs nui # HUD im Browser anschauen
+node docs/gen-hud-svg.mjs docs # Renderings für diese README neu bauen
```
Der Umbau ist in [PLAN.md](PLAN.md) dokumentiert, inklusive der offenen Punkte.
+## Bekannte Unsicherheiten
+
+Bisher **nicht im Spiel getestet** — geprüft sind Lua-Syntax und Globals, die
+Locale-JSONs und das HUD im Browser.
+
+- **`lampProp`** — der Prop-Name ist ungeprüft. Falls falsch: Warnung im Log, das
+ Licht funktioniert weiter, nur ohne sichtbares Prop. Ersatz finden:
+ [forge.plebmasters.de/objects](https://forge.plebmasters.de/objects)
+- **`lampDirection`** — welche lokale Achse des Kopf-Bones nach vorne zeigt, hängt
+ am Skelett. Leuchtet der Kegel falsch herum: Achse tauschen oder Vorzeichen drehen.
+- **`IsHudHidden()`** — ob die Native auf `DisplayHud(false)` anspringt, ist
+ ungetestet. Falls nicht, greifen Taste und Export weiterhin.
+- Nach einem Relog muss die Ausrüstung neu angelegt werden. Die Restluft geht
+ dabei nicht verloren, die liegt auf der Flasche.
+
## Lizenz
-GPL-3.0, wie das Original. Upstream-Historie ist erhalten und liegt auf dem
+GPL-3.0, wie das Original. Die Upstream-Historie ist erhalten und liegt auf dem
Remote `upstream`.
diff --git a/docs/gen-hud-svg.mjs b/docs/gen-hud-svg.mjs
new file mode 100644
index 0000000..3e84047
--- /dev/null
+++ b/docs/gen-hud-svg.mjs
@@ -0,0 +1,111 @@
+#!/usr/bin/env node
+// Erzeugt die HUD-Renderings für die README aus denselben Werten wie nui/style.css.
+// Keine In-Game-Screenshots - eine massstabsgetreue Nachbildung des Overlays.
+//
+// Nutzung: node docs/gen-hud-svg.mjs [ziel-ordner] (default: docs)
+
+import { writeFileSync, mkdirSync } from 'node:fs';
+import { join } from 'node:path';
+
+const OUT = process.argv[2] ?? 'docs';
+mkdirSync(OUT, { recursive: true });
+
+const C = {
+ accent: '#38bdf8',
+ warn: '#fbbf24',
+ danger: '#f87171',
+ text: '#e2e8f0',
+ muted: '#94a3b8',
+ panel: '#09121c',
+ border: '#94a3b8'
+};
+
+// Geometrie spiegelt das Grid aus style.css:
+// padding 14/22, Spalten 84 | 92 | 84 mit column-gap 22, row-gap 8, Lampe 23 hoch.
+const PAD_X = 22, PAD_Y = 14, COL = 84, GAUGE = 92, GAP = 22, ROW_GAP = 8, LAMP_H = 23;
+const PW = PAD_X * 2 + COL * 2 + GAUGE + GAP * 2;
+const PH = PAD_Y * 2 + GAUGE + ROW_GAP + LAMP_H;
+
+const W = 520, H = PH + 70;
+const PX = (W - PW) / 2, PY = (H - PH) / 2;
+
+const GX = PX + PAD_X + COL + GAP;
+const CX = GX + GAUGE / 2, CY = PY + PAD_Y + GAUGE / 2;
+
+const R = 52 * (GAUGE / 120); // r=52 im viewBox 120, auf 92px skaliert
+const SW = 8 * (GAUGE / 120);
+const CIRC = 2 * Math.PI * R;
+
+const DEPTH_X = PX + PAD_X + COL; // rechtsbündig, zeigt zum Ring
+const PRESS_X = GX + GAUGE + GAP; // linksbündig, zeigt zum Ring
+const CAP_Y = PY + PAD_Y + 35;
+const VAL_Y = PY + PAD_Y + 61;
+
+const FONT = "'Segoe UI',Roboto,system-ui,sans-serif";
+const fmt = (s) => `${String(Math.floor(s / 60)).padStart(2, '0')}:${String(Math.floor(s % 60)).padStart(2, '0')}`;
+
+function readout(x, anchor, label, value, unit, colour) {
+ const unitTspan = `