# Blackfile — jak przygotować sprawę

**Instrukcja dla twórców · format paczki `.case.zip`, schemaVersion 1.1.0**

Ta instrukcja prowadzi krok po kroku od pomysłu do opublikowanej sprawy. Nie musisz umieć programować — sprawa to dwa pliki tekstowe w formacie JSON i folder ze zdjęciami, nagraniami i dokumentami. Wszystko pakujesz do jednego archiwum ZIP i wgrywasz w serwisie.

Materiały pomocnicze (do pobrania na stronie **blackfile.pl/format**):

- `manifest.template.json` — szablon treści sprawy,
- `solution.template.json` — szablon rozwiązania,
- `przyklad-mapa-ktorej-nie-bylo.case.zip` — kompletna, opublikowana sprawa jako wzór (zawiera rozwiązanie — najpierw w nią zagraj).

---

## Spis treści

1. Jak działa sprawa w Blackfile
2. Etap 1 — zaprojektuj sprawę, zanim zaczniesz pisać
3. Etap 2 — przygotuj folder sprawy
4. Etap 3 — wypełnij `manifest.json`
5. Etap 4 — bloki treści, czyli jak budować dokumenty
6. Etap 5 — przygotuj materiały (zdjęcia, audio, wideo, PDF)
7. Etap 6 — wypełnij `solution.json`
8. Etap 7 — spakuj sprawę do ZIP
9. Etap 8 — wgraj paczkę i popraw błędy
10. Etap 9 — przetestuj sprawę w podglądzie
11. Etap 10 — wyślij do moderacji i publikuj kolejne wersje
12. Lista kontrolna przed wysłaniem
13. Najczęstsze błędy i jak je naprawić
14. Dodatek: podstawy JSON w 5 minut

---

## 1. Jak działa sprawa w Blackfile

Gracz przechodzi przez stały schemat — niezależnie od tego, kto przygotował sprawę:

1. **Ekran startowy** — tytuł, krótki wstęp, karta akt.
2. **Briefing** — co się stało i jakie jest zadanie gracza.
3. **Dowody** — lista dokumentów; każdy otwiera się jak kartka z akt (tekst, zdjęcia, nagrania, tabele, szkice, PDF).
4. **Podejrzani** — karty osób z rolą, zeznaniem i alibi.
5. **Notatnik** — prywatne notatki gracza i jawna oś czasu.
6. **Wskazówki** — odsłaniane pojedynczo, obniżają wynik.
7. **Zamknięcie akt** — nieodwracalne; od tej chwili gracz nie otworzy żadnego dowodu.
8. **Werdykt** — gracz odpowiada na Twoje pytania (np. kto, jak, dlaczego, które dowody) — tylko raz.
9. **Wynik i rozwiązanie** — punkty, poprawne odpowiedzi, Twoje wyjaśnienie i łańcuch dedukcji.

Ty dostarczasz **treść**, a serwis dba o wygląd, zapis postępu, blokadę akt i liczenie punktów. Nie da się wgrać własnego HTML, CSS ani skryptów — i nie jest to potrzebne.

Sprawa składa się z dwóch części:

| Plik | Co zawiera | Kto to widzi |
|---|---|---|
| `manifest.json` | wszystko, co widzi gracz: opis, briefing, podejrzanych, dowody, oś czasu, pytania werdyktu, wskazówki | gracz |
| `solution.json` | poprawne odpowiedzi, punktację, łańcuch dedukcji i wyjaśnienie | nikt przed werdyktem — serwer trzyma je osobno |

---

## 2. Etap 1 — zaprojektuj sprawę, zanim zaczniesz pisać

Najczęstszy błąd początkujących: najpierw piszą dokumenty, potem szukają rozwiązania. W Blackfile pracuj odwrotnie — **dokumenty mają wynikać z prawdy sprawy**. Przejdź po kolei przez osiem kroków (np. w zwykłym notatniku lub arkuszu).

### Krok 1. Prawda sprawy (tylko dla Ciebie)

Zapisz w 3–5 zdaniach: **co się stało, kto to zrobił, jak i dlaczego**. To trafi później do `solution.json` i nigdy nie będzie widoczne przed werdyktem.

> Przykład: *Konserwatorka Marta Kulesza o 17:51 podmieniła oryginał mapy na kopię, korzystając z wydanego jej klucza, i wysłała oryginał kurierem kupcowi za 24 000 zł. Przerwa w kamerze o 19:42 to zaplanowany test — fałszywy trop.*

### Krok 2. Oś czasu — trzy różne czasy

Dla każdego ważnego zdarzenia rozróżnij:

- **czas zdarzenia** — kiedy naprawdę coś się stało (17:51 podmiana),
- **czas powstania dokumentu** — kiedy powstał ślad (18:14 zdjęcie inwentarzowe),
- **czas odkrycia** — kiedy śledczy się o tym dowiedzieli (19 września, analiza laboratorium).

Pomylenie tych trzech czasów to najczęstsze źródło sprzeczności w sprawach.

### Krok 3. Podejrzani

Zaplanuj **od 2 do ok. 6** osób. Dla każdej zapisz: motyw, możliwość działania, dostęp (klucze, hasła, obecność), alibi i treść zeznania. Każdy niewinny podejrzany powinien mieć **powód, by go podejrzewać**, i **sposób, by go wykluczyć** na podstawie akt.

Zasada nadrzędna: **wygląd nie jest dowodem.** Portret to wyłącznie klimat. Rozwiązanie nie może zależeć od wyglądu, płci, wieku, pochodzenia, ubioru czy „podejrzanej miny”.

### Krok 4. Macierz dowodów

Minimum to **5 dowodów**, dobra sprawa na 30–60 minut ma zwykle **10–20**. Dla każdego dowodu zapisz w tabeli:

| Dowód | Co potwierdza | Czego NIE potwierdza | Których faktów dotyczy | Rola |
|---|---|---|---|---|
| Porównanie zdjęć | o 18:14 w szafce była kopia | kto podmienił | czas kradzieży | trop główny |
| Raport kamery | przerwa 19:42–19:43 | że wtedy była kradzież | alibi technika | fałszywy trop |
| Ślad obuwia | ktoś przechodził przez próg | kto i kiedy | — | neutralny |

Rola to: **trop główny**, **neutralny** (tło, realizm) albo **mylący**. Ta tabela zostaje u Ciebie — w manifeście nie wolno oznaczać, które dowody są ważne.

### Krok 5. Łańcuch dedukcji

Dla każdego pytania werdyktu napisz, **jak gracz ma dojść do odpowiedzi**. Każdy kluczowy wniosek musi opierać się na **co najmniej dwóch niezależnych dowodach**. Fałszywy trop musi dać się wyjaśnić po poznaniu prawdy.

