## Prüfbericht — 13 Seiten, CLOAKDROP

Geprüft per Skript (Tag‑Bilanz über `html.parser`, ID-/Anker‑Auflösung über alle 20 Dateien, CSS‑Byte‑Vergleich, Klassen‑Toter‑Code, Satz‑Dubletten). Skripte liegen unter `C:\Users\roman\AppData\Local\Temp\claude\c--Users-roman-Downloads-CLOAKDROP\f8f708d6-fcb7-4c81-b3ce-30429d9f9cd4\scratchpad\` (`check_css.py`, `audit.py`, `ids2.py`, `links.py`, `deep.py`, `deadcss.py`).

---

### 1. Tag‑Bilanz und Struktur — sauber

Alle 13 Dateien: **kein einziger unbalancierter Tag**, kein Stray‑Close, kein offenes Element. Keine doppelten IDs, kein `getElementById`/`querySelector` auf eine gelöschte ID (inkl. der `$()`‑Kurzform in `index.html`). Leere Container gibt es nur dort, wo JS sie füllt (`#file-list`, `#hc-res-list`, `#gl-live`) oder wo sie dekorativ sind (`.sq-base`, `.cta-floor`). **Hier ist nichts zu tun.**

Zwei Struktur‑Ausnahmen:
- `index.html` hat **zwei `<h1>`** (Z1688 „Cloak anything." im `#send-view`, Z1810 „Cloaked files," im `#receive-view`). Beide sind im DOM, je nach Modus ist einer `hidden`. Folge der Login‑Entfernung: in der **App‑Ansicht (`#app`) ist gar kein `h1` sichtbar** — oberste Überschrift ist das `h2` „Cloak your files" (Z1721). Fix: `#app-view` bekommt das `h1`, `#send-view`/`#receive-view` gehen auf `h2` herunter.
- Sprung h1 → h3 in `index.html` Z1831 (`This link didn't open`) und `blog.html` Z1705 (`Nothing published here yet`).

---

### 2. Geteilter CSS‑Unterbau — 3 Gruppen statt 1

`check_css.py` über alle 20 Seiten, zweiter `<style>`‑Block, erste 72.269 Zeichen:

| Hash | Seiten |
|---|---|
| `7361297…` | 17 Seiten (about, api, blog, changelog, contact, dashboard, docs, expired, glossary, help, limits, opensource, received, roadmap, settings, status, verify) |
| `1155ee6…` | **imprint.html, privacy.html** |
| `dd3db57…` | **index.html** |

- **privacy.html / imprint.html**: exakt **ein Zeilenumbruch zu viel** an Position 72.263 (`}\n\n\n\n/* =` statt `}\n\n\n/* ==`). Rein kosmetisch, bricht aber die Byte‑Identität — beim nächsten Sync‑Skript ein Fehlalarm. Ein `\n` entfernen.
- **index.html**: der zweite Style‑Block **endet bei 72.262 Zeichen**, d.h. index hat gar kein seitenspezifisches CSS — der „geteilte Unterbau" *ist* index.html's komplettes CSS, die anderen 19 Seiten hängen ihres hinten an. Die geforderte Grenze von 72.269 liegt also 7 Zeichen **hinter** dem Dateiende. **Die belastbare Grenze ist 72.262**, nicht 72.269 — die Prüfregel gehört korrigiert, sonst schlägt sie für index dauerhaft fehl.

**Tote Regeln im geteilten Unterbau** (auf keiner der 20 Seiten im Markup, auch nicht per JS erzeugt), `index.html` Z1240–1352 plus verstreut:

```
.auth-section .auth-card .auth-logo .auth-input .auth-legal .auth-error
.auth-divider .auth-switch .auth-note .btn-auth .btn-social .btn-social .gicon
.btn-blue .btn-gray .field .field-row .field-link .user-row .user-avatar
.price-tag .price-value  .app-section .pipe-gap .mock-days .mock-day-dot(.faded1-3)
```

**5.931 Zeichen × 20 Seiten ≈ 118 KB toter Ballast.** Das ist der komplette Login‑Screen (inkl. Google/GitHub‑Buttons `.btn-social`/`.gicon`), die eingeloggte User‑Zeile und die gestrichene Bezahlversion. `.nl-check` ist **kein** Fund — die Klasse wird in `index.html` Z2591 per `setAttribute` erzeugt.

---

### 3. Der Hauptbefund: die Login‑Entfernung ist nur auf index.html angekommen

