<?php
// ═══ Public site front controller ═══
require __DIR__ . '/app/bootstrap.php';
handle_status_api();   // ExpertAims HQ fleet monitoring (signed; 403 otherwise)
handle_command_api();  // ExpertAims HQ signed commands, e.g. remote admin reset (POST; 403 otherwise)

$uri  = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
$base = rtrim((string) config('base_url'), '/');
if ($base !== '' && strpos($uri, $base) === 0) $uri = substr($uri, strlen($base));
$path = trim(rawurldecode($uri), '/');
$path = preg_replace('/\.html$/', '', $path);

// ─── Contact enquiry capture (fetch POST from the contact form) ───
// Server-side gauntlet — every check independent of the client-side one:
// honeypot empty → session token matches → sum captcha correct → required
// fields present and valid → rate limit (5/hour/IP) → spam heuristics only
// FLAG (is_spam=1), so a mis-scored real enquiry still reaches the inbox.
if (($_GET['capture'] ?? '') === 'enquiry' && ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
  header('Content-Type: application/json');
  $fail = function ($err) { echo json_encode(['ok' => false, 'err' => $err]); exit; };
  if (!empty($_POST['company_website'])) { if (function_exists('security_log')) security_log('honeypot', 1, 'honeypot field filled'); $fail('blocked'); } // honeypot tripped
  if (($_SESSION['csrf'] ?? '') === ''
      || !hash_equals((string) $_SESSION['csrf'], (string) ($_POST['_token'] ?? ''))) $fail('session');
  if (!captcha_ok($_POST['sum'] ?? '')) $fail('captcha');
  $name    = mb_substr(trim((string) ($_POST['name'] ?? '')), 0, 160);
  $email   = mb_substr(trim((string) ($_POST['email'] ?? '')), 0, 190);
  $phone   = mb_substr(trim((string) ($_POST['phone'] ?? '')), 0, 60);
  $subject = mb_substr(trim((string) ($_POST['subject'] ?? '')), 0, 200);
  $message = mb_substr(trim((string) ($_POST['message'] ?? '')), 0, 5000);
  // strip control characters + header-injection newlines from one-line fields
  foreach (['name', 'email', 'phone', 'subject'] as $f) { $$f = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $$f); }
  $message = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $message);
  if ($name === '' || $message === '') $fail('fields');
  if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $fail('email');
  if ($phone !== '' && !preg_match('/^[0-9+()\-\.\s]{7,20}$/', $phone)) $fail('phone');
  if (form_throttled('enquiry')) $fail('rate');                        // 5 per IP per hour
  $spam = looks_spammy($name . ' ' . $email . ' ' . $subject . ' ' . $message) ? 1 : 0;
  try {
    try {
      db()->prepare('INSERT INTO submissions(name,email,phone,subject,message,page,is_spam) VALUES(?,?,?,?,?,?,?)')
        ->execute([$name, $email, $phone, $subject, $message, mb_substr(trim((string) ($_POST['_page'] ?? $path)), 0, 160), $spam]);
    } catch (\Throwable $e) { // pre-upgrade DB without is_spam — degrade gracefully
      db()->prepare('INSERT INTO submissions(name,email,phone,subject,message,page) VALUES(?,?,?,?,?,?)')
        ->execute([$name, $email, $phone, $subject, $message, mb_substr(trim((string) ($_POST['_page'] ?? $path)), 0, 160)]);
    }
  } catch (\Throwable $e) { $fail('server'); }
  captcha_rotate();   // a solved sum can't be replayed
  echo json_encode(['ok' => true]);
  exit;
}