### Krok 6. Wskazówki (zwykle 3)

Od najsłabszej do najmocniejszej:

1. przypomnienie o metodzie („Nie zakładaj, że kradzież nastąpiła w chwili zgłoszenia”),
2. wskazanie dokumentów („Porównaj zdjęcia z 16:52 i 18:14”),
3. mocna podpowiedź („Sprawdź, z jakiego konta zamówiono kuriera i kto miał wtedy klucz”).

Wskazówka **nigdy nie wprowadza nowego faktu**, którego nie ma w aktach.

### Krok 7. Wyjaśnienie końcowe

Opisz sprawę prostym językiem, odwołując się do numerów dowodów. Gracz zobaczy je dopiero po werdykcie.

### Krok 8. Test

Na końcu daj sprawę do rozwiązania komuś, kto nie zna rozwiązania, i nie podpowiadaj. Więcej w Etapie 9.

---

## 3. Etap 2 — przygotuj folder sprawy

Utwórz na komputerze folder, np. `zaginiecie-nad-jeziorem`, o takiej strukturze:

```text
zaginiecie-nad-jeziorem/
├── manifest.json
├── solution.json
├── README.md                  (opcjonalnie — notatka dla moderatora)
└── assets/
    ├── images/
    │   └── cover.jpg          okładka sprawy
    ├── suspects/
    │   ├── anna.jpg           portrety podejrzanych
    │   └── piotr.jpg
    └── evidence/
        ├── zdjecie-pomostu.jpg
        ├── rozmowa-911.mp3
        ├── kamera-parking.mp4
        ├── mapa-okolicy.png
        └── raport-sekcji.pdf
```

Zasady nazewnictwa plików:

- tylko **małe litery bez polskich znaków, cyfry, myślnik, podkreślnik i kropka**: `zdjecie-pomostu.jpg`, nie `Zdjęcie pomostu (1).JPG`,
- bez spacji,
- nazwa nie może zdradzać rozwiązania (`morderca-anna.jpg` — źle).

Podfoldery wewnątrz `assets/` są dowolne — powyższy podział to tylko propozycja.

Pliki JSON edytuj w edytorze tekstu, który zapisuje w kodowaniu **UTF-8** — najlepiej darmowym **Visual Studio Code** (podświetla błędy składni JSON). Notatnik systemu Windows też zadziała, ale łatwiej w nim o pomyłkę. Nie używaj Worda.

Najprostszy start: pobierz przykładową paczkę, rozpakuj, zmień nazwę folderu i podmieniaj treści krok po kroku.

---

## 4. Etap 3 — wypełnij `manifest.json`

Manifest to jeden duży obiekt JSON z sekcjami. Poniżej każda sekcja po kolei. Pola oznaczone **(wymagane)** muszą się znaleźć — inaczej walidator zgłosi błąd blokujący.

### 4.1. `schemaVersion` (wymagane)

```json
"schemaVersion": "1.1.0"
```

Zawsze dokładnie ten tekst.

### 4.2. `metadata` — dane do katalogu

```json
"metadata": {
  "id": "zaginiecie-nad-jeziorem",
  "slug": "zaginiecie-nad-jeziorem",
  "title": "Zaginięcie nad Jeziorem Czarnym",
  "caseNumber": "014/26",
  "description": "Młoda przewodniczka nie wróciła z wieczornego rejsu. Łódź przybiła do brzegu pusta.",
  "language": "pl",
  "difficulty": 3,
  "durationMinutes": { "min": 45, "max": 90 },
  "players": { "min": 1, "max": 4 },
  "tags": ["zaginięcie", "jezioro", "współczesna"],
  "contentWarnings": ["śmierć"],
  "location": "Jezioro Czarne",
  "date": "2026-07-14",
  "coverAssetId": "cover",
  "author": { "name": "Twoje imię lub pseudonim" },
  "fictionDisclaimer": "Wszystkie postacie, miejsca i dokumenty są fikcyjne."
}
```

| Pole | Opis |
|---|---|
| `id` **(wymagane)** | wewnętrzny identyfikator; najprościej taki sam jak `slug` |
| `slug` **(wymagane)** | adres sprawy: `blackfile.pl/sprawy/<slug>`; tylko `a–z`, `0–9` i myślniki, bez polskich znaków; musi być unikalny w serwisie |
| `title` **(wymagane)** | tytuł bez spoilerów |
| `description` **(wymagane)** | 1–3 zdania do katalogu; **bez nazwiska sprawcy i bez rozwiązania** |
| `language` **(wymagane)** | `"pl"` lub `"en"` |
| `difficulty` **(wymagane)** | liczba całkowita 1–5 (1–2 łatwy, 3 średni, 4 wyższy, 5 ekspert) |
| `durationMinutes` **(wymagane)** | przewidywany czas gry w minutach |
| `players` **(wymagane)** | liczba graczy przy jednym ekranie |
| `tags` | słowa kluczowe do wyszukiwarki |
| `contentWarnings` | ostrzeżenia treści (przemoc, śmierć, samobójstwo…) — wyświetlane w katalogu |
| `coverAssetId` | identyfikator okładki z listy `assets` (bez okładki pojawi się zdjęcie zastępcze) |
| `caseNumber`, `location`, `date` | klimatyczne dane akt — wyświetlane w grze |
| `author.name` | podpis autora |
| `fictionDisclaimer` | informacja o fikcyjności |

### 4.3. `gameplay` — ustawienia rozgrywki

```json
"gameplay": {
  "requireEvidenceLock": true,
  "lockConfirmation": {
    "title": "Czy zamknąć akta?",
    "text": "Po zatwierdzeniu nie wrócisz do dokumentów w tej rozgrywce."
  },
  "maxHints": 3,
  "hintPenaltyNotice": "Każda ujawniona podpowiedź obniży wynik o 5 punktów.",
  "featuredEvidenceIds": ["zgloszenie", "protokol-ogledzin", "mapa"],
  "evidenceGroups": [
    { "id": "protocol", "label": "Protokoły" },
    { "id": "testimony", "label": "Zeznania" },
    { "id": "technical", "label": "Technika" },
    { "id": "trace", "label": "Ślady" }
  ]
}
```