`index.html` selbst ist sauber gemacht: kein `#login`/`#signup`, `route()` (Z2949–2968) schickt `#app` direkt in `enterAppMode()`, `setMode()` (Z2408) kennt nur noch `landing`/`app`/`receive`, der Kommentar Z2407 sagt korrekt „keine Konten, kein Login". **Die App‑Ansicht läuft ohne Umweg.** Aber:

**a) 15 Seiten tragen weiterhin zwei tote Nav‑Buttons.** `links.py` löst sie als Anker ohne Ziel auf:

| Datei | Zeilen |
|---|---|
| about 1806/1807 · api 1915/1916 · blog 1687/1688 · changelog 1742/1743 · glossary 1800/1801 · help 1933/1934 · opensource 1720/1721 · privacy 1716/1717 · roadmap 1710/1711 · status 1754/1755 · verify 1770/1771 | `href="index.html#login"` / `href="index.html#signup"` |

Zusätzlich contact 1775/1776, imprint 1775/1776, limits 1787/1788. Klick landet auf der Landingpage mit einem Hash ins Leere — stumm, ohne Fehler. Gleichzeitig fehlt auf allen diesen Seiten der eigentliche Einstieg: `index.html` hat an dieser Stelle `<a href="#app" id="nav-app">Open the app</a>` (Z1665). **Zu tun:** beide Buttons auf allen 15 Seiten durch den einen `Open the app` → `index.html#app` ersetzen.

**b) `docs.html` Z1841 + Z1848** behauptet das Gegenteil des Produkts:
> „**A preview account:** sending requires a quick [sign-up](index.html#signup). … hit *Get started* on the homepage and **log in**."

während `privacy.html` Z1799 sagt „There is no account. Nothing on this site signs you in." und `status.html` Z1813 „There is nothing to sign in to". Der Abschnitt „Before you start" muss umgeschrieben werden.

**c) `help.html` Z2013** — ein Artikel über gelöschte Buttons:
> „Why do the Google and GitHub buttons do nothing?" → `index.html#login`

Wenn er fällt, **stimmen zwei Zähler nicht mehr**: Hero Z1988 „**29 articles**" → 28, und die Karte „The preview build" Z2004 „**6 articles**" → 5. (Alle fünf Kategorie‑Labels 6/6/6/5/6 = 29 stimmen aktuell exakt mit den `.hcart`‑Knoten überein — der Suchindex baut sich aus dem DOM, ist also selbstheilend; nur die beiden Handzahlen nicht.)

Nebenbei: die Karte heißt „**The preview build**", `data-cat` liefert aber „**Preview**" in die Trefferliste — die Vereinheitlichung aus `tasks/todo.md` (Punkt 16) ist halb gemacht.

**d) `roadmap.html` Z1729–1733** listet unter **Shipped** eine Funktion, die es nicht mehr gibt:
> „**Local preview gate** — The sign-up in front of sending lives entirely in this browser's storage."

und Z1752 unter *In progress* „**Real accounts** — Server-side sign-in **to replace the local preview gate**". Beide Karten sind hinfällig. Streicht man die erste, muss `<span class="rdcount">4</span>` in Z1723 auf **3**. (Alle vier Spaltenzähler 4/2/2/4 decken sich sonst exakt mit den `.rdcard`‑Knoten.)

**e) `changelog.html` Z1776** ist der einzige Ort, der die Login‑Gate‑Historie erzählt — legitim für ein Changelog, aber es **fehlt der Eintrag, der sie zurücknimmt**. Solange der nicht existiert, liest sich die Chronik als „Gate kam, Gate ist da".

**f) Toter Routing‑Zweig in `index.html` Z2435–2443.** `enterLandingMode(anchor)` reicht den rohen Hash an `document.querySelector()` weiter:

```js
function enterLandingMode(anchor) {
    setMode('landing');
    if (anchor) {
        var el = document.querySelector(anchor);   // anchor = '#t=abc'
```

Bei einem **abgeschnittenen Drop‑Link** (`#t=abc` ohne `k=`) gibt `parseHash()` `null` zurück, Z2965 ruft `enterLandingMode('#t=abc')`, und `querySelector('#t=abc')` wirft eine `SyntaxError`‑DOMException. `route()` bricht ab, es erscheint kommentarlos die Landingpage — statt der vorgesehenen Meldung „The key doesn't fit". Genau der Fall, der in Chat‑Apps ständig passiert. **Fix:** `try { … } catch(e) {}` oder vorher `/^#[A-Za-z][\w-]*$/` prüfen; im Zweifel `showError()` aufrufen.

---

### 4. Der Cookie‑Dialog widerlegt die Datenschutzseite auf derselben Seite