// ─── Career application capture (job "Apply" form → fetch POST) ───
// Same server-side gauntlet as the contact form, PLUS a time-trap: honeypot →
// session token → sum captcha → time-trap (≥2s since the form rendered) →
// required first/last name + valid email + optional-but-validated phone/YouTube →
// rate limit (5/hour/IP) → spam heuristics FLAG (never drop). Records which JOB
// (job title) and page the application came from.
if (($_GET['capture'] ?? '') === 'application' && ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
  if (!feature_on('applications')) { http_response_code(404); exit; }
  header('Content-Type: application/json');
  $fail = function ($err) { echo json_encode(['ok' => false, 'err' => $err]); exit; };
  // A multipart POST that PHP dropped because it exceeded post_max_size /
  // upload_max_filesize arrives with an empty $_POST but a non-zero Content-Length
  // — surface a friendly "too large" message (with the email fallback in the UI)
  // rather than a silent failure.
  if (empty($_POST) && (int) ($_SERVER['CONTENT_LENGTH'] ?? 0) > 0) $fail('toolarge');
  if (!empty($_POST['company_website'])) { if (function_exists('security_log')) security_log('honeypot', 1, 'honeypot field filled'); $fail('blocked'); } // honeypot tripped
  if (($_SESSION['csrf'] ?? '') === ''
      || !hash_equals((string) $_SESSION['csrf'], (string) ($_POST['_token'] ?? ''))) $fail('session');
  if (!captcha_ok($_POST['sum'] ?? '')) $fail('captcha');
  $openedAt = (int) ($_SESSION['app_time'] ?? 0);
  if ($openedAt <= 0 || (time() - $openedAt) < 2) $fail('blocked');   // time-trap (generic err → bot learns nothing)
  $first = mb_substr(trim((string) ($_POST['first_name'] ?? '')), 0, 80);
  $last  = mb_substr(trim((string) ($_POST['last_name'] ?? '')), 0, 80);
  $phone = mb_substr(trim((string) ($_POST['phone'] ?? '')), 0, 60);
  $email = mb_substr(trim((string) ($_POST['email'] ?? '')), 0, 190);
  $loc   = mb_substr(trim((string) ($_POST['location'] ?? '')), 0, 160);
  $video = mb_substr(trim((string) ($_POST['video_url'] ?? '')), 0, 300);
  $jobT  = mb_substr(trim((string) ($_POST['job_title'] ?? '')), 0, 200);
  $relRaw = strtolower(trim((string) ($_POST['relocate'] ?? '')));
  $relocate = ($relRaw === 'yes' || $relRaw === '1') ? 'Yes' : (($relRaw === 'no' || $relRaw === '0') ? 'No' : '');
  $message = mb_substr(trim((string) ($_POST['message'] ?? '')), 0, 5000);
  // strip control characters + header-injection newlines from one-line fields
  foreach (['first', 'last', 'phone', 'email', 'loc', 'video', 'jobT'] as $f) { $$f = preg_replace('/[\x00-\x1F\x7F]/u', ' ', $$f); }
  $message = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $message);
  if ($first === '' || $last === '') $fail('fields');
  if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $fail('email');
  if ($phone !== '' && !preg_match('/^[0-9+()\-\.\s]{7,20}$/', $phone)) $fail('phone');
  if ($video !== '' && !preg_match('#^https?://(www\.|m\.)?(youtube\.com|youtu\.be)/#i', $video)) $fail('video');
  if (form_throttled('application')) $fail('rate');                    // 5 per IP per hour
  $spam = looks_spammy($first . ' ' . $last . ' ' . $email . ' ' . $message . ' ' . $video) ? 1 : 0;
  $pageFrom = mb_substr(trim((string) ($_POST['_page'] ?? $path)), 0, 160);

  // ─── Secure file attachments (resumes are PII) ───
  // Up to three OPTIONAL files (Resume / Teaching philosophy / Teaching-or-demo
  // document). EACH file is validated by BOTH an extension whitelist AND a finfo
  // MIME whitelist, capped at 10 MB, and stored under uploads/applications/ with a
  // RANDOM name (the user's filename is kept only as a DB label — never reused on
  // disk, never web-reachable: uploads/applications/.htaccess denies all access;
  // downloads go only through the authenticated admin route ?p=application_file).
  // A disallowed type or an oversize file rejects the whole submission with a
  // clear error. If the folder can't be written the text fields still save and we
  // flag that files couldn't be stored (graceful — the UI shows the email fallback).
  $fileMap    = ['file_resume' => 1, 'file_philosophy' => 2, 'file_sample' => 3];
  $allowedExt = [
    'pdf'  => ['application/pdf'],
    'doc'  => ['application/msword', 'application/octet-stream', 'application/vnd.ms-office', 'application/x-ole-storage'],
    'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/zip', 'application/x-zip-compressed', 'application/octet-stream'],
    'txt'  => ['text/plain', 'application/octet-stream'],
    'rtf'  => ['application/rtf', 'text/rtf', 'text/plain', 'application/msword', 'application/octet-stream'],
  ];
  $maxBytes = 10 * 1024 * 1024;                                        // 10 MB per file
  $stored   = [1 => ['name' => null, 'orig' => null], 2 => ['name' => null, 'orig' => null], 3 => ['name' => null, 'orig' => null]];
  $storedPaths = [];   // absolute paths of files moved into place (unlinked if the DB insert has to fall back)
  $fileNote = false;   // true when a folder/write problem meant one or more files were skipped
  $upDir  = __DIR__ . '/uploads/applications';
  $finfo  = function_exists('finfo_open') ? finfo_open(FILEINFO_MIME_TYPE) : null;
  foreach ($fileMap as $field => $slot) {
    if (!isset($_FILES[$field])) continue;
    $ferr = $_FILES[$field]['error'] ?? UPLOAD_ERR_NO_FILE;
    if ($ferr === UPLOAD_ERR_NO_FILE) continue;                        // optional — not provided
    if ($ferr === UPLOAD_ERR_INI_SIZE || $ferr === UPLOAD_ERR_FORM_SIZE) { if ($finfo) finfo_close($finfo); foreach ($storedPaths as $pp) @unlink($pp); $fail('filesize'); }
    if ($ferr !== UPLOAD_ERR_OK) { $fileNote = true; continue; }       // partial / no tmp dir — skip gracefully
    $tmp  = (string) ($_FILES[$field]['tmp_name'] ?? '');
    $orig = (string) ($_FILES[$field]['name'] ?? '');
    $size = (int) ($_FILES[$field]['size'] ?? 0);
    if ($tmp === '' || !is_uploaded_file($tmp)) { $fileNote = true; continue; }
    if ($size <= 0 || $size > $maxBytes) { if ($finfo) finfo_close($finfo); foreach ($storedPaths as $pp) @unlink($pp); $fail('filesize'); }
    $ext = strtolower(pathinfo($orig, PATHINFO_EXTENSION));
    if (!isset($allowedExt[$ext])) { if ($finfo) finfo_close($finfo); foreach ($storedPaths as $pp) @unlink($pp); $fail('filetype'); }
    $mime = $finfo ? (string) @finfo_file($finfo, $tmp) : '';
    if ($mime !== '' && !in_array($mime, $allowedExt[$ext], true)) { if ($finfo) finfo_close($finfo); foreach ($storedPaths as $pp) @unlink($pp); $fail('filetype'); }
    if (!is_dir($upDir)) @mkdir($upDir, 0755, true);
    $rand = bin2hex(random_bytes(16)) . '.' . $ext;                    // never the user's filename
    $dest = $upDir . '/' . $rand;
    if (!is_dir($upDir) || !is_writable($upDir) || !@move_uploaded_file($tmp, $dest)) { $fileNote = true; continue; }
    @chmod($dest, 0640);
    $label = mb_substr(preg_replace('/[\x00-\x1F\x7F]/u', '', $orig), 0, 200);
    $stored[$slot] = ['name' => $rand, 'orig' => ($label !== '' ? $label : $rand)];
    $storedPaths[] = $dest;
  }
  if ($finfo) finfo_close($finfo);

  // Insert WITH the file columns; on a pre-upgrade DB (no file columns) fall back
  // to the text-only insert and drop the just-stored files so nothing is orphaned.
  $insDone = false;
  try {
    db()->prepare('INSERT INTO applications(job_title,first_name,last_name,phone,email,location,relocate,message,video_url,page,is_spam,file1_name,file1_orig,file2_name,file2_orig,file3_name,file3_orig) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)')
      ->execute([$jobT, $first, $last, $phone, $email, $loc, $relocate, $message, $video, $pageFrom, $spam,
                 $stored[1]['name'], $stored[1]['orig'], $stored[2]['name'], $stored[2]['orig'], $stored[3]['name'], $stored[3]['orig']]);
    $insDone = true;
  } catch (\Throwable $e) {
    try {
      db()->prepare('INSERT INTO applications(job_title,first_name,last_name,phone,email,location,relocate,message,video_url,page,is_spam) VALUES(?,?,?,?,?,?,?,?,?,?,?)')
        ->execute([$jobT, $first, $last, $phone, $email, $loc, $relocate, $message, $video, $pageFrom, $spam]);
      $insDone = true;
      foreach ($storedPaths as $pp) @unlink($pp);                      // columns missing → don't orphan files
      if ($storedPaths) $fileNote = true;
    } catch (\Throwable $e2) { $insDone = false; }
  }
  if (!$insDone) { foreach ($storedPaths as $pp) @unlink($pp); $fail('server'); }
  captcha_rotate();   // a solved sum can't be replayed
  // Best-effort email notification to the careers inbox (the DB row above is the
  // record of truth; this just tells the team by email too).
  ea_notify_application([
    'job' => $jobT, 'first' => $first, 'last' => $last, 'email' => $email, 'phone' => $phone,
    'video' => $video, 'message' => $message, 'page' => $pageFrom, 'spam' => $spam,
    'file1' => $stored[1]['orig'], 'file2' => $stored[2]['orig'], 'file3' => $stored[3]['orig'],
  ]);
  echo json_encode($fileNote ? ['ok' => true, 'filenote' => 1] : ['ok' => true]);
  exit;
}

