# Dokumentasi Aplikasi -- Simulasi Karier: Ilusi vs Realita

> **Catatan penting:** Dokumen ini adalah catatan hidup (living document). Setiap kali ada
> perubahan fitur, modul, alur, atau pilihan desain di aplikasi, dokumen ini **wajib ikut
> diperbarui** pada bagian yang relevan, dan setiap perubahan dicatat singkat di
> [Log Pembaruan](#log-pembaruan) di bagian paling bawah. Tujuannya supaya siapa pun (atau
> sesi kerja mana pun) yang membuka dokumen ini bisa langsung paham kondisi aplikasi
> **saat ini**, bukan kondisi saat aplikasi pertama dibangun.

## Daftar Isi

1. [Ringkasan](#ringkasan)
2. [Latar Belakang & Masalah yang Diselesaikan](#latar-belakang--masalah-yang-diselesaikan)
3. [Tujuan & Kebermanfaatan Aplikasi](#tujuan--kebermanfaatan-aplikasi)
4. [Target Pengguna](#target-pengguna)
5. [Arsitektur & Tech Stack](#arsitektur--tech-stack)
6. [Pilihan Desain / Template Visual](#pilihan-desain--template-visual)
7. [Alur Aplikasi End-to-End](#alur-aplikasi-end-to-end)
8. [Modul & Fitur](#modul--fitur)
9. [Keamanan Aplikasi](#keamanan-aplikasi)
10. [Model Data Inti (Ringkas)](#model-data-inti-ringkas)
11. [Log Pembaruan](#log-pembaruan)

---

## Ringkasan

**Simulasi Karier: Ilusi vs Realita** adalah aplikasi web edukasi berbasis simulasi
keputusan (choice-based simulation) untuk siswa SMA, dibangun dengan PHP native + MySQL
tanpa framework maupun Node.js (target deploy: shared hosting cPanel). Siswa "memainkan"
sebuah jalur karier lewat serangkaian babak cerita bercabang: di setiap babak mereka
dihadapkan pada **ilusi** (gambaran yang menggoda tentang karier tsb) dan **realita**
(apa yang sebenarnya terjadi), lalu mengambil keputusan yang berefek nyata ke empat
sumber daya diri (ekonomi, skill, mental, waktu). Di akhir, siswa mendapat laporan
reflektif; guru BK/admin mendapat dashboard data agregat lintas seluruh siswa.

## Latar Belakang & Masalah yang Diselesaikan

Siswa SMA umumnya memilih jurusan/karier berdasarkan **informasi satu arah** --
brosur, video, cerita orang lain -- yang cenderung menonjolkan sisi menariknya saja
("ilusi") dan menyembunyikan konsekuensi nyatanya ("realita": beban finansial, tuntutan
skill, tekanan mental, kompetisi waktu). Akibatnya keputusan karier sering diambil
berdasarkan bayangan yang tidak lengkap, dan konsekuensinya baru terasa setelah siswa
benar-benar menjalani jalur itu -- saat sudah lebih sulit untuk berbalik arah.

Aplikasi ini menjembatani gap tersebut dengan memberi siswa ruang **aman untuk salah**:
mereka bisa "mencoba" konsekuensi dari sebuah pilihan karier di dalam simulasi,
membandingkan langsung ilusi vs realita-nya, dan merasakan efeknya pada empat resource
inti -- tanpa risiko yang sesungguhnya. Ini bukan tes minat/bakat statis (isi kuesioner,
dapat rekomendasi), tapi simulasi *dinamis* di mana keputusan demi keputusan membentuk
lintasan cerita dan hasil akhir yang berbeda-beda per siswa.

## Tujuan & Kebermanfaatan Aplikasi

- **Bagi siswa:** membangun kesadaran (awareness) bahwa setiap pilihan karier punya
  trade-off nyata, membantu mengenali pola pengambilan keputusan diri sendiri (lebih
  condong ke ilusi/gambaran menarik, atau ke realita/pertimbangan matang), dan
  memberi bahan refleksi konkret (bukan sekadar label "cocok jadi X") untuk didiskusikan
  bersama guru BK atau orang tua.
- **Bagi guru BK/sekolah:** menyediakan alat bimbingan konseling yang interaktif
  (bukan cuma tes/kuesioner satu arah), plus **data agregat** lintas siswa -- karier apa
  yang paling diminati, seberapa besar kecenderungan siswa memilih jalur ilusi vs
  realita, seberapa banyak yang mengalami "burnout" simulasi -- yang bisa jadi bahan
  evaluasi program bimbingan karier di sekolah.
- **Bagi pengelola/pemilik aplikasi:** panel admin memungkinkan seluruh konten
  simulasi (karier, cerita/skema, pilihan, percabangan) diracik dan diubah tanpa
  menyentuh kode, sehingga konten bisa terus berkembang (career path baru, skenario
  baru) tanpa deploy ulang aplikasi.

## Target Pengguna

| Peran | Akses | Kebutuhan utama |
|---|---|---|
| **Siswa** | `public_html/home/` | Mendaftar, menjalani simulasi karier, melihat hasil/refleksi diri |
| **Guru BK** (role `guru`) | `public_html/admin/` | Melihat Data Pengguna & Laporan (read-only, lintas semua siswa) |
| **Admin/pengelola konten** (role `admin`) | `public_html/admin/` | Semua akses guru + CRUD penuh atas Karier/Skema/Tujuan/Misi, dan Simulator uji coba |

## Arsitektur & Tech Stack

- **Backend:** PHP native (tanpa framework), gaya prosedural + kelas statis kecil
  (`Auth`, `Csrf`, `RateLimiter`, `Database`, `GameEngine`) di `app/core/`.
- **Database:** MySQL/MariaDB (InnoDB, utf8mb4) via PDO dengan prepared statement di
  semua query -- lihat `database/schema.sql`.
- **Frontend:** Server-rendered PHP murni (tiap halaman = 1 request penuh, tanpa
  SPA/React/Vue), progressive enhancement lewat sedikit vanilla JS (tidak ada halaman
  yang *butuh* JS untuk berfungsi).
- **Struktur folder:**
  ```
  app/            kode inti bersama (config, koneksi DB, Auth, GameEngine, helper)
  public_html/    document root
    home/         frontend siswa
    admin/        panel admin & guru BK
  database/       schema.sql, seed.sql, script pembuatan akun admin CLI
  ```
- **Alasan tanpa framework/Node.js:** target deploy adalah shared hosting umum
  (cPanel) yang biasanya hanya menyediakan PHP + MySQL tanpa akses `composer`/`npm`
  build step atau proses Node persisten -- lihat `README.md`/`DEPLOY.md` untuk detail
  instalasi & deploy.
- **Mesin simulasi tunggal, dua pemakai:** `app/core/GameEngine.php` dipakai
  **bersama** oleh permainan siswa yang sungguhan tersimpan di database
  (`public_html/home/main.php`) maupun simulator uji-coba admin yang sepenuhnya
  ephemeral di session, tanpa menulis ke database (`public_html/admin/skema/simulator.php`)
  -- supaya aturan permainannya konsisten di satu tempat, tidak diimplementasikan dua kali.

## Pilihan Desain / Template Visual

Aplikasi ini **tidak** memakai template/theme pihak ketiga -- seluruh desain (CSS,
struktur komponen, animasi) ditulis khusus untuk proyek ini, dipecah jadi dua sistem
desain terpisah karena audiens dan tujuannya berbeda:

### Sisi siswa (`public_html/home/assets/`)
- **Gaya:** "Vibrant Gradient" -- tema terang, energik, gradasi indigo-violet-pink
  (`#6C5CE7` -> `#A855F7` -> `#FF5CA8`), kartu putih mengambang dengan bayangan
  berwarna, sudut membulat besar (`--radius: 20px`). Dipilih lewat opsi eksplisit dari
  pemilik aplikasi (dibanding alternatif "Clean Pastel" atau "Bold Editorial") supaya
  terasa hidup dan cocok untuk audiens anak muda/SMA, bukan kaku seperti aplikasi
  formal.
- **Font (self-hosted, di `home/assets/fonts/`, tanpa CDN eksternal):**
  - **Bricolage Grotesque** (variable, 400-800) -- judul/display, tegas dan modern.
  - **Plus Jakarta Sans** (400-700) -- body text, mudah dibaca.
  - **DM Mono** (400/500) -- label kecil/eyebrow/angka (kesan "data", teknikal).
- **Animasi & interaksi** (`home/assets/js/game.js` + CSS): scroll-reveal
  (`IntersectionObserver`, elemen fade+slide masuk saat di-scroll), meter resource
  yang "mengisi" saat halaman dimuat, kartu pilihan yang muncul bertahap
  (staggered), efek sheen berjalan di kartu "ilusi", cross-fade otomatis antar
  halaman lewat CSS `@view-transition` (browser modern), dan latar belakang gradasi
  radial yang diam relatif ke viewport (`background-attachment:fixed`) supaya tidak
  ada "sambungan" warna yang terlihat saat halaman panjang di-scroll.
- **Landing page tamu** (`home/index.php`, saat belum login): dibuat selebar halaman
  penuh (bukan kolom sempit 600px seperti halaman aplikasi lain) dengan hero,
  slider tagline, deretan angka statistik, kartu "kenapa simulasi ini",
  timeline "cara kerja" bergaya perjalanan (journey), dan CTA banner penutup --
  fungsinya murni promosi/hook sebelum orang mendaftar, jadi sengaja dibuat lebih
  "landing page" dibanding halaman aplikasi lain yang bergaya app mobile.

### Sisi admin/guru (`public_html/admin/assets/`)
- **Gaya:** "Dashboard bersih & profesional" -- terang, minim animasi berat, palet
  netral (abu/putih) dengan aksen indigo (`#4F46E5`), tabel rapi, sidebar navigasi
  tetap. Dipilih secara eksplisit **berbeda** dari tema siswa (bukan tema game yang
  sama) karena penggunanya (admin/guru) butuh efisiensi kerja/scan data, bukan
  pengalaman yang playful.
- **Font:** font sistem (`Segoe UI`/`system-ui` dst.) -- tanpa file tambahan,
  supaya panel admin tetap ringan dan cepat tanpa mengorbankan keterbacaan.
- **Grafik/chart:** **inline SVG murni**, dibangkitkan di sisi server oleh PHP
  (`app/includes/functions.php`), **bukan** library JS pihak ketiga -- karena
  Content-Security-Policy aplikasi ini sengaja ketat (`script-src 'self'`, tanpa
  CDN eksternal). Palet warna kategorikal untuk grafik divalidasi CVD-safe
  (ramah buta warna) memakai skill/tooling `dataviz`.

## Alur Aplikasi End-to-End

### Alur Siswa

```
Tamu buka /home/  ->  Daftar (register.php) / Masuk (login.php)
        |
        v
Beranda (index.php) --"Mulai simulasi baru"--> mulai.php (buat/lanjutkan game_session)
        |
        v
Onboarding (wajib berurutan, tujuan_onboarding_berikutnya() yang menentukan):
  1) minat.php   -- pilih Minat & Bakat (minimal 1 masing-masing)
  2) tujuan.php  -- pilih tepat 2 Tujuan Karier
  3) karier.php  -- pilih 1 dari 4 opsi karier yang muncul (2 "sesuai profil"
                    berdasar skor minat/bakat, 2 "ilusi populer" acak)
        |
        v
main.php -- babak demi babak: tampilkan skema (narasi + pilihan) -> siswa pilih
            -> GameEngine menerapkan efek resource -> tentukan babak berikutnya
            (lihat detail mekanik di modul "Mesin Simulasi")
        |
        v
Berakhir karena BURNOUT (2 resource nol bersamaan) atau SELESAI (syarat
minimal skema utama + tujuan/misi terpenuhi)
        |
        v
hasil.php -- laporan reflektif: tier hasil, lintasan resource, rasio ilusi vs
             realita, insight otomatis, status tujuan/misi tercapai
```

### Alur Admin / Guru

```
/admin/login.php (akun dibuat manual lewat CLI database/create-admin.php,
                   TIDAK ada form daftar sendiri di panel admin)
        |
        v
Dashboard (index.php) -- ringkasan angka + jalan pintas ke tiap modul
        |
        +-- [khusus role admin] Konten: Karier, Skema (+builder pilihan/trigger,
        |   Preview alur, Simulator uji coba), Tujuan Karier, Misi
        |
        +-- [admin & guru] Data: Data Pengguna (daftar semua siswa terdaftar),
            Laporan (dashboard analitik + laporan per-siswa + detail per-sesi)
```

## Modul & Fitur

Untuk tiap modul: **apa itu** -> **kebermanfaatannya**.

### 1. Beranda (`home/index.php`)
Satu file, dua tampilan tergantung status login:
- **Tamu (belum login):** landing page promosi berisi hook ("Ilusi vs
  Realita"), penjelasan kenapa simulasi ini berguna, alur cara kerja bergaya
  timeline, dan ajakan mendaftar. **Manfaat:** titik masuk pertama calon
  pengguna -- meyakinkan siswa (atau guru yang memperkenalkan aplikasi ke
  siswanya) bahwa ini bukan kuesioner biasa, sebelum mereka berkomitmen
  mendaftar.
- **Siswa yang sudah login:** riwayat simulasi ditampilkan sebagai **"save
  slot"** (`.save-slot`) -- kartu horizontal per playthrough dengan pita
  status (selesai/burnout), tier hasilnya, dan satu kartu "+ Sesi Baru" putus-
  putus di ujung -- dibanding tabel log transaksi biasa. **Manfaat:** terasa
  seperti memilih slot simpanan di sebuah game dengan banyak playthrough,
  mengundang siswa untuk mencoba jalur/pilihan berbeda, bukan cuma melihat
  riwayat datar.

### 2. Registrasi & Login Siswa (`home/register.php`, `login.php`, `logout.php`)
Pendaftaran akun siswa publik (nama, email, usia opsional, password) dan login
mandiri. **Manfaat:** setiap siswa punya progres & riwayat simulasi sendiri yang
tersimpan, bisa dilanjutkan kapan saja, dan hasilnya bisa ditinjau ulang guru BK
di kemudian hari.

### 3. Login Admin/Guru (`admin/login.php`, `logout.php`)
Login untuk akun `admin_users` (role `admin` atau `guru`). **Tidak ada** form
daftar sendiri di sini -- akun pertama dibuat manual lewat CLI
(`php database/create-admin.php`) oleh pengelola sistem, supaya tidak sembarang
orang bisa mendaftar jadi admin lewat browser. **Manfaat:** memisahkan akses
pengelolaan konten & data dari akses siswa secara tegas.

### 4. Profil Diri -- Minat & Bakat (`home/minat.php`)
Langkah 1 dari 2 onboarding: siswa memilih minimal 1 minat dan 1 bakat dari
daftar yang dikelola admin, disajikan sebagai **grid kartu yang di-tap**
(`.pick-grid`/`.pick-card`, bukan checkbox pill kecil) -- kartu menyala
gradient begitu dipilih (CSS `:has(:checked)`, tanpa JS wajib). **Manfaat:**
jadi bahan penyaringan opsi karier yang relevan di langkah "Pemilihan Karier"
(skor kecocokan), sekaligus data untuk guru BK memahami profil siswa; gaya
kartu-tap dipilih supaya terasa seperti membentuk karakter, bukan mengisi
formulir screening.

### 5. Profil Diri -- Tujuan Karier (`home/tujuan.php`)
Langkah 2 dari 2 onboarding: siswa memilih **1 atau 2** tujuan karier lewat
grid kartu besar (`.pick-card-wide`), dengan **counter visual** "X/2 dipilih"
yang berubah warna begitu minimal 1 terpilih (checkbox lain otomatis
dinonaktifkan sementara begitu 2 tercapai, murni umpan balik visual -- validasi
1-2 sesungguhnya tetap di server), dan begitu tepat 2 kartu terpilih muncul
**kalimat preview** yang menggabungkan nama kedua tujuan itu (mis. "Kamu
sedang menuju sosok yang mengejar A sekaligus B."). Syarat tiap tujuan
ditampilkan sebagai **narasi tingkat kebutuhan** ("...menjaga kondisi
finansial yang sangat kuat", bukan "Ekonomi >= 80") lewat
`narasi_syarat_tujuan()` -- angka ambang pastinya sengaja tidak ditampilkan ke
siswa (`format_syarat_tujuan()` yang menampilkan angka pasti tetap dipakai di
laporan admin/guru). **Manfaat:** mengubah "keberhasilan" simulasi dari
sekadar bertahan/tidak-burnout menjadi terukur -- siswa diajak menentukan dulu
apa yang penting buatnya berdasarkan makna, bukan mengoptimalkan angka, baru
dinilai apakah tercapai (dipakai mesin simulasi untuk memutuskan kapan alur
boleh berakhir, lihat modul Mesin Simulasi).

### 6. Pemilihan Karier (`home/karier.php`)
Menyajikan 4 opsi karier per sesi ala **pemilihan class RPG**: 2 opsi "sesuai
profil" (skor dari kecocokan minat/bakat siswa dengan syarat karier tsb) dan 2
opsi "ilusi populer" (dipilih acak dari karier berkategori `ilusi_populer`, mis.
karier yang kelihatannya glamor tapi realitanya berat), masing-masing sebagai
kartu radio (`.class-card`) dengan **ikon dekoratif** (bukan label
"berisiko"/"stabil" -- sengaja dihilangkan supaya tidak membocorkan
kategorisasi ke siswa) dan indikator "tingkat tantangan" (1-3 ikon, dihitung
dari jumlah skema krisis yang terhubung ke karier itu lewat `skema_karier`) --
pilih kartu dulu, baru konfirmasi lewat satu tombol "Mulai dengan Karier Ini"
di bawah (validasi "harus pilih satu" murni native HTML `required` pada radio,
tidak bergantung JS). **Manfaat:** sejak awal siswa dihadapkan pada campuran
pilihan yang "masuk akal" dan yang "menggoda" -- persis dinamika dunia nyata
saat memilih jurusan/karier -- dibungkus dengan rasa memilih "kelas karakter"
murni dari nama & pitch-nya, bukan label yang menuntun jawaban.

### 7. Mesin Simulasi -- Inti Permainan (`home/main.php` + `app/core/GameEngine.php`)
Ini modul paling sentral. Setiap sesi permainan dimulai dengan 4 resource:
Ekonomi 62, Skill 28, Mental 76, Waktu 80 (skala 0-100). Siswa berjalan dari satu
**skema** (babak cerita) ke skema berikutnya:

- **Tipe skema:** `utama` (babak inti alur karier), `ilusi` (gambaran yang
  menggoda), `realita` (apa yang sebenarnya terjadi), `turunan` (cabang lanjutan),
  `krisis` (kondisi darurat, dipicu otomatis saat satu resource menyentuh 0).
  Ilusi dan Realita sengaja jadi **node/skema terpisah** (bukan dua kolom teks
  di satu skema) supaya masing-masing bisa punya pilihan & percabangannya sendiri.
- **Efek keputusan:** tiap pilihan mengubah keempat resource (-100 s.d. 100,
  di-*clamp* 0-100), bisa juga memberi **modifier sementara** (pengali efek
  positif, berlaku sejumlah babak utama) dan **flag** (penanda perilaku, mis.
  `motif_ilusif`) yang memengaruhi pilihan skema berikutnya atau insight di
  laporan akhir.
- **Percabangan (`trigger_skema_selanjutnya`):** tiap pilihan bisa menunjuk
  beberapa skema kandidat berikutnya, berurut prioritas -- mesin memilih
  kandidat **pertama** yang syarat kemunculannya terpenuhi (ambang resource
  min/max, flag wajib, skema prasyarat, probabilitas acak). Ini yang membuat
  alur bisa personal per siswa (siswa dengan flag tertentu bisa diarahkan ke
  jalan berbeda dari siswa lain).
- **Krisis otomatis:** begitu satu resource menyentuh 0, mesin mencari skema
  krisis terkait (spesifik ke karier yang dimainkan, atau generik) dan
  menyisipkannya sebelum melanjutkan alur normal.
- **Alur skema utama berulang sampai syarat terpenuhi:** kalau di suatu titik
  tidak ada kandidat skema berikutnya yang lolos syarat (jalan buntu), mesin
  **tidak langsung mengakhiri permainan**. Alur baru boleh dianggap selesai kalau
  jumlah skema utama yang sudah dilalui mencapai minimal **11** DAN kedua tujuan
  karier yang dipilih siswa (plus misi tuntutan karier terkait) sudah terpenuhi
  oleh kondisi resource saat itu. Selama syarat itu belum terpenuhi, mesin
  memunculkan **skema utama baru** (diutamakan yang terkait karier yang sedang
  dimainkan, lalu fallback ke skema utama generik) supaya permainan terus
  berjalan. **Manfaat:** ini memastikan hasil akhir benar-benar mencerminkan
  apakah siswa "berhasil" menurut tujuannya sendiri, bukan berhenti prematur
  cuma karena kehabisan konten cerita yang sudah ditulis admin.
- **Burnout = game over:** begitu **dua** resource menyentuh 0 secara
  bersamaan, permainan berhenti sebagai "burnout" -- terlepas dari berapa
  banyak skema utama yang sudah dilalui. **Manfaat pedagogis:** mensimulasikan
  bahwa memaksakan diri di dunia nyata (mis. terus mengejar uang sambil
  mengabaikan mental & waktu) punya batas yang bisa runtuh sekaligus, bukan
  cuma pelan-pelan menurun.

**Presentasi babak (supaya terasa seperti menjalani hidup karakter, bukan
angket) -- murni tampilan, tidak mengubah data/mekanik di atas:**
- **Peta perjalanan:** rail titik babak 1..11 (`render_journey_rail()` di
  `functions.php`) di atas tiap babak, menandai posisi sekarang & yang sudah
  dilalui, murni dari `utama_count` yang sudah ada.
- **Kalender hidup:** label flavor ("Kelas 10 SMA", "Kelas 11 SMA", ...,
  "Menyempurnakan Arah") dari `label_waktu_fiksi()`, dipetakan dari
  `utama_count` -- memberi rasa waktu berjalan tanpa menyimpan tanggal apa pun.
- **Kartu aksi polos, tanpa petunjuk angka/arah dampak:** pilihan sengaja
  ditampilkan sebagai teks murni tanpa indikator naik/turun resource apa pun
  (sempat dicoba pakai ikon arah dampak halus, tapi dihapus lagi -- lihat
  Log Pembaruan -- karena membuat siswa memilih dengan mengoptimalkan poin,
  bukan berdasarkan pertimbangan/karakter dirinya sendiri).
- **Konsekuensi otomatis & naratif, bukan angka:** begitu memilih, banner
  konsekuensi TIDAK menampilkan delta angka resource sama sekali -- hanya
  narasi (dari pool `skema_pilihan_narasi` kalau admin sudah menulisnya, atau
  fallback generik `narasi_efek_generik()` berdasarkan tanda efek bersih).
  Narasinya diketik otomatis huruf-demi-huruf (`game.js`), lalu **otomatis
  tertutup dan berpindah ke babak berikutnya** setelah jeda baca -- pemain
  boleh tap banner untuk mempercepat, tapi tidak wajib klik apa pun. Warna
  aksen kiri banner (hijau/merah tipis) mengikuti sentimen bersih efeknya
  (`data-sentimen`) tanpa pernah menyebut angka. `#next-scene` tidak pernah
  dirender `hidden` dari server, jadi tanpa JS semuanya tetap langsung
  terlihat & bisa dimainkan (cuma tanpa animasi otomatisnya).
- **Mood ambient:** `mood_permainan()` menghitung status babak saat ini
  (`calm`/`low`/`high`/`tense`/`crisis` dari level mental & resource kritis/
  skema krisis aktif) dan mewarnai latar (`.bg-blob`) & HUD lewat class
  `mood-*` di `<body>` -- dunia terasa merespons kondisi pemain, termasuk
  tampilan lebih tegang otomatis saat resource kritis atau krisis aktif.
- **Feedback suara singkat (opsional, bisa dimatikan):** nada pendek
  disintesis di browser lewat Web Audio API (`game.js`, tanpa file audio
  sama sekali) -- tap halus saat memilih aksi, nada naik/turun mengikuti
  konsekuensi bersih (positif/negatif) saat menekan "Lanjutkan", dan nada
  peringatan kalau ada resource yang kritis. Toggle mute di HUD (ikon
  speaker), tersimpan di `localStorage` per-viewer.

### 8. Hasil & Refleksi Siswa (`home/hasil.php`)
Ditampilkan setelah sesi berakhir (selesai/burnout), dibingkai sebagai "report
card" yang di-reveal bertahap (verdict muncul dulu dengan animasi scale+fade,
baru breakdown resource menyusul lewat scroll-reveal). Berisi: tier hasil (mis.
"Berhasil dengan Baik", "Bertahan, tapi Tujuan Tidak Sepenuhnya Tercapai",
"Burnout"), resource awal->akhir sebagai **bar ganda** (bar redup untuk posisi
awal bertumpuk dengan bar vivid untuk posisi akhir, bukan cuma teks "62 -> 55"),
rasio keputusan ilusi vs realita, insight otomatis (kalimat reflektif berbasis
pola keputusan & resource akhir), status tiap tujuan/misi (tercapai/tidak),
**timeline mini** titik keputusan paling berpengaruh (ikon naik/turun per
keputusan, reuse komponen `.timeline` dari landing page), dan tabel lintasan
resource per keputusan. **Manfaat:** mengubah pengalaman bermain jadi bahan
refleksi konkret yang bisa dibawa siswa ke sesi konseling dengan guru BK, dan
terasa seperti "kartu hasil" yang layak dilihat ulang -- bukan cuma tabel
angka datar.

**Pencapaian & bagikan hasil (Fase 2 dari brief game-feel) -- murni tampilan,
dihitung ulang tiap kali laporan dibuka, tidak disimpan di mana pun:**
- **Lencana:** `hitung_lencana()` (`functions.php`) menandai pola menonjol dari
  sesi ini (mis. "Tanpa Krisis", "Realis Sejati", "Berhasil Penuh", "Jalan
  Panjang") berdasarkan `krisis_terpicu`, rasio ilusi/realita, dan `tier` yang
  sudah dihitung `hitung_evaluasi_sesi()` -- selalu tampil sebagai chip biasa
  di halaman (tidak butuh JS), lalu **muncul ulang sebagai toast** melayang
  kanan-atas lewat `game.js` sebagai hiasan tambahan.
- **Bagikan Hasil:** tombol yang menggambar kartu ringkasan (tier, karier,
  bar 4 resource, rasio ilusi/realita) ke `<canvas>` di sisi browser lalu
  mengunduhnya sebagai PNG -- murni client-side, tidak ada data yang
  dikirim ke server mana pun. **Manfaat:** memberi alasan konkret untuk
  main ulang dengan pilihan berbeda (mengejar lencana lain) dan menyebarkan
  hasilnya ke teman/keluarga sebagai bahan obrolan.

### 9. Admin -- Karier (`admin/karier/`)
CRUD data karier: nama, kategori (`real`/`ilusi_populer`), pitch (teaser),
deskripsi, status (`draft`/`published`, hanya yang published yang muncul ke
siswa), skema awal (titik masuk alur), serta minat/bakat yang relevan.
Termasuk **modal "+ Baru"** di form Karier untuk menambah minat/bakat baru
langsung dari situ (AJAX ke `karier/opsi_tambah.php`) **tanpa** mereset isian
form karier yang sedang diisi. **Manfaat:** admin bisa meracik jalur karier baru
dan melengkapi master data minat/bakat dalam satu alur kerja, tanpa harus
bolak-balik halaman atau menunggu fitur CRUD minat/bakat terpisah dibuat.

### 10. Admin -- Skema (`admin/skema/`)
Modul paling kompleks di panel admin, terdiri dari beberapa sub-fitur:
- **Daftar & form skema** (`list.php`, `form.php`): kelola tipe, narasi, syarat
  kemunculan (ambang resource, flag, skema prasyarat, probabilitas), pengaturan
  transisi tampilan.
- **Pilihan & trigger** (`pilihan.php`, `pilihan_form.php`): kelola pilihan per
  skema -- efek resource, sifat (ilusi/realistis), modifier, flag yang
  ditambahkan, pool variasi narasi efek, dan hingga 3 kandidat skema berikutnya
  berurut prioritas.
- **Preview alur** (`preview.php`): render pohon alur skema secara visual
  (rekursif, dengan deteksi node yang sudah pernah ditampilkan supaya tidak
  meledak eksponensial di grafik yang saling menyambung) supaya admin bisa
  memeriksa keseluruhan percabangan cerita tanpa harus memainkannya manual.
- **Simulator** (`simulator.php`): mainkan skema/karier apa pun (termasuk yang
  masih `draft`) langsung dari panel admin, memakai `GameEngine` yang **sama
  persis** dengan yang dipakai siswa sungguhan, tapi sepenuhnya di session
  (tidak menulis riwayat resmi ke database). **Manfaat keseluruhan modul:**
  memungkinkan admin menulis & menguji cerita bercabang yang kompleks secara
  mandiri, memverifikasi alurnya masuk akal sebelum di-publish ke siswa
  sungguhan.

### 11. Admin -- Tujuan Karier (`admin/tujuan/`)
CRUD "tujuan" yang bisa dipilih siswa di onboarding: nama + syarat resource
minimum (mis. "Ekonomi >= 80 & Mental >= 70"), plus misi wajib opsional yang
harus ikut tuntas. **Manfaat:** admin yang menentukan apa artinya "berhasil"
untuk tiap jenis tujuan, sehingga tolok ukur keberhasilan simulasi bisa terus
disesuaikan tanpa mengubah kode.

### 12. Admin -- Misi (`admin/misi/`)
CRUD "misi tuntutan karier" -- syarat tambahan yang melekat ke satu karier
(bukan ke tujuan), mis. syarat skill minimum atau wajib melalui skema tertentu.
Misi ini yang dirujuk sebagai syarat wajib oleh Tujuan Karier di atas.
**Manfaat:** memodelkan tuntutan spesifik-karier (mis. "karier Analis Data
menuntut skill teknis tinggi") terpisah dari tujuan pribadi siswa, supaya
keduanya bisa dikombinasikan secara fleksibel per tujuan.

### 13. Admin -- Data Pengguna (`admin/pengguna/list.php`)
Daftar seluruh siswa yang pernah mendaftar (pencarian nama/email + paginasi),
dengan ringkasan jumlah simulasi/selesai/burnout per siswa, dan tautan ke
laporan lengkap tiap siswa. **Manfaat:** titik masuk admin/guru untuk melihat
siapa saja yang sudah memakai aplikasi dan seberapa aktif mereka -- modul ini
menggantikan fitur "Kelas & Siswa" (pengelompokan per kelas) yang sebelumnya
ada, karena visibilitas sekarang bersifat global untuk semua admin/guru.

### 14. Admin -- Laporan (`admin/laporan/`)
Tiga tingkat laporan:
- **Dashboard agregat** (`list.php`): KPI (total pengguna, total simulasi,
  tingkat keberhasilan), grafik karier terpopuler, grafik hasil (selesai vs
  burnout) per karier, rasio ilusi/realita rata-rata seluruh pengguna, dan
  daftar pengguna paling aktif. **Manfaat:** "bank data" aplikasi -- pengelola
  bisa melihat seberapa berdampak aplikasi ini secara keseluruhan (bukan cuma
  per siswa), berguna untuk evaluasi program bimbingan karier sekolah.
- **Laporan per pengguna** (`pengguna.php?siswa_id=`): riwayat seluruh sesi
  simulasi satu siswa (status, karier yang dimainkan, tier hasil).
- **Detail per sesi** (`sesi.php?sesi_id=`): sama persis dengan yang dilihat
  siswa di `hasil.php`, ditambah **grafik dinamika resource** (garis
  ekonomi/skill/mental/waktu sepanjang sesi, SVG) supaya admin/guru bisa
  melihat pola naik-turun resource siswa secara visual, bukan cuma tabel angka.

## Keamanan Aplikasi

- **Password:** di-hash dengan `password_hash()` (bcrypt), tidak pernah
  disimpan/ditampilkan mentah.
- **CSRF:** token per-sesi (`Csrf::field()`/`Csrf::verify()`) wajib di semua
  form POST, termasuk endpoint AJAX (`karier/opsi_tambah.php`).
- **Rate limiting:** maksimal 5 percobaan login gagal / 15 menit per email
  (`RateLimiter`, tabel `login_attempts`), mencegah brute-force.
- **Session fixation:** `session_regenerate_id(true)` dipanggil di setiap
  login/logout (siswa maupun admin), dengan session key terpisah antara guard
  siswa dan admin supaya tidak bisa "double login" tak sengaja.
- **Otorisasi:** tiap halaman admin memanggil `Auth::requireAdmin()` (siapa pun
  admin/guru yang login) atau `Auth::requireRole('admin')` (khusus admin,
  dipakai di semua modul Konten: Karier/Skema/Tujuan/Misi) di baris paling
  atas.
- **Content-Security-Policy** ketat: `script-src 'self'` (tanpa
  `unsafe-inline`, tanpa CDN eksternal) -- semua interaksi JS (termasuk modal
  tambah-cepat minat/bakat) ditulis di file `.js` eksternal, semua grafik
  dibangun sebagai SVG oleh server, bukan lewat library chart pihak ketiga.
- **`APP_DEBUG` aman-secara-default:** konfigurasi produksi (`app/config/config.php`,
  ikut ter-commit) selalu `APP_DEBUG=false`; hanya file `app/config/local.php`
  (di-gitignore, tidak pernah ke server) yang boleh menyalakan mode debug lokal.

## Model Data Inti (Ringkas)

Lihat `database/schema.sql` untuk definisi lengkap & komentar per tabel. Peta
relasi inti:

- `siswa` (akun siswa) --1:N--> `game_session` (satu baris per playthrough) --1:N--> `game_riwayat` (log tiap keputusan, dengan kolom snapshot supaya tetap terbaca walau kontennya diedit admin belakangan)
- `admin_users` (role `admin`/`guru`) -- tidak lagi terhubung ke pengelompokan kelas (lihat Log Pembaruan)
- `karier` --N:M--> `minat`/`bakat` (syarat kecocokan), `karier` --1:N--> `misi`, `karier.skema_awal_id` -> `skema` (titik masuk alur)
- `skema` --1:N--> `skema_pilihan` --N:M--> `skema` lain lewat `skema_pilihan_trigger` (graf alur cerita, bisa siklik/konvergen)
- `tujuan_karier` --N:M--> `misi` (misi wajib), `game_session` --N:M--> `tujuan_karier` lewat `game_session_tujuan` (2 tujuan yang dipilih siswa, dengan status `tercapai`)

## Log Pembaruan

> Format: `Tanggal -- Ringkasan perubahan (dampak ke dokumen ini)`. Entri tanpa
> tanggal presisi adalah perubahan yang terjadi sebelum kebiasaan mencatat log
> ini dimulai.

- **Rilis awal (tanpa tanggal tercatat).** Pembangunan aplikasi dari nol:
  autentikasi siswa & admin/guru, skema database mengikuti GDD (skema
  utama/ilusi/realita/turunan/krisis sebagai node terpisah, syarat_muncul,
  trigger_skema_selanjutnya multi-kandidat), mesin simulasi (`GameEngine`)
  dipakai bersama simulator admin & permainan siswa, seluruh modul CRUD admin
  (Karier, Skema, Tujuan Karier, Misi), fitur Kelas & Siswa (siswa
  dikelompokkan per kelas, guru hanya bisa melihat kelasnya sendiri), dan
  laporan sederhana per kelas.
- **2026-08-20 -- Redesain visual total.** Sisi siswa diubah dari tampilan polos
  ke tema "Vibrant Gradient" (font self-hosted, animasi, HUD resource, dsb);
  panel admin diubah ke tema "dashboard bersih & profesional" terpisah dari
  tema siswa. Lihat bagian [Pilihan Desain / Template Visual](#pilihan-desain--template-visual).
- **2026-08-20 -- Empat perubahan fungsional besar:**
  1. Form Karier admin dilengkapi tombol "+ Baru" (modal, AJAX) untuk menambah
     Minat/Bakat langsung tanpa reset form -- lihat modul
     [Admin -- Karier](#9-admin----karier-admin-karier).
  2. Mekanik penyelesaian simulasi diperbaiki: alur sekarang memunculkan skema
     utama baru saat buntu, dan baru boleh berakhir kalau minimal 11 skema
     utama **dan** tujuan/misi terpenuhi (sebelumnya alur berhenti prematur
     begitu kehabisan kandidat trigger, tidak peduli progresnya) -- lihat
     modul [Mesin Simulasi](#7-mesin-simulasi----inti-permainan-homemainphp--appcoregameenginephp).
  3. Fitur **Kelas & Siswa dihapus**, diganti **Data Pengguna** (daftar semua
     siswa, visibilitas global, tanpa pengelompokan kelas) + **Laporan
     per-pengguna**. Role `guru` yang sebelumnya dibatasi per-kelas sekarang
     punya visibilitas sama luasnya dengan `admin` untuk Data Pengguna &
     Laporan (tetap tidak bisa mengedit Konten). Tabel `kelas` &
     kolom `siswa.kelas_id` masih ada di database (tidak dihapus, demi
     keamanan migrasi di production) tapi sudah tidak dipakai aplikasi.
  4. Halaman **Laporan admin diubah jadi dashboard analitik** ("bank data"):
     KPI agregat, grafik karier terpopuler, grafik hasil per karier, rasio
     ilusi/realita rata-rata, plus grafik dinamika resource (SVG) di laporan
     detail per sesi.
- **2026-08-20 -- Landing page tamu baru.** Halaman beranda untuk pengunjung
  yang belum login (`home/index.php`) diperluas dari sekadar hero singkat
  menjadi landing page penuh: hero + slider tagline, statistik singkat, kartu
  "kenapa simulasi ini", timeline "cara kerja", dan CTA banner penutup, lebar
  penuh halaman (bukan kolom sempit seperti halaman aplikasi lain), dengan
  animasi scroll-reveal.
- **2026-08-20 -- Perbaikan latar belakang.** Gradasi latar (`body`) diubah
  jadi `background-attachment:fixed` berukuran relatif viewport (`vw`/`vh`)
  supaya benar-benar diam relatif ke layar saat halaman di-scroll dan tidak
  ada lagi garis batas warna yang terlihat di halaman panjang.
- **2026-08-20 -- Dokumentasi ini dibuat.** File `DOKUMENTASI-APLIKASI.md`
  ditambahkan sebagai catatan hidup seluruh fitur/modul/alur/kebermanfaatan
  aplikasi.
- **2026-08-21 -- Bug fix: modal "+ Baru" minat/bakat tidak bisa ditutup.**
  `.modal-backdrop{display:flex}` di `admin.css` unconditional, menimpa
  atribut HTML `hidden` (author CSS mengalahkan default `[hidden]` browser)
  sehingga modal selalu tampil & memblokir seluruh form Karier. Diperbaiki
  dengan menambah `.modal-backdrop[hidden]{display:none}`. **Pelajaran yang
  dipakai di perubahan berikutnya:** elemen yang di-gate lewat JS tidak boleh
  dirender `hidden` dari server sama sekali -- JS yang menyuntikkan/menghapus
  keadaan tersembunyi saat runtime, supaya gagal-aman (bukan gagal-terkunci).
- **2026-08-26 -- Interaksi inti main.php & onboarding diubah supaya terasa
  seperti game, bukan angket** (Fase 1 dari brief besar; achievement, suara
  sintesis, tombol share-image, dan beranda "save-slot" penuh disimpan untuk
  lanjutan). Tidak ada perubahan skema database atau mekanik `GameEngine` --
  murni presentasi/derivasi dari data yang sudah ada:
  1. **main.php** dapat peta perjalanan (`render_journey_rail()`), label
     kalender hidup (`label_waktu_fiksi()`), ikon scene per tipe skema,
     kartu aksi dengan risk-preview arah dampak halus (`render_risk_icons()`,
     tanpa angka pasti), layar konsekuensi bertahap (gate JS murni, lihat
     modul [Mesin Simulasi](#7-mesin-simulasi----inti-permainan-homemainphp--appcoregameenginephp)),
     dan mood ambient (`mood_permainan()`) yang mewarnai latar & HUD mengikuti
     kondisi mental/krisis pemain.
  2. **minat.php/tujuan.php** jadi grid kartu tap (`.pick-grid`/`.pick-card`,
     menggantikan `.tags`/`.select-card` yang dihapus); tujuan.php tambah
     counter "X/2 dipilih" + kalimat preview kombinasi tujuan.
  3. **karier.php** jadi kartu ala pemilihan class RPG (`.class-card`) dengan
     badge gaya + indikator tantangan, dipilih dulu baru dikonfirmasi lewat
     satu tombol (radio `required`, bukan submit instan per kartu).
  4. **hasil.php** jadi "report card": verdict reveal beranimasi, bar ganda
     awal->akhir per resource, dan timeline mini keputusan kritis (reuse
     komponen `.timeline` dari landing page).
- **2026-08-26 -- Fase 2 dari brief game-feel: pencapaian, suara, bagikan
  hasil, beranda save-slot.** Melengkapi keempat item yang sengaja ditunda di
  Fase 1 (lihat entri sebelumnya). Tetap tanpa perubahan skema database:
  1. **Lencana/pencapaian** (`hitung_lencana()`) tampil di `hasil.php` sebagai
     chip statis + toast melayang -- lihat modul
     [Hasil & Refleksi Siswa](#8-hasil--refleksi-siswa-homehasilphp).
  2. **Feedback suara** disintesis (Web Audio API, tanpa file audio) untuk tap
     pilihan, konsekuensi positif/negatif, dan peringatan resource kritis,
     dengan toggle mute di HUD (`localStorage`) -- lihat modul
     [Mesin Simulasi](#7-mesin-simulasi----inti-permainan-homemainphp--appcoregameenginephp).
  3. **Tombol "Bagikan Hasil"** di `hasil.php` menggambar kartu ringkasan ke
     `<canvas>` dan mengunduhnya sebagai PNG, murni client-side.
  4. **Beranda (`index.php`) untuk siswa yang sudah login** diubah jadi
     "save slot" (`.save-slot`) dengan pita status + tier hasil, menggantikan
     kartu riwayat polos -- lihat modul [Beranda](#1-beranda-homeindexphp).
- **2026-08-26 -- Koreksi setelah uji coba langsung di production: sembunyikan
  semua angka resource dari siswa, longgarkan syarat tujuan, sederhanakan kartu
  karier, buat konsekuensi otomatis.** Masukan langsung dari pemilik aplikasi
  setelah mencoba alurnya sendiri. Tidak ada perubahan skema database:
  1. **Tujuan Karier** (`tujuan.php`): syarat resource sekarang ditampilkan
     sebagai narasi tingkat kebutuhan (`narasi_syarat_tujuan()`), bukan angka
     ambang mentah -- dipakai juga di `hasil.php`. `format_syarat_tujuan()`
     (angka pasti) tetap dipertahankan khusus untuk laporan admin/guru.
  2. **Jumlah tujuan yang boleh dipilih dilonggarkan dari wajib-tepat-2 jadi
     1 atau 2** (`count($dipilih) < 1 || count($dipilih) > 2`) -- lihat modul
     [Profil Diri -- Tujuan Karier](#5-profil-diri----tujuan-karier-hometujuanphp).
  3. **Kartu pilihan karier** (`karier.php`): label teks "Stabil &
     Realistis"/"Berisiko & Menggoda" **dihapus** (dianggap membocorkan
     kategorisasi ke siswa), diganti ikon dekoratif polos per kategori.
  4. **Risk-preview di kartu aksi main.php DIHAPUS lagi** (`render_risk_icons()`
     dihapus dari `functions.php`) -- fitur ini baru ditambahkan di entri Fase 1
     di atas, tapi setelah dicoba langsung ternyata membuat siswa memilih
     dengan menghitung untung-rugi resource, bukan berdasarkan pertimbangan
     pribadi/karakternya -- bertentangan dengan tujuan pedagogis aplikasi ini.
     **Pelajaran:** fitur yang terlihat "menambah rasa game" belum tentu cocok
     kalau mekanisme intinya butuh keputusan yang jujur/naif, bukan optimal.
  5. **Banner konsekuensi tidak lagi menampilkan delta angka resource sama
     sekali** (chip "Ekonomi -11" dst. dihapus) -- hanya narasi (asli atau
     fallback generik `narasi_efek_generik()`), dengan aksen warna kiri
     mengikuti sentimen bersih (`data-sentimen`) tanpa angka.
  6. **Alur konsekuensi jadi otomatis**: narasi diketik huruf-demi-huruf,
     lalu banner otomatis tertutup dan babak berikutnya otomatis tampil
     setelah jeda baca -- tombol "Lanjutkan" manual **dihapus**, diganti
     tap-untuk-mempercepat yang opsional. Tujuannya supaya pemain lebih
     banyak menikmati alurnya lewat sedikit klik, bukan mengklik terus-
     menerus. Lihat modul [Mesin Simulasi](#7-mesin-simulasi----inti-permainan-homemainphp--appcoregameenginephp)
     untuk detail lengkap poin 5 & 6.