`privacy.html` Z1760:
> „`cloakdrop-consent` — left over from an earlier build that showed a cookie banner. **That banner is gone**, and nothing here writes or reads this key any more."

`privacy.html` Z1968/1969, 200 Zeilen weiter unten in derselben Datei:
```html
<div class="cookie-pop" id="cookie-pop" role="dialog" …>
  <p class="cookie-text">We use cookies to collect data and improve our services.
```
plus Analytics‑Schalter `#ck-analytics` und `var KEY = 'cloakdrop-consent'` in Z2095.

Der Commit hat den Dialog nur aus den **vier App‑Seiten** entfernt (dashboard, settings, received, expired). Auf **16 Seiten läuft er weiter**, darunter alle 13 geprüften. Und er behauptet Cookies und Analytics auf einer Seite, deren erster Aufzählungspunkt (Z1734) „**No cookies.** Nothing on this site sets one" lautet und deren zweiter „no analytics". Der „Learn more"‑Link zeigt auf `index.html#faq`, wo zu Cookies nichts steht.

**Zu tun:** Dialog auch aus den 16 restlichen Seiten entfernen (dann stimmt privacy Z1760), oder — falls er bleiben soll — Text und privacy‑Passage angleichen. Die erste Variante ist die einzige, die zur Produktgeschichte passt. Danach die Fußzeilen‑Zeile „Cookie settings" (`href="#"`, `id="cookie-settings"`) auf allen 13 Seiten mit entfernen.

---

### 5. Zahlen, die nicht mehr stimmen

| Fund | Stellen |
|---|---|
| **4 MB vs. 4 MiB** — derselbe Wert, zwei Einheiten | **MB:** index 1940, status 1787, about 1904, verify 1838, changelog 1783 · **MiB:** api 1948/2095/2219, glossary 1865/1867, privacy 1771. Der Code sagt `CHUNK_SIZE = 4 * 1024 * 1024` (index Z2139) → **MiB ist richtig**, die fünf MB‑Stellen sind zu korrigieren. |
| **500 MB vs. 3 GB** | index 1971 / api / help 1994 / changelog 1771 / status 1794 / about 1904 sagen **500 MB**, `MAX_TOTAL = 500 * 1024 * 1024` (index Z2140) bestätigt das. **`limits.html` sagt siebenmal „3 GB at most"** — und ist genau die Seite, auf die `help.html` mit „Why does the app stop me at 500 MB?" verlinkt. Harter Widerspruch, hoher Sichtbarkeit. |
| **Blog: „category filters and live search"** | changelog Z1771 und roadmap Z1738 behaupten beides als geliefert. `blog.html` hat **weder Filter noch Suche** — nur den Leerzustand Z1702–1710. Dazu zeigt changelog Z1763 „Read more" auf ebendiese leere Seite. |
| **Frühes Löschen eines Drops** | drei Versionen: `limits.html` „Delete a drop earlier at any time" (existiert), glossary Z1875 „Deleting one early is planned with the relay" (geplant), api Z2094 `DELETE /v1/drops/{id}` „the only revocation" (Entwurf). |
| **„Sixteen terms"** glossary Z1697 | ✅ stimmt: 16 `data-term`, Buchstabenleiste B–Z deckt alle 14 Gruppen ab, keine Leiste ohne Ziel, keine Gruppe ohne Leiste. |
| **„Seven endpoints"** api Z1962 | ✅ 7 `.apiep-row`. |
| **„29 / 6 / 6 / 6 / 5 / 6 articles"** help | ✅ heute exakt — bricht erst, wenn der tote Artikel (3c) fällt. |
| **„Seven localStorage keys"** privacy Z1735 | ✅ 7 Schlüssel gelistet, alle 7 im Code auffindbar (4 in index/dashboard direkt, `cloakdrop-consent` auf 16 Seiten, `-dense`/`-side-collapsed` auf den App‑Seiten). Nur die *Beschreibung* von `-consent` ist falsch (siehe 4). |
| **„four columns / four rows / four stages"** about Z1878, 1897 | ✅ deckt sich mit roadmap. |

---

### 6. Halbe Raster und Inline‑Flicken

- **`index.html` Z1959–1965** — der Rest der gestrichenen Bezahlversion:
  ```html
  <section class="pricing" id="limits">
  <div class="price-grid" style="grid-template-columns: 1fr; max-width: 560px;">
  ```
  Das geteilte `.price-grid` (Z795) ist auf `1fr 1fr` / `max-width:880px` für einen Zwei‑Karten‑Vergleich ausgelegt; die verbliebene einzelne Karte wird per Inline‑Style zurechtgebogen. Dazu Z1978 ein zweiter Flicken `style="justify-content:center; margin-top:24px"`. Die Klasse `pricing` beschreibt nichts mehr, und `.price-tag`/`.price-value` sind tot (siehe 2). **Zu tun:** eigene `.limits-card`‑Regel im geteilten CSS statt zweier Inline‑Styles, Sektion auf `class="limits"` umbenennen, `.price-*`‑Reste löschen.
