true, 'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off', 'samesite' => 'Lax', 'path' => '/']);
session_start();
header('Cache-Control: no-store');
header('X-Content-Type-Options: nosniff');
header("Content-Security-Policy: default-src 'self'; style-src 'self'; script-src 'self'; img-src 'self' data:; form-action 'self'; frame-ancestors 'none'; base-uri 'self'");
if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32));
}
date_default_timezone_set('Asia/Jakarta');
const CATEGORIES = [1 => 'Matematika', 2 => 'Sinonim', 3 => 'Antonim', 4 => 'Padanan kata', 5 => 'Kepribadian'];
const STAGES = [0 => 'Calon kandidat', 1 => 'Tidak dilanjutkan', 2 => 'Tes offline & interview', 3 => 'Diterima', 99 => 'Demo / arsip'];
function e($value) { return htmlspecialchars(str_replace("\0", '', (string)$value), ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
function redirect($url) { header('Location: ' . $url); exit; }
function csrf_field() { echo ''; }
function verify_csrf() { if (!hash_equals($_SESSION['csrf'], (string)($_POST['csrf'] ?? ''))) { http_response_code(403); exit('Sesi formulir tidak valid. Muat ulang halaman.'); } }
function require_admin() {
if (empty($_SESSION['admin_id']) || time() - ($_SESSION['admin_active'] ?? 0) > 3600) {
unset($_SESSION['admin_id']); redirect('login.php');
}
$_SESSION['admin_active'] = time();
}
function db() {
static $connection;
if ($connection) return $connection;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
require __DIR__ . '/../includes/conn.php';
$connection = $conn;
$connection->set_charset('utf8mb4');
return $connection;
}
function query($sql, $types = '', $params = []) {
$statement = db()->prepare($sql);
if ($types !== '') $statement->bind_param($types, ...$params);
$statement->execute();
return $statement;
}
function rows($sql, $types = '', $params = []) { return query($sql, $types, $params)->get_result()->fetch_all(MYSQLI_ASSOC); }
function one($sql, $types = '', $params = []) { return rows($sql, $types, $params)[0] ?? null; }
function threshold() { return max(0, (int)(getenv('REC_PASS_SCORE') ?: 300)); }
function fail_page($message, $status = 503) { http_response_code($status); page_start('Layanan belum siap'); echo ' ' . e($message) . 'Layanan belum siap