| Pole | Opis |
|---|---|
| `requireEvidenceLock` | `true` (zalecane) — werdykt dopiero po zamknięciu akt |
| `lockConfirmation` | tytuł i treść okna potwierdzenia zamknięcia akt |
| `maxHints` | ile wskazówek gracz może odsłonić |
| `hintPenaltyNotice` | tekst o karze za wskazówki (sama kara jest w `solution.json`) |
| `featuredEvidenceIds` | 3 dowody pokazywane na ekranie briefingu jako „najważniejsze akta” — wybierz startowe, nie kluczowe dla rozwiązania |
| `evidenceGroups` | filtry na liście dowodów; każdy dowód przypisujesz do grupy polem `group` |

### 4.4. `briefing` — wprowadzenie

```json
"briefing": {
  "intro": {
    "eyebrow": "Akta sprawy 014/26",
    "title": "Zaginięcie",
    "titleEmphasis": "nad Jeziorem Czarnym",
    "lead": "Łódź przybiła do brzegu pusta. Na pomoście zostały buty i telefon.",
    "note": { "title": "Notatka na okładce", "text": "Kto ostatni widział ją żywą?" },
    "fileCard": {
      "kicker": "Komenda Powiatowa · Jezioro Czarne",
      "title": "Sprawa 014/26",
      "lines": ["Zgłoszenie zaginięcia · 14 lipca 2026", "Sygnatura: KP/0714/3"],
      "stamp": "Poufne"
    }
  },
  "headline": "Briefing",
  "subheadline": "Zaginięcie przewodniczki podczas wieczornego rejsu.",
  "coverTag": "Jezioro Czarne · przystań",
  "coverTitle": "Zaginięcie\nnad Jeziorem Czarnym",
  "coverText": "Łódź wróciła bez niej. Trzy osoby miały powód, by kłamać.",
  "task": {
    "title": "Twoje zadanie",
    "text": "Ustal, co stało się z Julią i kto za tym stoi.",
    "steps": [
      "Przejrzyj dokumenty i zapisuj sprzeczności.",
      "Porównaj zeznania z nagraniami i logami.",
      "Wskaż sprawcę, sposób działania i motyw."
    ]
  },
  "summary": { "title": "Karta sprawy", "text": "Jedna łódź, trzy wersje wieczoru." },
  "sideNote": "Zwracaj uwagę na godziny. Najgłośniejszy trop nie zawsze jest najważniejszy.",
  "notebookTips": [
    { "title": "Mała podpowiedź", "text": "Ułóż zdarzenia według czasu, w którym naprawdę zaszły." }
  ],
  "blocks": []
}
```

- `intro` — ekran startowy (wielki tytuł: `title` + wyróżniona czerwoną kursywą `titleEmphasis`, notatka i karta akt ze stemplem).
- `coverTitle` — tytuł na okładce briefingu; `\n` łamie linię.
- `task` — zadanie i 3 kroki.
- `sideNote` — zdanie w bocznym menu gry.
- `notebookTips` — dodatkowe podpowiedzi w notatniku (bez nowych faktów).
- `blocks` — opcjonalne dodatkowe bloki treści na ekranie briefingu (patrz rozdział 5).

### 4.5. `suspects` — podejrzani (min. 2)

```json
"suspects": [
  {
    "id": "anna",
    "name": "Anna Wróbel",
    "role": "Właścicielka przystani",
    "portraitAssetId": "portret-anna",
    "description": "Wypożyczyła łódź i jako ostatnia rozmawiała z Julią na pomoście.",
    "motive": "Julia groziła zgłoszeniem nielegalnych rejsów.",
    "statement": "O 20:10 zamknęłam biuro i pojechałam do domu.",
    "alibi": { "status": "unconfirmed", "text": "Nikt nie potwierdza jej powrotu do domu." }
  }
]
```

| Pole | Opis |
|---|---|
| `id` **(wymagane)** | krótki, unikalny identyfikator (`anna`, `piotr-nowak`); używany w rozwiązaniu |
| `name` **(wymagane)** | imię i nazwisko |
| `role` | rola w sprawie |
| `portraitAssetId` | portret z listy `assets` (opcjonalny — bez niego wyświetlą się inicjały) |
| `description` | kim jest i dlaczego jest w sprawie |
| `motive` | jawny, możliwy motyw (to, co wie śledczy — nie prawda sprawy) |
| `statement` | krótkie zeznanie (bez cudzysłowów — dodaje je serwis) |
| `alibi.status` | `confirmed` (potwierdzone), `partial` (częściowe), `unconfirmed` (niepotwierdzone), `none` (brak) |
| `alibi.text` | opis alibi |

Wszystko w `suspects` jest jawne dla gracza. Nie wpisuj tu, kto jest winny.

### 4.6. `assets` — rejestr plików

Każdy plik z folderu `assets/` wpisujesz na listę **raz** i nadajesz mu identyfikator. Potem dowody, portrety i okładka odwołują się do pliku po tym identyfikatorze (`assetId`). Jeden plik możesz użyć w wielu miejscach.

```json
"assets": [
  { "id": "cover", "type": "image", "path": "assets/images/cover.jpg", "mime": "image/jpeg",
    "alt": "Pusta łódź przy pomoście o zmierzchu", "caption": "Przystań nad Jeziorem Czarnym",
    "credit": "Zdjęcie własne", "license": "własne" },
  { "id": "portret-anna", "type": "image", "path": "assets/suspects/anna.jpg", "mime": "image/jpeg",
    "alt": "Portret fikcyjny: Anna Wróbel", "fictional": true },
  { "id": "nagranie-911", "type": "audio", "path": "assets/evidence/rozmowa-911.mp3", "mime": "audio/mpeg",
    "caption": "Zgłoszenie alarmowe, 21:47", "duration": 94 },
  { "id": "kamera-parking", "type": "video", "path": "assets/evidence/kamera-parking.mp4", "mime": "video/mp4",
    "caption": "Monitoring parkingu przystani" },
  { "id": "mapa", "type": "sketch", "path": "assets/evidence/mapa-okolicy.png", "mime": "image/png",
    "alt": "Szkic jeziora z przystanią, lasem i drogą dojazdową" },
  { "id": "raport-sekcji", "type": "document", "path": "assets/evidence/raport-sekcji.pdf", "mime": "application/pdf",
    "caption": "Raport z sekcji zwłok" }
]
```