// ─── Newsletter signup capture (homepage popup → fetch POST) ───
// Hardened, all server-side (the client checks are courtesy only):
//   • honeypot empty            • session-bound single-use token
//   • sum captcha (random operands, validated here only)
//   • time-trap (reject < 2s after the popup rendered)
//   • strict email + length cap + disposable-domain blocklist (flag, don't drop)
//   • per-IP rate limit (5/hour, shared login-style throttle)
// Errors are deliberately generic so a bot learns nothing about which gate it hit.
if (($_GET['capture'] ?? '') === 'subscribe' && ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
  header('Content-Type: application/json');
  $ok = false; $spam = 0;
  $want    = (int) ($_SESSION['sub_a'] ?? 0) + (int) ($_SESSION['sub_b'] ?? 0);
  $tokenOk = ($_SESSION['csrf'] ?? '') !== '' && hash_equals((string) $_SESSION['csrf'], (string) ($_POST['_token'] ?? ''));
  $openedAt = (int) ($_SESSION['sub_time'] ?? 0);
  $fastBot  = $openedAt <= 0 || (time() - $openedAt) < 2;    // time-trap
  $sumOk    = $want > 0 && (int) ($_POST['sum'] ?? -1) === $want;
  $email    = strtolower(trim((string) ($_POST['email'] ?? '')));
  $email    = preg_replace('/[\x00-\x1F\x7F]/u', '', $email);
  $emailOk  = $email !== '' && mb_strlen($email) <= 190 && filter_var($email, FILTER_VALIDATE_EMAIL);
  if (empty($_POST['company_website']) && $tokenOk && $sumOk && $emailOk && !$fastBot
      && !form_throttled('subscribe', 5, 3600)) {
    if (email_domain_blocked($email)) $spam = 1;   // keep it, but flag as spam
    $pageFrom = mb_substr(trim((string) ($_POST['_page'] ?? $path)), 0, 160);
    try {
      try {
        db()->prepare('INSERT INTO subscribers(email,page,is_spam) VALUES(?,?,?) ON DUPLICATE KEY UPDATE email=email')
          ->execute([$email, $pageFrom, $spam]);
      } catch (\Throwable $e) { // pre-upgrade DB without is_spam — degrade gracefully
        db()->prepare('INSERT IGNORE INTO subscribers(email,page) VALUES(?,?)')->execute([$email, $pageFrom]);
      }
      $ok = true;
      unset($_SESSION['sub_a'], $_SESSION['sub_b'], $_SESSION['sub_time']); // single-use — no replay
    } catch (\Throwable $e) { $ok = false; }
  }
  echo json_encode(['ok' => $ok]);
  exit;
}