- **`verify.html` Z1850** — `.vflimit { grid-template-columns: 1fr 1fr }` mit **5** `.vflimit-item`: die letzte Karte steht allein in halber Breite. Entweder ein sechster Punkt, oder `:last-child { grid-column: 1 / -1 }`.
- **`help.html`** — hier ist das Raster **in Ordnung**: `.hcgrid` = 6 Spalten, 4 × `.span3` + 1 × `.offset` (`grid-column: 2 / span 4`), und die Offset‑Karte hat exakt die 6 Artikel, die ihr `grid-template-rows: repeat(3, auto)` × 2 Spalten braucht. Fällt aber der tote Artikel aus Karte 2 („The preview build", 6 → 5), bleibt in *dieser* Karte kein Rasterloch, weil sie `.span3` ist — dort ist es unkritisch. **Nicht** die Offset‑Karte anfassen, sonst reißt `nth-child(3n)` die Trennlinien auf.
- **`roadmap.html`** — das komplette Board (4 Spalten, 12 Karten) benutzt **kein einziges Überschriften‑Element**: Spaltentitel sind `<div class="rdcol-head">`, Kartentitel sind `<b>`. Die Seite hat h1=1, **h2=0, h3=0**. Für Screenreader und Dokumentstruktur ist das Board eine flache Fläche. Spalten → `h2`, Karten → `h3`.
- **`docs.html`** — 13 tote Platzhalter `href="#"` in der Sidebar und im Blätterer: `Guides`, `API (planned)`, `Introduction`, `Receiving files`, `Links & expiry`, **`Accounts`**, `The security model`, `Troubleshooting`, `Previous (Guides)`, `Next (Guides)`, `Star us on GitHub`. Ein vorgetäuschter Dokumentbaum, in dem nichts klickbar ist — und ein Eintrag heißt „Accounts". Das echte Inhaltsverzeichnis (7 Anker) funktioniert dagegen vollständig. Außerdem hat `docs.html` als **einzige** der 13 eine völlig andere Navigation (4 Links: CloakDrop, Blog, Changelog, Star us on GitHub) — von dort kommt man ohne Fußzeile nirgendwohin.
- **Fußzeilen‑Wortmarke** `<a class="wordmark" href="#">` auf allen 13 Seiten — sollte `index.html` sein.
- **Aktiv‑Markierung: drei Konventionen nebeneinander.** `.active` in Nav *und* Fußzeile: api, help, verify, glossary. Nur einmal: about, docs. **Gar nicht:** index, status, changelog, roadmap, opensource, blog. **`aria-current="page"`:** nur `privacy.html` Z1961 — obwohl das geteilte CSS eine `a[aria-current="page"]`‑Regel mitbringt, die sonst nirgends greift. Auf eine Konvention festlegen.

---

### 7. Tote Artefakte aus früheren Durchgängen

- **`assets/og.png` (147 KB)** wird von **keiner** der 20 Seiten referenziert. Es gibt **null** `og:image`, `og:title`, `og:description`, `twitter:card` im ganzen Projekt — die im Browser gerenderte Social‑Karte ist gebaut und nie eingehängt.
- **`favicon.ico`, `assets/icon-*.png`, `apple-touch-icon.png`, `site.webmanifest`** — ebenfalls von keiner Seite verlinkt; alle 20 Seiten nutzen weiterhin nur das inline‑`data:`‑Icon.
- **`index.html` fehlt `<meta name="description">`** — die einzige der 20 Seiten ohne. Alle anderen haben eine.
- **`dashboard.html#soon` / `#downloaded` / `#waiting`** sind auf dashboard, expired, received, settings jeweils Z2903–2905 verlinkt, die Anker existieren nicht. (Außerhalb der 13, aber im selben Auslieferungsstand.)

---

### 8. Tonbrüche und Wiederholungen

- **Der Disclaimer als Tic.** „the relay … **is being built**" steht **19×** auf 11 der 13 Seiten, „relay" insgesamt 57×, „**The encryption is finished**" wörtlich auf 7 Seiten. Der Satz *„The relay that carries a drop from one person to another is being built."* steht **buchstabengleich** in blog Z1699‑Umfeld, changelog Z1757 und docs. Jede Seite beginnt mit derselben Entschuldigung — das liest sich nach der dritten Seite wie ein Copy‑Paste‑Baustein statt nach Ehrlichkeit. **Zu tun:** einen kanonischen Satz definieren, ihn auf index/status/roadmap/privacy stehen lassen und auf den übrigen Seiten durch einen Halbsatz oder eine Verlinkung ersetzen.
- **„Four X" als Formel.** `about.html`: „**Four** rules", „**four** columns", „**four** rows", „**four** stages", „**Four** omissions", „**four** checks", „**Those four points**" — sieben Mal auf einer Seite. `verify.html`: „**Four** checks" im Hero, als Kicker Z1801 *und* im Fließtext „**Four checks are four checks**" Z1846. `privacy.html` Z1741 „Those four points". Zwei bis drei davon streichen.
- **Schreibweise uneinheitlich:** „**zero knowledge**" (index Z2003, glossary — dort sogar Stichwort) vs. „**zero-knowledge**" (opensource Z1697). Auf die Glossar‑Schreibweise vereinheitlichen.
- **„preview build" vs. „Preview"** — docs (2×), help (2×), verify, glossary. `tasks/todo.md` Punkt 16 hatte „Preview" beschlossen; help zeigt beides gleichzeitig (Kartentitel „The preview build", Trefferlabel „Preview").
- **Widerspruch Fußzeile ↔ Statusseite:** die geteilte Fußzeile trägt auf **16 Seiten** die Zeile „**All systems operational**" mit pulsierendem grünem Punkt und verlinkt auf `status.html`, dessen `h1` „**Nothing to monitor yet**" lautet und dessen erster Kasten heißt „A status page with nothing to measure". Ein Verfügbarkeits‑Versprechen für Infrastruktur, die es nach eigener Aussage nicht gibt — auf einer Seite, deren ganzes Argument „wir behaupten nichts Unprüfbares" ist. Die stärkste Tonverletzung im Satz. Ersetzen durch z.B. „Preview — nothing to monitor".
- **Widerspruch Nav ↔ opensource:** die Navigation bietet „**Star us on GitHub**" (`href="#"`), die Fußzeile ein GitHub‑Icon (`href="#"`), während `opensource.html` Z1700 schreibt „There is nowhere to clone from, **nothing to star**, nothing to audit."

**Positiv:** über alle 13 Seiten gibt es **nur einen** wörtlich doppelten Absatz‑Satz und **keinen** innerhalb einer Seite. Redundanz ist ein Formulierungs‑, kein Copy‑Paste‑Problem.

---

### 9. Reihenfolge zum Abarbeiten

1. Cookie‑Dialog aus den 16 verbleibenden Seiten entfernen (löst privacy Z1760 auf) — **inhaltlich der schwerste Widerspruch.**
2. „Log in"/„Sign up" auf 15 Seiten durch „Open the app" → `index.html#app` ersetzen.
3. `limits.html` 3 GB → 500 MB (oder umgekehrt den Code), sonst zeigt help.html auf eine widersprechende Antwort.
4. Fußzeile „All systems operational" entschärfen.
5. `docs.html` Z1841/1848 (Preview‑Account) umschreiben; die 13 toten `#`‑Sidebar‑Links entweder verdrahten oder entfernen.
6. `help.html` Z2013 löschen, Zähler Z1988 auf 28 und Z2004 auf 5.
7. `roadmap.html` Z1730 löschen + Z1723 auf 3, Z1752 „Real accounts" neu formulieren; Spalten/Karten auf `h2`/`h3`.
8. Einheiten vereinheitlichen: 4 MB → 4 MiB an 5 Stellen.
9. `index.html` Z2438 `querySelector` absichern; `h1` in die App‑Ansicht.
10. `index.html` Z1959–1978 entpflastern; `.price-*`/`.auth-*`/`.btn-social`/`.user-*` aus dem geteilten CSS (≈118 KB über 20 Seiten) — **danach die 20 Dateien neu synchronisieren und `check_css.py` mit Grenze 72.262 laufen lassen.**
11. privacy/imprint: ein `\n` bei 72.263 entfernen.
12. OG‑Tags + Icon‑Links + `site.webmanifest` einhängen, `index.html` Meta‑Description ergänzen.
13. Blog: entweder Filter/Suche bauen (dann stimmen changelog Z1771 und roadmap Z1738) oder beide Behauptungen streichen; changelog Z1763 „Read more" nicht auf die leere Seite zeigen lassen.
14. Disclaimer‑Boilerplate und die „Four X"‑Formel entschlacken.