| Pole | Opis |
|---|---|
| `id` **(wymagane)** | unikalny identyfikator pliku |
| `type` **(wymagane)** | `image` (zdjęcie), `sketch` (szkic, plan, mapa), `audio`, `video`, `document` (PDF) |
| `path` **(wymagane)** | ścieżka w paczce, zaczynająca się od `assets/`; **wielkość liter ma znaczenie** |
| `mime` | typ pliku: `image/jpeg`, `image/png`, `image/webp`, `audio/mpeg`, `audio/mp4`, `audio/ogg`, `video/mp4`, `video/webm`, `application/pdf` |
| `alt` | opis alternatywny dla osób niewidomych — **wymagany dla każdego obrazu** (inaczej ostrzeżenie) |
| `caption` | podpis wyświetlany pod materiałem |
| `credit`, `license` | autor i licencja materiału |
| `duration` | długość nagrania w sekundach |
| `transcript` | transkrypcja nagrania (tekst) — można też podać w bloku `audio`/`video` |
| `fictional` | `true` dla portretów fikcyjnych postaci — **wymagane dla portretów** |

### 4.7. `evidence` — dowody (min. 5)

```json
"evidence": [
  {
    "id": "protokol-ogledzin",
    "number": "02",
    "category": "Oględziny",
    "group": "protocol",
    "title": "Protokół oględzin pomostu",
    "summary": "Buty, telefon i ślady na deskach",
    "datetime": "2026-07-14T22:30",
    "thumbnailAssetId": "zdjecie-pomostu",
    "blocks": [
      { "type": "paragraph", "text": "**Protokół nr:** KP/0714/3\n**Godzina oględzin:** 22:30" },
      { "type": "image", "assetId": "zdjecie-pomostu", "alt": "Buty i telefon na końcu pomostu",
        "caption": "Fot. 1. Rzeczy zaginionej na końcu pomostu." },
      { "type": "paragraph", "text": "Telefon był wyciszony. Ostatnie połączenie: **20:58**." }
    ]
  }
]
```

| Pole | Opis |
|---|---|
| `id` **(wymagane)** | unikalny identyfikator dowodu; używany w werdykcie, osi czasu i rozwiązaniu |
| `title` **(wymagane)** | tytuł dokumentu |
| `number` | numer w aktach („01”, „02”…) — pokazywany na karcie i w werdykcie |
| `category` | etykieta nad tytułem (Oględziny, Zeznania, Monitoring…) |
| `group` | przypisanie do filtra z `gameplay.evidenceGroups` |
| `summary` | jedno zdanie na karcie dowodu |
| `datetime` | jawna data/godzina dokumentu (`RRRR-MM-DD` lub `RRRR-MM-DDTGG:MM`) |
| `thumbnailAssetId` | miniatura na karcie (mała wersja jest widoczna przed otwarciem dowodu, pełna dopiero po otwarciu) |
| `blocks` **(wymagane)** | treść dokumentu jako lista bloków — patrz rozdział 5 |

**Czego nie wpisywać do dowodu:** pól typu `isKey`, `importance`, `misleading`, `answer` — manifest nie może zdradzać znaczenia dowodu. Walidator zgłosi to jako błąd.

Kolejność dowodów w pliku = kolejność na liście w grze.

### 4.7a. Jak przypisać materiały do konkretnego dowodu

Materiał „należy” do dowodu wtedy, gdy blok w `blocks` tego dowodu wskazuje go przez `assetId`. Powiązanie ma zawsze trzy ogniwa:

```text
plik w paczce                  wpis w assets[]                    blok w dowodzie
assets/evidence/                { "id": "ev07-foto-a",             evidence[id="porownanie-zdjec"]
  porownanie-zdjec/               "type": "image",                   .blocks[] → { "type": "image",
    foto-a.jpg          ───►      "path": "assets/evidence/   ───►                "assetId": "ev07-foto-a" }
                                   porownanie-zdjec/foto-a.jpg" }
```

Zalecana konwencja, dzięki której od razu widać, co do czego należy:

1. **Jeden podfolder na dowód**, nazwany jak `id` dowodu: `assets/evidence/<id-dowodu>/`.
2. **Identyfikatory zasobów z numerem dowodu**: `ev07-foto-a`, `ev07-foto-b`, `ev12-nagranie`.
3. Materiały wspólne (okładka, portrety, zdjęcie użyte w kilku dowodach) trzymaj osobno: `assets/images/`, `assets/suspects/`, `assets/shared/`.

Przykład dowodu z kilkoma własnymi materiałami:

```json
"assets": [
  { "id": "ev07-foto-a", "type": "image", "path": "assets/evidence/porownanie-zdjec/foto-a.jpg", "alt": "Mapa na zdjęciu z 16:52" },
  { "id": "ev07-foto-b", "type": "image", "path": "assets/evidence/porownanie-zdjec/foto-b.jpg", "alt": "Mapa na zdjęciu z 18:14" },
  { "id": "ev07-raport", "type": "document", "path": "assets/evidence/porownanie-zdjec/raport-laboratorium.pdf" }
],
"evidence": [
  { "id": "porownanie-zdjec", "number": "07", "title": "Porównanie zdjęć inwentarzowych",
    "thumbnailAssetId": "ev07-foto-a",
    "blocks": [
      { "type": "paragraph", "text": "Dwa zdjęcia szafki M-3 z systemu inwentarzowego." },
      { "type": "gallery", "items": [
        { "assetId": "ev07-foto-a", "caption": "Fotografia A · 16:52" },
        { "assetId": "ev07-foto-b", "caption": "Fotografia B · 18:14" }
      ] },
      { "type": "document", "assetId": "ev07-raport", "title": "Raport laboratorium" }
    ] }
]
```

Co z tego wynika w grze:

- materiały dowodu są **pobierane dopiero po jego otwarciu** i przestają być dostępne po zamknięciu akt — serwis sprawdza to właśnie na podstawie bloków dowodu,
- `thumbnailAssetId` to miniatura na karcie dowodu (przed otwarciem widoczna tylko w małym rozmiarze),
- ten sam zasób możesz użyć w kilku dowodach (np. zdjęcie budynku w protokole i w raporcie) — wystarczy podać ten sam `assetId`,
- zasób wpisany do `assets[]`, ale nieużyty w żadnym bloku, zgłosi ostrzeżenie „nie jest nigdzie użyty”.

### 4.8. `timeline` — jawna oś czasu

Pokazywana graczowi w notatniku. Wpisz tylko to, co **śledczy już wiedzą z akt** — nie prawdę sprawy.

```json
"timeline": [
  { "id": "t1", "date": "2026-07-14", "time": "19:40", "label": "Wypożyczenie łodzi", "evidenceIds": ["rejestr-wypozyczen"] },
  { "id": "t2", "date": "2026-07-14", "time": "20:58", "label": "Ostatnie połączenie z telefonu Julii", "evidenceIds": ["billing"] },
  { "id": "t3", "date": "2026-07-14", "time": "21:47", "label": "Zgłoszenie na 112", "evidenceIds": ["nagranie-112-dowod"] }
]
```