// ─── Certificate verification (GET /verify or /verify?id=...) ───
if ($path === 'verify') {
  // Check if request wants JSON (API mode) vs HTML (page mode)
  $want_json = isset($_GET['id']) && !isset($_GET['page']);

  if ($want_json) {
    // API mode: return JSON
    header('Content-Type: application/json');
    $cert_id = (string) ($_GET['id'] ?? '');
    if ($cert_id === '') {
      http_response_code(400);
      echo json_encode(['ok' => false, 'error' => 'missing_id']);
      exit;
    }
    $cert = find_certificate($cert_id);
    if (!$cert) {
      http_response_code(404);
      echo json_encode(['ok' => false, 'error' => 'not_found']);
      exit;
    }
    echo json_encode(['ok' => true, 'certificate' => [
      'id' => (string) $cert['certificate_id'],
      'name' => (string) $cert['internee_name'],
      'role' => (string) $cert['role'],
      'company' => (string) $cert['company'],
      'location' => (string) ($cert['location'] ?? ''),
      'start_date' => (string) $cert['start_date'],
      'end_date' => (string) $cert['end_date'],
      'issued_date' => (string) $cert['issued_date'],
      'education' => (string) ($cert['education'] ?? ''),
      'institution' => (string) ($cert['institution'] ?? ''),
      'country' => (string) ($cert['country'] ?? ''),
      'description' => (string) ($cert['description'] ?? ''),
    ]]);
    exit;
  }

  // HTML page mode: show verification form + results
  $cert_id = (string) ($_GET['id'] ?? '');
  $cert = $cert_id !== '' ? find_certificate($cert_id) : null;

  // Create page object for verification page
  $page = [
    'id' => 0,
    'slug' => 'verify',
    'title' => 'Verify Certificate',
    'nav_label' => null,
    'menu_parent' => null,
    'hero_eyebrow' => 'Certificate Verification',
    'hero_heading' => 'Verify Your Certificate',
    'hero_subtext' => 'Enter your certificate ID to verify your ExpertAims internship or course certificate',
    'hero_image' => '',
    'hero_show' => 1,
    'meta_title' => 'Verify Certificate | ExpertAims',
    'meta_description' => 'Verify your ExpertAims internship or course completion certificate using your unique certificate ID.',
    'canonical' => null,
    'og_image' => null,
    'robots' => 'index,follow',
  ];

  // Build the verification form and results HTML
  $content = <<<'HTML'
<section class="sec rv" style="max-width:1200px;margin:0 auto;padding:3rem 1.5rem">
  <div style="display:grid;grid-template-columns:1fr 1fr;gap:3rem;align-items:start" style="@media(max-width:768px){grid-template-columns:1fr;gap:2rem}">
    <!-- Search Panel -->
    <div>
      <div style="background:var(--sur);border:1px solid var(--line);border-radius:8px;padding:2rem">
        <h3 style="margin-top:0;color:var(--ink);font-size:1.25rem">Verify Your Certificate</h3>
        <label for="cert-id" style="display:block;margin-bottom:0.5rem;font-weight:600;color:var(--ink)">Certificate ID</label>
        <input
          type="text"
          id="cert-id"
          placeholder="e.g., EA-DEMO-2026-0001"
          autocomplete="off"
          style="width:100%;padding:0.75rem;margin-bottom:1rem;border:1px solid var(--line);border-radius:4px;font-size:1rem;font-family:monospace"
        />
        <button id="verify-btn" onclick="verifyCertificate()"
          style="width:100%;padding:0.75rem;background:#c41e3a;color:white;border:none;border-radius:4px;font-size:1rem;font-weight:600;cursor:pointer;transition:background 0.2s">
          Verify Certificate
        </button>

        <div class="loading" id="loading" style="display:none;text-align:center;padding:1rem">
          <div style="display:inline-block;width:24px;height:24px;border:3px solid #f3f3f3;border-top:3px solid #c41e3a;border-radius:50%;animation:spin 1s linear infinite"></div>
          <p style="color:var(--ink2);margin-top:0.5rem;font-size:0.9rem">Verifying...</p>
        </div>

        <div class="cert-result" id="result" style="background:var(--sur);border:1px solid var(--line);border-radius:8px;padding:1.5rem;margin-top:1.5rem;display:none">
          <p class="status" id="status-msg" style="margin:0;font-weight:600"></p>
          <div class="cert-details" id="cert-details" style="background:white;border-radius:4px;padding:1rem;margin-top:1rem"></div>
        </div>
      </div>
    </div>

    <!-- Info Panel -->
    <div>
      <div class="cert-faq"><h3 style="margin-top:0;color:#FFFFFF;font-size:1.25rem;font-weight:600">About Certificate Verification</h3>
      <p style="line-height:1.6;color:#F0F0F0;font-weight:500">
        ExpertAims Consultancy issues certificates to interns and course participants who successfully complete their programs. Each certificate has a unique ID that can be verified using this tool.
      </p>

      <style>
  .cert-faq { color: var(--ink); }
  @media (prefers-color-scheme: dark) { .cert-faq { color: var(--ink); } }
  </style>
  <div class="cert-faq" style="background:var(--sur);border-left:4px solid #c41e3a;padding:1.5rem;margin-top:1.5rem;border-radius:4px">
        <h4 style="margin-top:0;margin-bottom:0.5rem;color:var(--ink);font-size:1rem">Where is the Certificate ID?</h4>
        <p style="margin:0.5rem 0;color:var(--ink2);font-size:0.95rem">The certificate ID appears in the top-right corner of your certificate (e.g., EA-DEMO-2026-0001).</p>

        <h4 style="margin-top:1rem;margin-bottom:0.5rem;color:var(--ink);font-size:1rem">What information will be displayed?</h4>
        <p style="margin:0.5rem 0;color:var(--ink2);font-size:0.95rem">The verification will show your name, role, company location, internship dates, and other key details from your certificate.</p>

        <h4 style="margin-top:1rem;margin-bottom:0.5rem;color:var(--ink);font-size:1rem">Why verify?</h4>
        <p style="margin:0.5rem 0;color:var(--ink2);font-size:0.95rem">This verification tool allows employers and educational institutions to confirm the authenticity of your internship or course completion certificate.</p>

        <h4 style="margin-top:1rem;margin-bottom:0.5rem;color:var(--ink);font-size:1rem">Didn't find your certificate?</h4>
        <p style="margin:0.5rem 0;color:var(--ink2);font-size:0.95rem">If your certificate ID doesn't appear, please contact us at <a href="mailto:info@expertaims.com" style="color:var(--acc)">info@expertaims.com</a>.</p>
      </div>
    </div>
  </div>
</section>

<style>
  @keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
  }
  .cert-result.success { background:var(--sur) !important; border-color:var(--line) !important }
  .cert-result.success .status { color:var(--acc) !important }
  .cert-result.error { background:var(--sur) !important; border-color:var(--line) !important }
  .cert-result.error .status { color:var(--acc) !important }
  .cert-details { background:var(--sur) !important }
  .cert-detail-row { display:flex; justify-content:space-between; padding:0.75rem 0; border-bottom:1px solid var(--line) }
  .cert-detail-row:last-child { border-bottom:none }
  .cert-detail-row strong { color:var(--ink); flex-basis:35% }
  .cert-detail-row span { color:var(--ink2); flex:1; text-align:right }
  @media(max-width:768px) {
    [style*="grid-template-columns:1fr 1fr"] { grid-template-columns:1fr !important; gap:2rem !important }
  }