- Zdarzenia w **kolejności chronologicznej** (inaczej ostrzeżenie o sprzecznych czasach).
- Każde zdarzenie wskazuje w `evidenceIds` dowody, z których gracz może je odtworzyć.
- Czas w formacie `GG:MM`.

### 4.9. `verdict` — pytania werdyktu (min. 1)

```json
"verdict": {
  "intro": "Wskaż sprawcę, sposób działania, motyw i dowody, które spinają Twoją teorię.",
  "questions": [
    { "id": "culprit", "type": "single", "prompt": "Kto odpowiada za zaginięcie Julii?", "source": "suspects", "required": true },
    { "id": "method", "type": "single", "prompt": "Co się wydarzyło?", "source": "options", "required": true,
      "options": [
        { "id": "boat", "label": "Julia została zepchnięta z łodzi podczas rejsu." },
        { "id": "pier", "label": "Do zdarzenia doszło na pomoście, a łódź puszczono z prądem." },
        { "id": "escape", "label": "Julia upozorowała zaginięcie i uciekła." }
      ] },
    { "id": "motive", "type": "single", "prompt": "Jaki był motyw?", "source": "options", "required": true,
      "options": [
        { "id": "money", "label": "Pieniądze" },
        { "id": "silence", "label": "Uciszenie świadka nielegalnych rejsów" },
        { "id": "jealousy", "label": "Zazdrość" }
      ] },
    { "id": "evidence", "type": "multiple", "prompt": "Wskaż dowody", "description": "Zaznacz do trzech dokumentów.",
      "source": "evidence", "maxSelections": 3, "required": false }
  ]
}
```

| Pole | Opis |
|---|---|
| `id` **(wymagane)** | identyfikator pytania — ten sam klucz wpiszesz w `solution.json` |
| `type` **(wymagane)** | `single` (jedna odpowiedź) lub `multiple` (kilka) |
| `prompt` **(wymagane)** | treść pytania |
| `source` | skąd biorą się odpowiedzi: `suspects` — automatycznie lista podejrzanych; `evidence` — lista dowodów (wszystkich albo tych z `options`); `options` — Twoje własne odpowiedzi |
| `options` | dla `options`: lista `{ "id", "label" }` (min. 2); dla `evidence` możesz ograniczyć wybór do części dowodów i nadać im krótkie etykiety |
| `maxSelections` | limit zaznaczeń dla `multiple` |
| `required` | czy odpowiedź jest obowiązkowa (domyślnie tak) |
| `description` | podpowiedź pod pytaniem |

**Nie zaznaczaj poprawnej odpowiedzi w manifeście** — poprawne odpowiedzi są wyłącznie w `solution.json`.

Rada: opcje błędne powinny być wiarygodne — każda powinna odpowiadać jakiemuś fałszywemu tropowi z akt.

### 4.10. `hints` — wskazówki

```json
"hints": [
  { "id": "h1", "level": 1, "text": "Nie zakładaj, że Julia zniknęła w chwili zgłoszenia." },
  { "id": "h2", "level": 2, "text": "Porównaj godzinę ostatniego połączenia z logiem łodzi." },
  { "id": "h3", "level": 3, "text": "Sprawdź, kto zamykał biuro przystani i o której wróciła łódź." }
]
```

Wskazówki są odsłaniane w kolejności z pliku. Serwer wydaje je graczowi dopiero po kliknięciu.

---

## 5. Etap 4 — bloki treści, czyli jak budować dokumenty

Każdy dowód (a także dodatkowy briefing i wyjaśnienie w rozwiązaniu) to lista **bloków**. Łączysz je w dowolnej kolejności i ilości — tak budujesz raport, protokół przesłuchania, korespondencję, kartę zdjęć, nagranie z monitoringu czy odręczny plan.

### Formatowanie tekstu

Wewnątrz tekstu możesz użyć tylko:

| Zapis | Efekt |
|---|---|
| `**tekst**` | **pogrubienie** |
| `*tekst*` | *kursywa* |
| `==tekst==` | wyróżnienie markerem |
| `\n` | nowa linia |

Znaczniki HTML (`<b>`, `<br>`, `<script>`…) są **odrzucane jako błąd**.

### Wszystkie typy bloków

**Akapit**
```json
{ "type": "paragraph", "text": "**Data:** 14 lipca 2026\n**Miejsce:** przystań" }
```

**Nagłówek** (`level` 2 = duży, 3 = mniejszy)
```json
{ "type": "heading", "text": "Przesłuchanie świadka", "level": 2 }
```

**Cytat** — zeznania, wypowiedzi (cudzysłów dodaje się sam)
```json
{ "type": "quote", "text": "Widziałem łódź około dziewiątej.", "cite": "Piotr Nowak, wędkarz" }
```

**Lista**
```json
{ "type": "list", "items": ["telefon komórkowy", "para butów", "klucz od szafki nr 4"], "ordered": false }
```

**Ramka z uwagą** (`tone`: `note`, `info`, `warning`)
```json
{ "type": "callout", "title": "Uwaga", "text": "Ślad pośredni — nie identyfikuje osoby.", "tone": "note" }
```

**Pieczątka**
```json
{ "type": "stamp", "text": "Poufne" }
```

**Tabela** — logi, rejestry, billingi
```json
{ "type": "table",
  "columns": ["Godzina", "Zdarzenie", "Kto"],
  "rows": [
    ["19:40", "Wypożyczenie łodzi nr 3", "J. Kowalska"],
    ["20:10", "Zamknięcie biura", "A. Wróbel"]
  ] }
```

**Zdjęcie**
```json
{ "type": "image", "assetId": "zdjecie-pomostu", "alt": "Buty i telefon na pomoście", "caption": "Fot. 1. Koniec pomostu." }
```

**Galeria** — kilka zdjęć obok siebie
```json
{ "type": "gallery", "items": [
  { "assetId": "foto-1", "alt": "Łódź od strony dziobu", "caption": "Fot. 2" },
  { "assetId": "foto-2", "alt": "Wiosło z rysą", "caption": "Fot. 3" }
] }
```

**Nagranie audio** — z transkrypcją (zalecana; bez niej ostrzeżenie)
```json
{ "type": "audio", "assetId": "nagranie-911", "caption": "Zgłoszenie alarmowe, 21:47",
  "transcript": [
    { "speaker": "Dyżurny", "time": "00:02", "text": "Numer alarmowy, słucham." },
    { "speaker": "Zgłaszający", "time": "00:04", "text": "Łódź wróciła pusta…" }
  ] }
```
Transkrypcja może być też zwykłym tekstem: `"transcript": "Pełna treść rozmowy…"`.

**Wideo** — z okładką i transkrypcją
```json
{ "type": "video", "assetId": "kamera-parking", "posterAssetId": "kadr-parking",
  "caption": "Kamera parkingu, 20:05–20:20", "transcript": "20:12 — na parking wjeżdża srebrny samochód." }
```

**Stenogram** — rozmowa jako tekst, bez pliku audio
```json
{ "type": "transcript", "title": "Stenogram przesłuchania",
  "lines": [
    { "speaker": "Śledczy", "text": "Gdzie była Pani o 20:30?" },
    { "speaker": "Anna Wróbel", "text": "W domu. Sama." }
  ] }
```

**Dokument PDF** — wyświetlany w bezpiecznej przeglądarce ze stronicowaniem i powiększeniem
```json
{ "type": "document", "assetId": "raport-sekcji", "title": "Raport z sekcji", "caption": "Strony 1–3" }
```

**Szkic / plan / mapa ze znacznikami**
```json
{ "type": "sketch", "assetId": "mapa", "alt": "Szkic jeziora", "caption": "Plan okolicy przystani.",
  "markers": [
    { "x": 0.18, "y": 0.62, "label": "A", "description": "Przystań" },
    { "x": 0.55, "y": 0.30, "label": "B", "description": "Miejsce znalezienia łodzi" }
  ] }
```
Współrzędne znaczników to **ułamki od 0 do 1**: `x` od lewej krawędzi obrazu, `y` od górnej. Punkt w środku obrazu to `x: 0.5, y: 0.5`. Aby znaleźć współrzędne, otwórz obraz w dowolnym programie graficznym, odczytaj piksele punktu i podziel przez szerokość/wysokość obrazu (np. 320 px z 1400 px → `0.229`).

### Przykładowe układy dokumentów

- **Raport / protokół:** `stamp` → `paragraph` (dane nagłówkowe z `**Pole:**` i `\n`) → `image` → kilka `paragraph` → `callout`.
- **Protokół przesłuchania:** `paragraph` (data) → dla każdej osoby `heading` + `quote` + `paragraph` z komentarzem.
- **Log / rejestr:** `paragraph` (zakres) → `table` → `paragraph` z interpretacją, czego log NIE pokazuje.
- **Karta zdjęć:** `paragraph` → `gallery` → `paragraph`.
- **Nagranie monitoringu:** `paragraph` (kamera, zakres godzin) → `video` z transkrypcją → `callout`.
- **Rozmowa telefoniczna:** `paragraph` (kto, kiedy) → `audio` z transkrypcją.

---

## 6. Etap 5 — przygotuj materiały (zdjęcia, audio, wideo, PDF)

### Dozwolone formaty i limity

| Typ (`type`) | Formaty | Limit na plik |
|---|---|---|
| `image`, `sketch` | JPG, PNG, WebP | 15 MB |
| `audio` | MP3, M4A, OGG | 30 MB |
| `video` | MP4, WebM | 100 MB |
| `document` | PDF | 25 MB |
| cała paczka | ZIP | 250 MB |

**Niedozwolone:** SVG, GIF, HEIC, HTML, pliki skryptów, archiwa w archiwum (ZIP w ZIP-ie), PDF-y ze skryptami, formularzami XFA lub załącznikami.

Serwis sprawdza **rzeczywistą zawartość** pliku, nie tylko rozszerzenie — zmiana nazwy `zdjecie.heic` na `zdjecie.jpg` nie zadziała. Zdjęcie trzeba naprawdę przekonwertować.

### Zalecenia praktyczne

- **Zdjęcia:** 1600–2000 px na dłuższym boku, JPG jakość ok. 80% — wystarczy do lupy w grze, a plik ma 200–500 KB.
- **Okładka:** pozioma, proporcje ok. 16:9 lub 3:2, min. 1600 px szerokości; bez tekstu i bez spoilerów (np. bez twarzy sprawcy).
- **Portrety:** pionowe, ok. 600×800 px; muszą przedstawiać **fikcyjne postacie** (np. wygenerowane lub za zgodą modela) i mieć `"fictional": true`.
- **Szkice i plany:** PNG, czytelne napisy; znaczniki A/B/C dodaj w manifeście, nie rysuj ich na obrazie, jeśli chcesz je opisać w legendzie.
- **Audio:** MP3 128 kbps mono w zupełności wystarczy; zawsze dodaj transkrypcję — część graczy nie może odsłuchać nagrania.
- **Wideo:** MP4 (H.264), 720p, krótkie fragmenty (do 1–2 min); dodaj transkrypcję lub opis tego, co widać.
- **PDF:** zapisz jako zwykły PDF („Drukuj do PDF”), bez formularzy i skryptów.
- **Metadane:** serwis usuwa z zdjęć EXIF, w tym lokalizację GPS, ale najlepiej nie wgrywać zdjęć z danymi o prawdziwych miejscach.

### Prawa do materiałów

Wgrywając sprawę potwierdzasz, że masz prawa do wszystkich materiałów. Nie używaj zdjęć prawdziwych osób bez ich zgody, prywatnych danych (prawdziwe numery telefonów, adresy, tablice rejestracyjne) ani materiałów objętych prawami autorskimi. Wpisuj autora i licencję w polach `credit` i `license`.

---

## 7. Etap 6 — wypełnij `solution.json`

```json
{
  "schemaVersion": "1.1.0",
  "caseId": "zaginiecie-nad-jeziorem",
  "answerKey": {
    "culprit": "anna",
    "method": "pier",
    "motive": "silence",
    "evidence": ["billing", "log-lodzi", "kamera-parking", "zeznanie-wedkarza"]
  },
  "scoring": {
    "maxScore": 100,
    "hintPenalty": 5,
    "solvedRequires": ["culprit", "method", "motive"],
    "questions": {
      "culprit": { "points": 55 },
      "method":  { "points": 20 },
      "motive":  { "points": 15 },
      "evidence": { "points": 10, "mode": "atLeast", "min": 2 }
    }
  },
  "reasoningChain": [
    { "claim": "Julia nie wypłynęła — łódź odpłynęła bez niej.", "evidenceIds": ["log-lodzi", "zeznanie-wedkarza"] },
    { "claim": "Anna była na przystani po 20:10, wbrew zeznaniu.", "evidenceIds": ["kamera-parking", "billing"] }
  ],
  "truthTimeline": [
    { "time": "20:40", "fact": "Anna wraca na przystań i kłóci się z Julią na pomoście." },
    { "time": "20:58", "fact": "Ostatnie połączenie Julii — do brata." }
  ],
  "explanation": [
    { "type": "paragraph", "text": "Za zaginięciem Julii stoi **Anna Wróbel**. …" },
    { "type": "callout", "title": "Najmocniejsze połączenie", "text": "Log łodzi (05) + kamera parkingu (07) + billing (09)." }
  ]
}
```