</style>

<script>
const baseUrl = location.origin;

function verifyCertificate() {
  const certId = document.getElementById('cert-id').value.trim().toUpperCase();
  const result = document.getElementById('result');
  const loading = document.getElementById('loading');
  const details = document.getElementById('cert-details');
  const statusMsg = document.getElementById('status-msg');
  const btn = document.getElementById('verify-btn');

  if (!certId) {
    statusMsg.textContent = 'Please enter a certificate ID';
    result.classList.add('error');
    result.classList.remove('success');
    result.style.display = 'block';
    return;
  }

  result.style.display = 'none';
  result.classList.remove('success', 'error');
  loading.style.display = 'block';
  btn.disabled = true;

  fetch(baseUrl + '/verify?id=' + encodeURIComponent(certId))
    .then(res => res.json())
    .then(data => {
      loading.style.display = 'none';
      btn.disabled = false;

      if (data.ok && data.certificate) {
        const cert = data.certificate;
        const startDate = new Date(cert.start_date).toLocaleDateString('en-US',
          { year:'numeric', month:'short', day:'numeric' });
        const endDate = new Date(cert.end_date).toLocaleDateString('en-US',
          { year:'numeric', month:'short', day:'numeric' });
        const issuedDate = new Date(cert.issued_date).toLocaleDateString('en-US',
          { year:'numeric', month:'short', day:'numeric' });

        statusMsg.innerHTML = '<strong>Certificate Verified</strong>';
        details.innerHTML = `
          <div class="cert-detail-row"><strong>Name</strong><span>${escapeHtml(cert.name)}</span></div>
          <div class="cert-detail-row"><strong>Role</strong><span>${escapeHtml(cert.role)}</span></div>
          <div class="cert-detail-row"><strong>Company</strong><span>${escapeHtml(cert.company)}</span></div>
          ${cert.location ? `<div class="cert-detail-row"><strong>Location</strong><span>${escapeHtml(cert.location)}</span></div>` : ''}
          <div class="cert-detail-row"><strong>Duration</strong><span>${startDate} to ${endDate}</span></div>
          ${cert.education ? `<div class="cert-detail-row"><strong>Education</strong><span>${escapeHtml(cert.education)}</span></div>` : ''}
          ${cert.institution ? `<div class="cert-detail-row"><strong>Institution</strong><span>${escapeHtml(cert.institution)}</span></div>` : ''}
          ${cert.country ? `<div class="cert-detail-row"><strong>Country</strong><span>${escapeHtml(cert.country)}</span></div>` : ''}
          <div class="cert-detail-row"><strong>Certificate ID</strong><span style="font-family:monospace;font-size:0.9rem">${escapeHtml(cert.id)}</span></div>
          <div class="cert-detail-row"><strong>Issued Date</strong><span>${issuedDate}</span></div>
          ${cert.description ? `<div class="cert-detail-row"><strong>Notes</strong><span>${escapeHtml(cert.description)}</span></div>` : ''}
        `;
        result.classList.add('success');
        result.style.display = 'block';
      } else {
        statusMsg.textContent = 'Certificate not found';
        details.innerHTML = `<p style="color:var(--ink2);margin:0">No certificate found with ID: <strong>${escapeHtml(certId)}</strong></p>
          <p style="color:var(--mut);font-size:0.9rem;margin:0.5rem 0 0">Please check the ID and try again.</p>`;
        result.classList.add('error');
        result.style.display = 'block';
      }
    })
    .catch(err => {
      loading.style.display = 'none';
      btn.disabled = false;
      statusMsg.textContent = 'Error verifying certificate';
      details.innerHTML = '<p style="color:var(--ink2);margin:0">An error occurred. Please try again later.</p>';
      result.classList.add('error');
      result.style.display = 'block';
      console.error('Error:', err);
    });
}