| Pole | Opis |
|---|---|
| `schemaVersion` | `"1.1.0"` |
| `caseId` | to samo co `metadata.id` w manifeście |
| `answerKey` **(wymagane)** | dla **każdego wymaganego pytania** z werdyktu: klucz = `id` pytania, wartość = `id` poprawnej opcji. Dla pytań `multiple` — lista poprawnych identyfikatorów (np. wszystkie dowody, które uznasz za trafne) |
| `scoring.maxScore` | maksymalny wynik (domyślnie 100) |
| `scoring.hintPenalty` | ile punktów odejmuje każda wskazówka (domyślnie 5) |
| `scoring.solvedRequires` | pytania, na które trzeba odpowiedzieć dobrze, by sprawa była „rozwiązana” |
| `scoring.questions` | punkty za pytania; dla `multiple` wybierz tryb `mode`: `atLeast` (pełne punkty, gdy trafione co najmniej `min` poprawnych), `exact` (tylko dokładnie ten zestaw), `proportional` (proporcjonalnie, błędne odejmują) |
| `reasoningChain` | wnioski z listą dowodów — **każdy z min. 2 dowodami** (inaczej ostrzeżenie); pokazywane graczowi po werdykcie |
| `truthTimeline` | opcjonalnie: prawdziwy przebieg zdarzeń, pokazywany po werdykcie |
| `explanation` **(wymagane)** | wyjaśnienie końcowe — bloki treści jak w dowodach; odwołuj się do numerów dowodów |

Jeśli pominiesz `scoring`, punkty rozłożą się po równo między pytania z kluczem, a kara za wskazówkę wyniesie 5 pkt.

---

## 8. Etap 7 — spakuj sprawę do ZIP

Paczka to zwykłe archiwum ZIP. Możesz spakować **zawartość folderu** (wtedy `manifest.json` leży bezpośrednio w archiwum) albo **cały folder** — oba sposoby działają.

**Windows:** zaznacz `manifest.json`, `solution.json`, `README.md` i folder `assets` → prawy przycisk → *Wyślij do* → *Folder skompresowany (zip)*. Zmień nazwę na `zaginiecie-nad-jeziorem.case.zip` (jeśli nie widzisz rozszerzeń, włącz w Eksploratorze *Widok → Rozszerzenia nazw plików*).

**macOS:** zaznacz pliki → prawy przycisk → *Kompresuj*. Zmień nazwę na `….case.zip`. Pliki `.DS_Store` i folder `__MACOSX` są automatycznie ignorowane.

**Linux / terminal:**
```bash
cd zaginiecie-nad-jeziorem
zip -r ../zaginiecie-nad-jeziorem.case.zip manifest.json solution.json README.md assets
```

Rozszerzenie `.case.zip` jest zalecane dla porządku — serwis przyjmie też zwykłe `.zip`.

---

## 9. Etap 8 — wgraj paczkę i popraw błędy

1. Zaloguj się (lub załóż konto) i wejdź w **Stwórz sprawę**.
2. Jeśli chcesz tylko sprawdzić paczkę, przeciągnij ją na pole **Tylko sprawdź paczkę** — nic nie zostanie zapisane.
3. Aby utworzyć sprawę, przeciągnij plik na pole **Wgraj nową sprawę**.
4. Po chwili zobaczysz **raport zgodności**:
   - **Błędy blokujące** (czerwone) — musisz je poprawić przed wysłaniem do moderacji.
   - **Ostrzeżenia** (żółte) — nie blokują, ale zobaczy je moderator; warto je poprawić.
5. Każda pozycja wskazuje plik i dokładne miejsce, np.
   `manifest.json › evidence[3].blocks[1].assetId: Zasób „foto-7” nie istnieje w assets[].`
   Liczenie zaczyna się od zera: `evidence[3]` to **czwarty** dowód na liście, `blocks[1]` to **drugi** blok w nim.

Poprawki możesz wprowadzać na dwa sposoby:

- popraw pliki na komputerze, spakuj ponownie i w edytorze sprawy użyj zakładki **Wgraj paczkę** (zastąpi całą wersję roboczą),
- albo popraw drobiazgi bezpośrednio w serwisie — zakładki **manifest.json** i **solution.json** to edytor tekstu; **Zapisz i waliduj** od razu odświeży raport. Pojedyncze pliki dodasz w zakładce **Materiały**.

Po zmianach w serwisie zawsze możesz pobrać aktualną paczkę przyciskiem **Eksportuj .case.zip** — to dobra kopia zapasowa.

---

## 10. Etap 9 — przetestuj sprawę w podglądzie

Przycisk **Podgląd i test** uruchamia sprawę w tym samym odtwarzaczu, z którego korzystają gracze (sesja testowa nie wpływa na oceny). Przejdź całą rozgrywkę do końca, łącznie z zamknięciem akt i werdyktem. Sprawdź:

- czy każdy dowód się otwiera i wszystkie zdjęcia, nagrania, wideo i PDF-y działają,
- czy godziny zgadzają się między briefingiem, dokumentami, osią czasu i rozwiązaniem,
- czy da się dojść do każdej poprawnej odpowiedzi **wyłącznie z akt**,
- czy fałszywe tropy są wyjaśnione w rozwiązaniu,
- czy wskazówki pomagają, ale nie dodają nowych faktów,
- czy punktacja daje sensowny wynik przy poprawnej i błędnej odpowiedzi,
- czy tytuł, opis i okładka nie zdradzają rozwiązania.

Najważniejszy test: **daj sprawę do rozwiązania osobie, która jej nie zna**, i nie podpowiadaj. Zapisz, gdzie utknęła i czy rozwiązanie uznała za uczciwe. Kolejną sesję testową rozpoczniesz, klikając ponownie **Podgląd i test**.

---

## 11. Etap 10 — wyślij do moderacji i publikuj kolejne wersje

1. Gdy raport nie ma błędów blokujących, kliknij **Wyślij do moderacji**. Wersja zostaje zablokowana do edycji (możesz ją **wycofać**, jeśli chcesz coś jeszcze zmienić).
2. Moderator rozgrywa sprawę, sprawdza ostrzeżenia, zasady jakości i prawa do materiałów.
3. Po akceptacji sprawa pojawia się w katalogu pod adresem `blackfile.pl/sprawy/<slug>`.
4. Jeśli moderator odrzuci wersję, zobaczysz jego uwagi w edytorze sprawy. Popraw i wyślij ponownie.