function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

// Allow Enter key to trigger verification
document.getElementById('cert-id').addEventListener('keypress', function(e) {
  if (e.key === 'Enter') verifyCertificate();
});

// Pre-fill and auto-verify from URL parameter
const urlParams = new URLSearchParams(location.search);
const certParam = urlParams.get('id');
if (certParam) {
  document.getElementById('cert-id').value = certParam;
  setTimeout(() => verifyCertificate(), 200);
}
</script>
HTML;

  echo render_document($page, $content);
  exit;
}

// ─── Regular page ───  ('' and 'index' both map to the Home page)
$slug = ($path === '' || $path === 'index') ? 'home' : $path;
$page = find_page($slug, true);

if (!$page) {
  http_response_code(404);
  $p = [
    'slug' => '404', 'title' => 'Page not found', 'meta_title' => '404 · Not found | ' . setting('site_name'),
    'meta_description' => null, 'canonical' => null, 'og_image' => null,
    'robots' => 'noindex,follow',
  ];
  echo render_document($p,
      '<section class="sec rv" style="min-height:56vh;display:grid;place-items:center;text-align:center">'
    . '<div class="wrap"><p class="stn">Error 404</p>'
    . '<h1 class="d1" style="margin-top:14px">Page not found<span class="aim" aria-hidden="true"></span></h1>'
    . '<p class="lede" style="margin:20px auto 28px;max-width:46ch">The page you&rsquo;re looking for doesn&rsquo;t exist or has moved.</p>'
    . '<a class="btn btn-acc" href="' . base_url() . '">Back to home</a></div></section>');
  exit;
}

// Hosting page turned OFF in the CMS → show the old-style fallback that links to expertaims.xyz.
if ($slug === 'hosting' && function_exists('hosting_page_enabled') && !hosting_page_enabled()) {
  $ext = function_exists('hosting_external_url') ? hosting_external_url() : setting('hosting_url', 'https://expertaims.xyz');
  $page['robots'] = 'noindex,follow';   // off = don't index the placeholder
  echo render_document($page,
      '<section class="sec rv" style="min-height:52vh;display:grid;place-items:center;text-align:center">'
    . '<div class="wrap"><p class="stn">Web hosting</p>'
    . '<h1 class="d1" style="margin-top:14px">Our hosting lives here<span class="aim" aria-hidden="true"></span></h1>'
    . '<p class="lede" style="margin:20px auto 28px;max-width:48ch">Browse plans, domains and business email, and manage your account on our hosting site.</p>'
    . '<a class="btn btn-acc" href="' . e($ext) . '" target="_blank" rel="noopener">Go to our hosting site &#8594;</a></div></section>');
  exit;
}

if (feature_on('visits')) track_visit($slug);   // first-party visit counter (admin dashboard) — 404s above are never counted

echo render_document($page, render_sections($page['id']));