**Wersje:** opublikowana wersja jest niezmienna. Każda zmiana po publikacji (nowa paczka lub zapis w edytorze) tworzy kolejną wersję roboczą, która znów przechodzi walidację i moderację. Gracze w trakcie rozgrywki kończą ją na wersji, od której zaczęli. Aby zachować postęp i odwołania, **nie zmieniaj identyfikatorów** (`id`) istniejących dowodów, podejrzanych i pytań między wersjami.

---

## 12. Lista kontrolna przed wysłaniem

**Struktura**
- [ ] `schemaVersion` to `"1.1.0"` w obu plikach
- [ ] `slug` zawiera tylko małe litery, cyfry i myślniki
- [ ] min. 2 podejrzanych, 5 dowodów, 1 pytanie werdyktu
- [ ] każdy plik z `assets/` jest wpisany do `assets[]`, a każdy wpis ma plik
- [ ] `solution.json` ma odpowiedź dla każdego wymaganego pytania i wyjaśnienie

**Uczciwość zagadki**
- [ ] każdy kluczowy wniosek wynika z min. 2 niezależnych dowodów
- [ ] godziny są spójne we wszystkich dokumentach
- [ ] fałszywe tropy są wyjaśnione w rozwiązaniu
- [ ] rozwiązanie nie zależy od wyglądu, płci, wieku ani pochodzenia
- [ ] wskazówki nie wprowadzają nowych faktów

**Spoilery**
- [ ] tytuł, opis, tagi i okładka nie zdradzają sprawcy
- [ ] nazwy plików i identyfikatory nie zdradzają rozwiązania (`morderca.jpg`, `klucz-dowod`)
- [ ] w manifeście nie ma oznaczeń ważności dowodów ani poprawnych odpowiedzi

**Dostępność i prawa**
- [ ] każde zdjęcie i szkic ma `alt`
- [ ] każde nagranie ma transkrypcję
- [ ] portrety mają `"fictional": true`
- [ ] masz prawa do wszystkich materiałów; brak prawdziwych danych osobowych

**Test**
- [ ] sprawa przeszła pełny podgląd do werdyktu
- [ ] ktoś, kto nie zna rozwiązania, rozwiązał ją samodzielnie (lub wiesz, dlaczego nie)

---

## 13. Najczęstsze błędy i jak je naprawić

| Komunikat | Przyczyna | Rozwiązanie |
|---|---|---|
| `Niepoprawny JSON: Expected ',' or '}'…` | brak przecinka, nadmiarowy przecinek po ostatnim elemencie, niezamknięty nawias | otwórz plik w VS Code — podkreśli miejsce błędu; sprawdź przecinki między elementami, ale nie po ostatnim |
| `Brak pliku „assets/…” w paczce` | literówka w `path`, inna wielkość liter, plik poza paczką | porównaj nazwę znak po znaku (`Foto.JPG` ≠ `foto.jpg`) |
| `Zasób „x” nie istnieje w assets[]` | blok odwołuje się do `assetId`, którego nie ma na liście | dodaj wpis do `assets` lub popraw identyfikator |
| `Zawartość pliku nie odpowiada rozszerzeniu` | np. HEIC lub PNG z nazwą `.jpg` | przekonwertuj plik w programie graficznym i zapisz ponownie |
| `Niedozwolone znaczniki HTML` | w tekście jest `<b>`, `<br>`, `<i>` | zamień na `**…**`, `\n`, `*…*` |
| `Nieznany typ bloku „…”` | literówka w `type` | dozwolone: paragraph, heading, quote, list, callout, stamp, table, image, gallery, audio, video, transcript, document, sketch |
| `Powtórzony identyfikator…` | dwa elementy z tym samym `id` | nadaj unikalne identyfikatory |
| `Poprawna opcja „x” nie występuje w pytaniu` | w `answerKey` wpisano `id`, którego nie ma w opcjach | popraw `answerKey` lub opcje pytania |
| `Slug „…” jest już zajęty` | inna sprawa ma ten adres | zmień `metadata.slug` |
| `Ścieżka wychodzi poza katalog paczki` | plik z `../` w ścieżce | spakuj ponownie z folderu sprawy |
| `Niedozwolony typ pliku (.svg)` | SVG, HTML, skrypt w paczce | usuń plik lub przekonwertuj SVG do PNG |
| `PDF zawiera aktywną zawartość` | PDF z formularzem lub skryptem | wydrukuj dokument do nowego PDF („Drukuj → Zapisz jako PDF”) |
| ostrzeżenie `Kluczowy wniosek opiera się na mniej niż dwóch dowodach` | w `reasoningChain` jest tylko jeden dowód | dodaj drugi, niezależny dowód do akt i wniosku |
| ostrzeżenie `Tytuł, opis lub tagi mogą ujawniać rozwiązanie` | nazwisko sprawcy w opisie | przeredaguj opis |

---

## 14. Dodatek: podstawy JSON w 5 minut

JSON to prosty zapis danych tekstem. Wystarczy znać cztery zasady:

1. **Obiekt** to nawiasy klamrowe z parami `"klucz": wartość`, oddzielonymi przecinkami:
   ```json
   { "name": "Anna Wróbel", "role": "Właścicielka przystani" }
   ```
2. **Lista** to nawiasy kwadratowe z elementami oddzielonymi przecinkami:
   ```json
   ["zaginięcie", "jezioro", "współczesna"]
   ```
3. **Tekst** zawsze w prostych cudzysłowach `"…"` (nie `„…”` ani `'…'`). Cudzysłów wewnątrz tekstu poprzedź ukośnikiem: `"Powiedział: \"nie wiem\""` — albo użyj polskich cudzysłowów „…”, które nie wymagają ukośnika. Nową linię zapisuj jako `\n`.
4. **Liczby i wartości logiczne** bez cudzysłowów: `3`, `0.25`, `true`, `false`.

Najczęstsze pułapki:

- **przecinek po ostatnim elemencie** listy lub obiektu — niedozwolony: `["a", "b",]` ✗
- **brak przecinka** między elementami ✗
- **komentarze** (`// …`) są w JSON niedozwolone ✗

Wskazówka: jeśli przygotowujesz sprawę z pomocą asystenta AI, przekaż mu tę instrukcję i szablony — poproś o wypełnienie `manifest.json` i `solution.json` zgodnie z formatem, a następnie zawsze sprawdź wynik w polu **Tylko sprawdź paczkę** i samodzielnie przetestuj logikę sprawy.

---

*Blackfile · instrukcja dla twórców · format 1.1.0 · blackfile.pl/format*
