<?php
require_once __DIR__ . '/Includes.php';
check_login('learner');

$pdo->exec("CREATE TABLE IF NOT EXISTS classes (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  email VARCHAR(150) NOT NULL UNIQUE,
  password_hash VARCHAR(255) NOT NULL,
  role ENUM('admin', 'teacher', 'hop', 'accountant', 'parent') NOT NULL DEFAULT 'teacher',
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");

try {
  $pdo->exec("ALTER TABLE users MODIFY role ENUM('admin', 'teacher', 'hop', 'accountant', 'parent') NOT NULL DEFAULT 'teacher'");
} catch (PDOException $e) {
  // Ignore when schema is already updated.
}

$pdo->exec("CREATE TABLE IF NOT EXISTS subjects (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  class_id INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_subjects_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS learners (
  reg_number VARCHAR(30) NOT NULL PRIMARY KEY,
  id INT NOT NULL AUTO_INCREMENT UNIQUE,
  class_id INT NOT NULL,
  date_of_admission DATE NULL,
  surname VARCHAR(100) NULL,
  christian_names VARCHAR(150) NULL,
  full_name VARCHAR(150) NOT NULL,
  date_of_birth_info VARCHAR(40) NULL,
  birth_certificate_number VARCHAR(80) NULL,
  religious_denomination VARCHAR(120) NULL,
  gender ENUM('Male', 'Female', 'Other') NOT NULL DEFAULT 'Other',
  guardian_name VARCHAR(120) NULL,
  guardian_national_id VARCHAR(60) NULL,
  guardian_address VARCHAR(255) NULL,
  guardian_phone VARCHAR(40) NULL,
  student_photo_path VARCHAR(255) NULL,
  contact_email VARCHAR(150) NULL,
  password_hash VARCHAR(255) NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_learners_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS elearning_posts (
  id INT AUTO_INCREMENT PRIMARY KEY,
  teacher_id INT NOT NULL,
  class_id INT NOT NULL,
  subject_id INT NULL,
  content_type ENUM('assignment','exercise','note','video') NOT NULL DEFAULT 'assignment',
  title VARCHAR(180) NOT NULL,
  instructions TEXT NULL,
  resource_url VARCHAR(255) NULL,
  due_date DATE NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_ep_teacher FOREIGN KEY (teacher_id) REFERENCES users(id) ON DELETE CASCADE,
  CONSTRAINT fk_ep_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE,
  CONSTRAINT fk_ep_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE SET NULL,
  INDEX idx_ep_teacher_created (teacher_id, created_at),
  INDEX idx_ep_class (class_id)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS learner_marks (
  id INT AUTO_INCREMENT PRIMARY KEY,
  learner_id INT NOT NULL,
  teacher_id INT NOT NULL,
  class_id INT NOT NULL,
  subject_id INT NOT NULL,
  assessment_type ENUM(
    'daily_exercise',
    'weekly_test',
    'end_of_month_test',
    'end_of_term_exam',
    'midyear_exam',
    'end_of_year_exam'
  ) NOT NULL,
  term_name VARCHAR(30) NOT NULL,
  assessment_date DATE NOT NULL,
  score DECIMAL(6,2) NOT NULL,
  max_score DECIMAL(6,2) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_marks_learner FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
  CONSTRAINT fk_marks_teacher FOREIGN KEY (teacher_id) REFERENCES users(id) ON DELETE CASCADE,
  CONSTRAINT fk_marks_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE,
  CONSTRAINT fk_marks_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE CASCADE,
  INDEX idx_marks_teacher_date (teacher_id, assessment_date),
  INDEX idx_marks_type (assessment_type)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS fee_invoices (
  id INT AUTO_INCREMENT PRIMARY KEY,
  invoice_number VARCHAR(40) NOT NULL UNIQUE,
  learner_id INT NOT NULL,
  class_id INT NOT NULL,
  description VARCHAR(255) NOT NULL,
  amount_due DECIMAL(10,2) NOT NULL,
  fee_gpf DECIMAL(10,2) NOT NULL DEFAULT 0,
  fee_ssf DECIMAL(10,2) NOT NULL DEFAULT 0,
  fee_sdc DECIMAL(10,2) NOT NULL DEFAULT 0,
  fee_registration DECIMAL(10,2) NOT NULL DEFAULT 0,
  fee_zimsec DECIMAL(10,2) NOT NULL DEFAULT 0,
  due_date DATE NULL,
  status ENUM('unpaid', 'partially_paid', 'paid') NOT NULL DEFAULT 'unpaid',
  created_by INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  CONSTRAINT fk_fee_invoices_learner FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
  CONSTRAINT fk_fee_invoices_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE,
  CONSTRAINT fk_fee_invoices_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE CASCADE,
  INDEX idx_fee_invoices_learner (learner_id),
  INDEX idx_fee_invoices_status (status),
  INDEX idx_fee_invoices_due (due_date)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS fee_payments (
  id INT AUTO_INCREMENT PRIMARY KEY,
  invoice_id INT NULL,
  learner_id INT NOT NULL,
  amount_paid DECIMAL(10,2) NOT NULL,
  currency_code ENUM('USD','ZIG','RAND') NOT NULL DEFAULT 'USD',
  fx_rate_to_usd DECIMAL(18,8) NOT NULL DEFAULT 1,
  amount_paid_usd DECIMAL(12,2) NOT NULL DEFAULT 0,
  payment_date DATE NOT NULL,
  fee_component VARCHAR(10) NOT NULL DEFAULT 'GPF',
  payment_method ENUM('cash','ecocash','bank_transfer','pos','other') NOT NULL DEFAULT 'cash',
  reference_no VARCHAR(80) NULL,
  note VARCHAR(255) NULL,
  received_by INT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_fee_payments_invoice FOREIGN KEY (invoice_id) REFERENCES fee_invoices(id) ON DELETE SET NULL,
  CONSTRAINT fk_fee_payments_learner FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
  CONSTRAINT fk_fee_payments_user FOREIGN KEY (received_by) REFERENCES users(id) ON DELETE CASCADE,
  INDEX idx_fee_payments_learner_date (learner_id, payment_date),
  INDEX idx_fee_payments_invoice (invoice_id)
)");

$learnerInvoiceColumnsToEnsure = [
  "fee_gpf DECIMAL(10,2) NOT NULL DEFAULT 0",
  "fee_ssf DECIMAL(10,2) NOT NULL DEFAULT 0",
  "fee_sdc DECIMAL(10,2) NOT NULL DEFAULT 0",
  "fee_registration DECIMAL(10,2) NOT NULL DEFAULT 0",
  "fee_zimsec DECIMAL(10,2) NOT NULL DEFAULT 0"
];
foreach ($learnerInvoiceColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE fee_invoices ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors for idempotent upgrades.
  }
}

$learnerPaymentColumnsToEnsure = [
  "currency_code ENUM('USD','ZIG','RAND') NOT NULL DEFAULT 'USD'",
  "fx_rate_to_usd DECIMAL(18,8) NOT NULL DEFAULT 1",
  "amount_paid_usd DECIMAL(12,2) NOT NULL DEFAULT 0",
  "fee_component VARCHAR(10) NOT NULL DEFAULT 'GPF'"
];
foreach ($learnerPaymentColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE fee_payments ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors for idempotent upgrades.
  }
}

$pdo->exec("CREATE TABLE IF NOT EXISTS elearning_submissions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  post_id INT NOT NULL,
  learner_id INT NOT NULL,
  response_text TEXT NULL,
  response_link VARCHAR(255) NULL,
  attachment_name VARCHAR(255) NULL,
  attachment_path VARCHAR(255) NULL,
  attachment_type VARCHAR(120) NULL,
  attachment_size INT NULL,
  submitted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  status ENUM('submitted','graded','returned') NOT NULL DEFAULT 'submitted',
  score DECIMAL(6,2) NULL,
  teacher_comment TEXT NULL,
  graded_at DATETIME NULL,
  CONSTRAINT fk_es_post FOREIGN KEY (post_id) REFERENCES elearning_posts(id) ON DELETE CASCADE,
  CONSTRAINT fk_es_learner FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
  UNIQUE KEY uq_es_post_learner (post_id, learner_id),
  INDEX idx_es_post (post_id),
  INDEX idx_es_learner (learner_id)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS library_resources (
  id INT AUTO_INCREMENT PRIMARY KEY,
  teacher_id INT NOT NULL,
  class_id INT NOT NULL,
  subject_id INT NULL,
  document_type VARCHAR(40) NOT NULL DEFAULT 'other',
  title VARCHAR(180) NOT NULL,
  description TEXT NULL,
  original_name VARCHAR(255) NOT NULL,
  file_path VARCHAR(255) NOT NULL,
  mime_type VARCHAR(120) NULL,
  file_size BIGINT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  is_active TINYINT(1) NOT NULL DEFAULT 1,
  CONSTRAINT fk_lr_teacher FOREIGN KEY (teacher_id) REFERENCES users(id) ON DELETE CASCADE,
  CONSTRAINT fk_lr_class FOREIGN KEY (class_id) REFERENCES classes(id) ON DELETE CASCADE,
  CONSTRAINT fk_lr_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE SET NULL,
  INDEX idx_lr_teacher_created (teacher_id, created_at),
  INDEX idx_lr_class (class_id)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS library_access_logs (
  id INT AUTO_INCREMENT PRIMARY KEY,
  resource_id INT NOT NULL,
  learner_id INT NOT NULL,
  action ENUM('view','download') NOT NULL,
  accessed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_lal_resource FOREIGN KEY (resource_id) REFERENCES library_resources(id) ON DELETE CASCADE,
  CONSTRAINT fk_lal_learner FOREIGN KEY (learner_id) REFERENCES learners(id) ON DELETE CASCADE,
  INDEX idx_lal_resource_time (resource_id, accessed_at),
  INDEX idx_lal_learner_time (learner_id, accessed_at)
)");

$pdo->exec("CREATE TABLE IF NOT EXISTS announcements (
  id INT AUTO_INCREMENT PRIMARY KEY,
  sender_id INT NOT NULL,
  sender_role ENUM('admin','teacher') NOT NULL,
  audience ENUM('teacher','learner','parent') NOT NULL,
  target_class_id INT NULL,
  target_learner_id INT NULL,
  title VARCHAR(180) NOT NULL,
  body TEXT NOT NULL,
  status ENUM('published','pending_admin_review','rejected') NOT NULL DEFAULT 'published',
  admin_review_comment TEXT NULL,
  reviewed_by INT NULL,
  reviewed_at DATETIME NULL,
  published_at DATETIME NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  CONSTRAINT fk_ann_sender FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
  CONSTRAINT fk_ann_reviewer FOREIGN KEY (reviewed_by) REFERENCES users(id) ON DELETE SET NULL,
  CONSTRAINT fk_ann_target_class FOREIGN KEY (target_class_id) REFERENCES classes(id) ON DELETE SET NULL,
  CONSTRAINT fk_ann_target_learner FOREIGN KEY (target_learner_id) REFERENCES learners(id) ON DELETE SET NULL,
  INDEX idx_ann_audience_status_created (audience, status, created_at)
)");

$submissionColumnsToEnsure = [
  "attachment_name VARCHAR(255) NULL",
  "attachment_path VARCHAR(255) NULL",
  "attachment_type VARCHAR(120) NULL",
  "attachment_size INT NULL"
];
foreach ($submissionColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE elearning_submissions ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors to keep migration idempotent.
  }
}

$libraryResourceColumnsToEnsure = [
  "document_type VARCHAR(40) NOT NULL DEFAULT 'other'"
];
foreach ($libraryResourceColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE library_resources ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors to keep migration idempotent.
  }
}

$learnerColumnsToEnsure = [
  "birth_certificate_number VARCHAR(80) NULL",
  "student_photo_path VARCHAR(255) NULL",
  "contact_email VARCHAR(150) NULL",
  "password_hash VARCHAR(255) NULL"
];
foreach ($learnerColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE learners ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors to keep migration idempotent.
  }
}

$announcementColumnsToEnsure = [
  "target_class_id INT NULL",
  "target_learner_id INT NULL"
];
foreach ($announcementColumnsToEnsure as $columnSql) {
  try {
    $pdo->exec("ALTER TABLE announcements ADD COLUMN " . $columnSql);
  } catch (PDOException $e) {
    // Ignore duplicate column errors to keep migration idempotent.
  }
}

function get_core_learning_platforms(): array {
  return [
    [
      'name' => 'Learning Passport Zimbabwe',
      'url' => 'https://mopsezw.learningpassport.org/',
      'description' => 'Curriculum-focused learning materials for Zimbabwe learners.'
    ],
    [
      'name' => 'Khan Academy',
      'url' => 'https://www.khanacademy.org/',
      'description' => 'Free lessons, exercises, and videos for many subjects.'
    ],
    [
      'name' => 'Akello Smart Learning',
      'url' => 'https://smartlearning.akello.co/',
      'description' => 'Smart digital learning platform with structured lessons and progress support.'
    ],
    [
      'name' => 'Dzidzo',
      'url' => 'https://www.dzidzo.co.zw/',
      'description' => 'Local education platform for digital learning support.'
    ],
    [
      'name' => 'Stellar Learning',
      'url' => 'https://stellarlearning.app/',
      'description' => 'Digital learning platform for guided lessons and learner progress tracking.'
    ]
  ];
}

function build_subject_library_resources(string $subjectName): array {
  $subject = strtolower(trim($subjectName));
  $resources = [];

  if (strpos($subject, 'math') !== false) {
    $resources[] = ['title' => 'Khan Academy Math Practice', 'url' => 'https://www.khanacademy.org/math', 'type' => 'Practice'];
    $resources[] = ['title' => 'Math is Fun', 'url' => 'https://www.mathsisfun.com/', 'type' => 'Revision'];
  }
  if (strpos($subject, 'english') !== false) {
    $resources[] = ['title' => 'Khan Academy Grammar', 'url' => 'https://www.khanacademy.org/humanities/grammar', 'type' => 'Language'];
    $resources[] = ['title' => 'British Council LearnEnglish', 'url' => 'https://learnenglish.britishcouncil.org/', 'type' => 'Reading + Writing'];
  }
  if (strpos($subject, 'science') !== false) {
    $resources[] = ['title' => 'Khan Academy Science', 'url' => 'https://www.khanacademy.org/science', 'type' => 'Concepts'];
    $resources[] = ['title' => 'National Geographic Education', 'url' => 'https://education.nationalgeographic.org/', 'type' => 'Research'];
  }
  if (strpos($subject, 'history') !== false || strpos($subject, 'geography') !== false || strpos($subject, 'social') !== false) {
    $resources[] = ['title' => 'Khan Academy Humanities', 'url' => 'https://www.khanacademy.org/humanities', 'type' => 'Social Studies'];
  }
  if (strpos($subject, 'computer') !== false || strpos($subject, 'ict') !== false) {
    $resources[] = ['title' => 'Code.org', 'url' => 'https://code.org/', 'type' => 'ICT Skills'];
  }

  if (empty($resources)) {
    $resources[] = ['title' => 'Learning Passport Zimbabwe', 'url' => 'https://mopsezw.learningpassport.org/', 'type' => 'Curriculum'];
    $resources[] = ['title' => 'Khan Academy', 'url' => 'https://www.khanacademy.org/', 'type' => 'General Learning'];
  }

  return $resources;
}

function format_file_size($bytes): string {
  $size = (float)$bytes;
  if ($size <= 0) {
    return '0 B';
  }

  $units = ['B', 'KB', 'MB', 'GB', 'TB'];
  $power = min((int)floor(log($size, 1024)), count($units) - 1);
  $value = $size / pow(1024, $power);
  return number_format($value, $value >= 100 || $power === 0 ? 0 : 1) . ' ' . $units[$power];
}

function get_library_document_types(): array {
  return [
    'schemebook' => 'Schemebook',
    'lesson_plan' => 'Lesson Plans',
    'irbm' => 'IRBM',
    'mark_list' => 'Mark Lists',
    'class_register' => 'Class Register',
    'inventory_book' => 'Inventory Book',
    'other' => 'Other Document'
  ];
}

function report_grade_from_percent(float $percent): array {
  if ($percent >= 80) {
    return ['grade' => 'A', 'status' => 'PASS'];
  }
  if ($percent >= 70) {
    return ['grade' => 'B', 'status' => 'PASS'];
  }
  if ($percent >= 60) {
    return ['grade' => 'C', 'status' => 'PASS'];
  }
  if ($percent >= 50) {
    return ['grade' => 'D', 'status' => 'PASS'];
  }
  if ($percent >= 40) {
    return ['grade' => 'E', 'status' => 'FAIL'];
  }
  return ['grade' => 'U', 'status' => 'FAIL'];
}

$learnerId = (int)($_SESSION['user_id'] ?? 0);
$alerts = [];

$learnerStmt = $pdo->prepare("SELECT l.id, l.reg_number, l.full_name, l.surname, l.christian_names, l.class_id, c.name AS class_name,
  l.date_of_admission, l.date_of_birth_info, l.birth_certificate_number, l.religious_denomination, l.gender,
    l.guardian_name, l.guardian_national_id, l.guardian_address, l.guardian_phone,
    l.student_photo_path, l.contact_email, l.password_hash
  FROM learners l
  JOIN classes c ON c.id = l.class_id
  WHERE l.id = ?
  LIMIT 1");
$learnerStmt->execute([$learnerId]);
$activeLearner = $learnerStmt->fetch();

if (!$activeLearner) {
  logout_user();
  header('Location: index.php');
  exit;
}

if (isset($_POST['save_learner_profile'])) {
  $contactEmail = trim((string)($_POST['contact_email'] ?? ''));

  if ($contactEmail !== '' && !filter_var($contactEmail, FILTER_VALIDATE_EMAIL)) {
    $alerts[] = ['type' => 'error', 'text' => 'Please provide a valid contact email address.'];
  } else {
    $updateProfileStmt = $pdo->prepare("UPDATE learners SET contact_email = ? WHERE id = ? LIMIT 1");
    $updateProfileStmt->execute([$contactEmail === '' ? null : $contactEmail, $learnerId]);
    $activeLearner['contact_email'] = $contactEmail;
    $alerts[] = ['type' => 'success', 'text' => 'Learner profile updated successfully.'];
  }
}

if (isset($_POST['change_learner_password'])) {
  $currentPassword = (string)($_POST['current_password'] ?? '');
  $newPassword = (string)($_POST['new_password'] ?? '');
  $confirmPassword = (string)($_POST['confirm_password'] ?? '');
  $storedLearnerHash = trim((string)($activeLearner['password_hash'] ?? ''));
  $storedSurname = strtolower(trim((string)($activeLearner['surname'] ?? '')));
  $currentPasswordValid = false;

  if ($currentPassword === '' || $newPassword === '' || $confirmPassword === '') {
    $alerts[] = ['type' => 'error', 'text' => 'Current password, new password, and confirmation are required.'];
  } else {
    if ($storedLearnerHash !== '') {
      $currentPasswordValid = password_verify($currentPassword, $storedLearnerHash);
    } else {
      $currentPasswordValid = $storedSurname !== '' && strtolower($currentPassword) === $storedSurname;
    }

    if (!$currentPasswordValid) {
      $alerts[] = ['type' => 'error', 'text' => 'Current password is incorrect.'];
    } elseif (!mavela_password_is_strong($newPassword)) {
      $alerts[] = ['type' => 'error', 'text' => 'Use at least 10 characters with uppercase, lowercase, and a number.'];
    } elseif ($newPassword !== $confirmPassword) {
      $alerts[] = ['type' => 'error', 'text' => 'New password and confirmation do not match.'];
    } else {
      $newHash = password_hash($newPassword, PASSWORD_DEFAULT);
      $updatePasswordStmt = $pdo->prepare("UPDATE learners SET password_hash = ? WHERE id = ? LIMIT 1");
      $updatePasswordStmt->execute([$newHash, $learnerId]);
      $activeLearner['password_hash'] = $newHash;
      $alerts[] = ['type' => 'success', 'text' => 'Learner password changed successfully.'];
    }
  }
}

$learnerDisplayName = trim((string)($activeLearner['full_name'] ?? ''));
if ($learnerDisplayName === '') {
  $learnerDisplayName = trim((string)($activeLearner['christian_names'] ?? '') . ' ' . (string)($activeLearner['surname'] ?? ''));
}
if ($learnerDisplayName === '') {
  $learnerDisplayName = 'Learner';
}

$learnerPhotoPath = trim((string)($activeLearner['student_photo_path'] ?? ''));
$learnerPhotoExists = $learnerPhotoPath !== '' && is_file(__DIR__ . '/' . ltrim($learnerPhotoPath, '/\\'));
$learnerInitials = '';
foreach (preg_split('/\s+/', $learnerDisplayName) as $namePart) {
  if ($namePart !== '') {
    $learnerInitials .= strtoupper(substr($namePart, 0, 1));
  }
  if (strlen($learnerInitials) >= 2) {
    break;
  }
}
if ($learnerInitials === '') {
  $learnerInitials = 'L';
}

$defaultSchoolName = 'MAVELA GOVERNMENT HIGH SCHOOL';
$defaultSchoolAddress = 'P.O.Box 12 Tsholotsho';
$defaultSchoolContact = 'Phone: +263 77 000 0000 | Email: admin@mavelahigh.org';
$defaultSchoolMotto = 'Imfundo Yinsika Yesizwe';
$defaultSchoolLogoPath = 'Uploads/Mavela_Logo.png';

$normalizeHexColor = static function (string $raw, string $fallback): string {
  $value = strtoupper(trim($raw));
  if (preg_match('/^#[0-9A-F]{6}$/', $value)) {
    return $value;
  }
  return strtoupper($fallback);
};

$schoolName = trim(get_app_setting($pdo, 'school_name', $defaultSchoolName));
$schoolAddress = trim(get_app_setting($pdo, 'school_address', $defaultSchoolAddress));
$schoolContact = trim(get_app_setting($pdo, 'school_contact', $defaultSchoolContact));
$schoolMotto = trim(get_app_setting($pdo, 'school_motto', $defaultSchoolMotto));
$schoolLogoPath = trim(get_app_setting($pdo, 'school_logo_path', $defaultSchoolLogoPath));

if ($schoolName === '') {
  $schoolName = $defaultSchoolName;
}
if ($schoolAddress === '') {
  $schoolAddress = $defaultSchoolAddress;
}
if ($schoolContact === '') {
  $schoolContact = $defaultSchoolContact;
}
if ($schoolMotto === '') {
  $schoolMotto = $defaultSchoolMotto;
}
if ($schoolLogoPath === '') {
  $schoolLogoPath = $defaultSchoolLogoPath;
}

$resolvedSchoolLogoPath = $schoolLogoPath;
$resolvedSchoolLogoAbsolute = __DIR__ . '/' . ltrim($resolvedSchoolLogoPath, '/\\');
if (!is_file($resolvedSchoolLogoAbsolute)) {
  $fallbackLogoAbsolute = __DIR__ . '/' . ltrim($defaultSchoolLogoPath, '/\\');
  if (is_file($fallbackLogoAbsolute)) {
    $resolvedSchoolLogoPath = $defaultSchoolLogoPath;
    $resolvedSchoolLogoAbsolute = $fallbackLogoAbsolute;
  } else {
    $resolvedSchoolLogoPath = '';
    $resolvedSchoolLogoAbsolute = '';
  }
}

$paletteBgCream = $normalizeHexColor(get_app_setting($pdo, 'palette_bg_cream', '#f6efe0'), '#f6efe0');
$paletteBgSand = $normalizeHexColor(get_app_setting($pdo, 'palette_bg_sand', '#eadcc0'), '#eadcc0');
$paletteInk = $normalizeHexColor(get_app_setting($pdo, 'palette_ink', '#0a2f63'), '#0a2f63');
$paletteMuted = $normalizeHexColor(get_app_setting($pdo, 'palette_muted', '#4f5f7a'), '#4f5f7a');
$paletteBrandNavy = $normalizeHexColor(get_app_setting($pdo, 'palette_brand_navy', '#0a3b79'), '#0a3b79');
$paletteBrandNavyDeep = $normalizeHexColor(get_app_setting($pdo, 'palette_brand_navy_deep', '#082e60'), '#082e60');

$schoolLogoDataUri = '';
$learnerReportLogoPath = $resolvedSchoolLogoAbsolute;
if ($learnerReportLogoPath !== '' && is_file($learnerReportLogoPath)) {
  $learnerReportLogoBytes = @file_get_contents($learnerReportLogoPath);
  if ($learnerReportLogoBytes !== false) {
    $schoolLogoDataUri = 'data:image/png;base64,' . base64_encode($learnerReportLogoBytes);
  }
}

if (isset($_GET['library_action']) && isset($_GET['resource_id'])) {
  $libraryAction = trim((string)$_GET['library_action']);
  $resourceId = (int)($_GET['resource_id'] ?? 0);

  if (!in_array($libraryAction, ['view', 'download'], true) || $resourceId <= 0) {
    $alerts[] = ['type' => 'error', 'text' => 'Invalid library action.'];
  } else {
    $resourceStmt = $pdo->prepare("SELECT lr.id, lr.title, lr.original_name, lr.file_path, lr.mime_type
      FROM library_resources lr
      WHERE lr.id = ? AND lr.class_id = ? AND lr.is_active = 1
      LIMIT 1");
    $resourceStmt->execute([$resourceId, (int)$activeLearner['class_id']]);
    $resource = $resourceStmt->fetch();

    if (!$resource) {
      $alerts[] = ['type' => 'error', 'text' => 'Library resource not found for your class.'];
    } else {
      $absolutePath = __DIR__ . '/' . ltrim((string)$resource['file_path'], '/\\');
      if (!is_file($absolutePath)) {
        $alerts[] = ['type' => 'error', 'text' => 'Library file is missing on server. Please notify your teacher.'];
      } else {
        $logStmt = $pdo->prepare("INSERT INTO library_access_logs (resource_id, learner_id, action) VALUES (?, ?, ?)");
        $logStmt->execute([$resourceId, $learnerId, $libraryAction]);

        $mimeType = (string)($resource['mime_type'] ?: 'application/octet-stream');
        $downloadName = (string)($resource['original_name'] ?: basename($absolutePath));

        if (!headers_sent()) {
          header('Content-Type: ' . $mimeType);
          header('Content-Length: ' . (string)filesize($absolutePath));
          header('Content-Disposition: ' . ($libraryAction === 'download' ? 'attachment' : 'inline') . '; filename="' . rawurlencode($downloadName) . '"');
          header('X-Content-Type-Options: nosniff');
          readfile($absolutePath);
          exit;
        }
      }
    }
  }
}

$coreLearningPlatforms = get_core_learning_platforms();
$learnerLibraryBySubject = [];
$libraryDocumentTypes = get_library_document_types();
$classSubjectStmt = $pdo->prepare("SELECT name FROM subjects WHERE class_id = ? ORDER BY name");
$classSubjectStmt->execute([(int)$activeLearner['class_id']]);
foreach ($classSubjectStmt->fetchAll() as $subjectRow) {
  $subjectName = (string)$subjectRow['name'];
  if (!isset($learnerLibraryBySubject[$subjectName])) {
    $learnerLibraryBySubject[$subjectName] = build_subject_library_resources($subjectName);
  }
}
if (!empty($learnerLibraryBySubject)) {
  ksort($learnerLibraryBySubject);
}

if (isset($_POST['submit_hub_response'])) {
  $postId = (int)($_POST['post_id'] ?? 0);
  $responseText = trim($_POST['response_text'] ?? '');
  $responseLink = trim($_POST['response_link'] ?? '');
  $attachmentName = null;
  $attachmentPath = null;
  $attachmentType = null;
  $attachmentSize = null;

  if (isset($_FILES['response_file']) && (int)($_FILES['response_file']['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_NO_FILE) {
    $fileError = (int)($_FILES['response_file']['error'] ?? UPLOAD_ERR_NO_FILE);
    if ($fileError !== UPLOAD_ERR_OK) {
      $alerts[] = ['type' => 'error', 'text' => 'File upload failed. Please try again.'];
    } else {
      $originalName = (string)($_FILES['response_file']['name'] ?? '');
      $tmpName = (string)($_FILES['response_file']['tmp_name'] ?? '');
      $fileSize = (int)($_FILES['response_file']['size'] ?? 0);
      $extension = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
      $allowedExtensions = ['pdf', 'doc', 'docx', 'jpg', 'jpeg', 'png', 'gif', 'webp'];
      $allowedMimeTypes = [
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'image/jpeg',
        'image/png',
        'image/gif',
        'image/webp'
      ];

      if ($originalName === '' || $tmpName === '') {
        $alerts[] = ['type' => 'error', 'text' => 'Invalid file upload payload.'];
      } elseif (!in_array($extension, $allowedExtensions, true)) {
        $alerts[] = ['type' => 'error', 'text' => 'Allowed file types: PDF, Word (.doc/.docx), JPG, PNG, GIF, WEBP.'];
      } elseif ($fileSize <= 0 || $fileSize > 10 * 1024 * 1024) {
        $alerts[] = ['type' => 'error', 'text' => 'File must be between 1 byte and 10 MB.'];
      } else {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $detectedMime = $finfo ? (string)finfo_file($finfo, $tmpName) : '';
        if ($finfo) {
          finfo_close($finfo);
        }

        if ($detectedMime !== '' && !in_array($detectedMime, $allowedMimeTypes, true)) {
          $alerts[] = ['type' => 'error', 'text' => 'Uploaded file type is not permitted.'];
        } else {
          $safeBase = preg_replace('/[^A-Za-z0-9._-]/', '_', pathinfo($originalName, PATHINFO_FILENAME));
          $safeBase = trim((string)$safeBase, '._-');
          if ($safeBase === '') {
            $safeBase = 'submission';
          }

          $uploadDirAbsolute = __DIR__ . '/Uploads/submissions';
          if (!is_dir($uploadDirAbsolute) && !mkdir($uploadDirAbsolute, 0775, true)) {
            $alerts[] = ['type' => 'error', 'text' => 'Could not prepare upload folder on server.'];
          } else {
            $newFileName = 'learner_' . $learnerId . '_post_' . $postId . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(3)) . '_' . $safeBase . '.' . $extension;
            $destinationAbsolute = $uploadDirAbsolute . '/' . $newFileName;

            if (!move_uploaded_file($tmpName, $destinationAbsolute)) {
              $alerts[] = ['type' => 'error', 'text' => 'Could not save uploaded file.'];
            } else {
              $attachmentName = $originalName;
              $attachmentPath = 'Uploads/submissions/' . $newFileName;
              $attachmentType = $detectedMime !== '' ? $detectedMime : null;
              $attachmentSize = $fileSize;
            }
          }
        }
      }
    }
  }

  if ($postId <= 0) {
    $alerts[] = ['type' => 'error', 'text' => 'Invalid e-learning item selected.'];
  } elseif ($responseText === '' && $responseLink === '' && $attachmentPath === null) {
    $alerts[] = ['type' => 'error', 'text' => 'Type a response, provide a link, or upload a file.'];
  } else {
    $postAccessStmt = $pdo->prepare("SELECT id FROM elearning_posts WHERE id = ? AND class_id = ? LIMIT 1");
    $postAccessStmt->execute([$postId, (int)$activeLearner['class_id']]);

    if (!$postAccessStmt->fetch()) {
      $alerts[] = ['type' => 'error', 'text' => 'You can only submit work for posts assigned to your class.'];
    } else {
      $submissionStmt = $pdo->prepare("INSERT INTO elearning_submissions
        (post_id, learner_id, response_text, response_link, attachment_name, attachment_path, attachment_type, attachment_size, status, submitted_at)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'submitted', NOW())
        ON DUPLICATE KEY UPDATE
          response_text = VALUES(response_text),
          response_link = VALUES(response_link),
          attachment_name = COALESCE(VALUES(attachment_name), attachment_name),
          attachment_path = COALESCE(VALUES(attachment_path), attachment_path),
          attachment_type = COALESCE(VALUES(attachment_type), attachment_type),
          attachment_size = COALESCE(VALUES(attachment_size), attachment_size),
          status = 'submitted',
          submitted_at = NOW()");
      $submissionStmt->execute([
        $postId,
        $learnerId,
        $responseText,
        $responseLink === '' ? null : $responseLink,
        $attachmentName,
        $attachmentPath,
        $attachmentType,
        $attachmentSize
      ]);
      $alerts[] = ['type' => 'success', 'text' => 'Submission sent to your teacher for marking.'];
    }
  }
}

$postsStmt = $pdo->prepare("SELECT ep.id, ep.title, ep.content_type, ep.instructions, ep.resource_url, ep.due_date, ep.created_at,
    s.name AS subject_name, u.name AS teacher_name,
  es.id AS submission_id, es.response_text, es.response_link, es.attachment_name, es.attachment_path, es.attachment_type, es.attachment_size,
  es.status, es.score, es.teacher_comment, es.submitted_at, es.graded_at
  FROM elearning_posts ep
  LEFT JOIN subjects s ON s.id = ep.subject_id
  JOIN users u ON u.id = ep.teacher_id
  LEFT JOIN elearning_submissions es ON es.post_id = ep.id AND es.learner_id = ?
  WHERE ep.class_id = ?
  ORDER BY ep.created_at DESC
  LIMIT 120");
$postsStmt->execute([$learnerId, (int)$activeLearner['class_id']]);
$hubItems = $postsStmt->fetchAll();

$libraryUploadsStmt = $pdo->prepare("SELECT lr.id, lr.title, lr.description, lr.original_name, lr.file_path, lr.mime_type, lr.file_size,
  lr.document_type, lr.created_at, s.name AS subject_name, u.name AS teacher_name
  FROM library_resources lr
  LEFT JOIN subjects s ON s.id = lr.subject_id
  JOIN users u ON u.id = lr.teacher_id
  WHERE lr.class_id = ? AND lr.is_active = 1
  ORDER BY lr.created_at DESC
  LIMIT 250");
$libraryUploadsStmt->execute([(int)$activeLearner['class_id']]);
$libraryUploads = $libraryUploadsStmt->fetchAll();

$learnerAccessCountStmt = $pdo->prepare("SELECT COUNT(*) FROM library_access_logs WHERE learner_id = ?");
$learnerAccessCountStmt->execute([$learnerId]);
$learnerLibraryAccessCount = (int)$learnerAccessCountStmt->fetchColumn();

$submittedCount = 0;
$feedbackCount = 0;
foreach ($hubItems as $item) {
  if (!empty($item['submission_id'])) {
    $submittedCount++;
  }
  if (in_array($item['status'], ['graded', 'returned'], true)) {
    $feedbackCount++;
  }
}

$reportCardExamOptions = [
  'end_of_term_exam_term1' => 'End of Term 1',
  'midyear_exam' => 'Midyear',
  'end_of_year_exam' => 'End of Year'
];

$learnerMajorMarksStmt = $pdo->prepare("SELECT lm.learner_id, lm.assessment_type, lm.term_name, lm.assessment_date,
    lm.score, lm.max_score, s.name AS subject_name
  FROM learner_marks lm
  JOIN subjects s ON s.id = lm.subject_id
  WHERE lm.learner_id = ?
    AND lm.class_id = ?
    AND lm.assessment_type IN ('end_of_term_exam', 'midyear_exam', 'end_of_year_exam')
  ORDER BY s.name, lm.assessment_date");
$learnerMajorMarksStmt->execute([$learnerId, (int)$activeLearner['class_id']]);
$learnerMajorMarks = $learnerMajorMarksStmt->fetchAll();

$classMajorMarksStmt = $pdo->prepare("SELECT lm.learner_id, lm.assessment_type, lm.term_name, lm.score, lm.max_score
  FROM learner_marks lm
  WHERE lm.class_id = ?
    AND lm.assessment_type IN ('end_of_term_exam', 'midyear_exam', 'end_of_year_exam')");
$classMajorMarksStmt->execute([(int)$activeLearner['class_id']]);
$classMajorMarks = $classMajorMarksStmt->fetchAll();

$reportCardDataset = [];
foreach ($reportCardExamOptions as $examKey => $examLabel) {
  $subjectBuckets = [];

  foreach ($learnerMajorMarks as $markRow) {
    $type = (string)$markRow['assessment_type'];
    $termName = strtolower(trim((string)$markRow['term_name']));
    $isMatch = false;

    if ($examKey === 'end_of_term_exam_term1') {
      $isMatch = $type === 'end_of_term_exam' && $termName === 'term 1';
    } elseif ($examKey === 'midyear_exam') {
      $isMatch = $type === 'midyear_exam';
    } elseif ($examKey === 'end_of_year_exam') {
      $isMatch = $type === 'end_of_year_exam';
    }

    if (!$isMatch) {
      continue;
    }

    $subjectName = (string)$markRow['subject_name'];
    if (!isset($subjectBuckets[$subjectName])) {
      $subjectBuckets[$subjectName] = ['obtained' => 0.0, 'total' => 0.0];
    }

    $subjectBuckets[$subjectName]['obtained'] += (float)$markRow['score'];
    $subjectBuckets[$subjectName]['total'] += (float)$markRow['max_score'];
  }

  ksort($subjectBuckets);
  $subjectRows = [];
  $totalObtained = 0.0;
  $totalPossible = 0.0;
  foreach ($subjectBuckets as $subjectName => $totals) {
    $obtained = (float)$totals['obtained'];
    $possible = (float)$totals['total'];
    $percent = $possible > 0 ? ($obtained / $possible) * 100 : 0.0;
    $gradeInfo = report_grade_from_percent($percent);
    $subjectRows[] = [
      'subject' => $subjectName,
      'obtained' => round($obtained, 1),
      'total' => round($possible, 1),
      'percent' => round($percent, 1),
      'grade' => $gradeInfo['grade'],
      'status' => $gradeInfo['status']
    ];
    $totalObtained += $obtained;
    $totalPossible += $possible;
  }

  $overallPercent = $totalPossible > 0 ? ($totalObtained / $totalPossible) * 100 : 0.0;
  $overallGrade = report_grade_from_percent($overallPercent);

  $classTotalsByLearner = [];
  foreach ($classMajorMarks as $classRow) {
    $type = (string)$classRow['assessment_type'];
    $termName = strtolower(trim((string)$classRow['term_name']));
    $isMatch = false;

    if ($examKey === 'end_of_term_exam_term1') {
      $isMatch = $type === 'end_of_term_exam' && $termName === 'term 1';
    } elseif ($examKey === 'midyear_exam') {
      $isMatch = $type === 'midyear_exam';
    } elseif ($examKey === 'end_of_year_exam') {
      $isMatch = $type === 'end_of_year_exam';
    }

    if (!$isMatch) {
      continue;
    }

    $rowLearnerId = (int)$classRow['learner_id'];
    if (!isset($classTotalsByLearner[$rowLearnerId])) {
      $classTotalsByLearner[$rowLearnerId] = ['obtained' => 0.0, 'total' => 0.0];
    }
    $classTotalsByLearner[$rowLearnerId]['obtained'] += (float)$classRow['score'];
    $classTotalsByLearner[$rowLearnerId]['total'] += (float)$classRow['max_score'];
  }

  $classPercents = [];
  foreach ($classTotalsByLearner as $rowLearnerId => $totals) {
    $possible = (float)$totals['total'];
    $percent = $possible > 0 ? ((float)$totals['obtained'] / $possible) * 100 : 0.0;
    $classPercents[] = ['learner_id' => (int)$rowLearnerId, 'percent' => $percent];
  }

  usort($classPercents, static function (array $a, array $b): int {
    return $b['percent'] <=> $a['percent'];
  });

  $classStrength = count($classPercents);
  $classAverage = 0.0;
  $classMax = 0.0;
  $classMin = 0.0;
  $position = '-';
  if ($classStrength > 0) {
    $sum = 0.0;
    foreach ($classPercents as $idx => $row) {
      $sum += (float)$row['percent'];
      if ((int)$row['learner_id'] === $learnerId && $position === '-') {
        $position = (string)($idx + 1);
      }
    }
    $classAverage = $sum / $classStrength;
    $classMax = (float)$classPercents[0]['percent'];
    $classMin = (float)$classPercents[$classStrength - 1]['percent'];
  }

  $reportCardDataset[$examKey] = [
    'exam_label' => $examLabel,
    'subject_rows' => $subjectRows,
    'total_obtained' => round($totalObtained, 1),
    'total_possible' => round($totalPossible, 1),
    'overall_percent' => round($overallPercent, 1),
    'overall_grade' => $overallGrade['grade'],
    'overall_status' => $overallGrade['status'],
    'class_strength' => $classStrength,
    'class_average' => round($classAverage, 1),
    'class_max' => round($classMax, 1),
    'class_min' => round($classMin, 1),
    'position' => $position
  ];
}

$learnerInvoicesStmt = $pdo->prepare("SELECT fi.id, fi.invoice_number, fi.description, fi.amount_due,
  fi.fee_gpf, fi.fee_ssf, fi.fee_sdc, fi.fee_registration, fi.fee_zimsec, fi.due_date,
  fi.status, fi.created_at, COALESCE(SUM(COALESCE(fp.amount_paid_usd, fp.amount_paid)), 0) AS paid_total
  FROM fee_invoices fi
  LEFT JOIN fee_payments fp ON fp.invoice_id = fi.id
  WHERE fi.learner_id = ?
  GROUP BY fi.id
  ORDER BY fi.created_at DESC");
$learnerInvoicesStmt->execute([$learnerId]);
$learnerInvoices = $learnerInvoicesStmt->fetchAll();

$learnerPaymentsStmt = $pdo->prepare("SELECT fp.id, fp.invoice_id, fp.amount_paid, fp.currency_code, fp.fx_rate_to_usd, fp.amount_paid_usd,
  fp.fee_component,
  fp.payment_date, fp.payment_method, fp.reference_no, fp.note, fp.created_at, fi.invoice_number
  FROM fee_payments fp
  LEFT JOIN fee_invoices fi ON fi.id = fp.invoice_id
  WHERE fp.learner_id = ?
  ORDER BY fp.payment_date DESC, fp.created_at DESC");
$learnerPaymentsStmt->execute([$learnerId]);
$learnerPayments = $learnerPaymentsStmt->fetchAll();

$ledgerEntries = [];
foreach ($learnerInvoices as $invoice) {
  $ledgerEntries[] = [
    'entry_type' => 'invoice',
    'entry_date' => (string)($invoice['created_at'] ?: date('Y-m-d H:i:s')),
    'description' => 'Invoice ' . (string)$invoice['invoice_number'] . ' - ' . (string)$invoice['description'],
    'debit' => (float)$invoice['amount_due'],
    'credit' => 0.0,
    'reference' => (string)$invoice['invoice_number']
  ];
}

// Consolidate component-split rows (GPF/SSF/SDC) into one ledger payment line.
$paymentLedgerGroups = [];
foreach ($learnerPayments as $payment) {
  $creditUsd = (float)($payment['amount_paid_usd'] ?? 0);
  if ($creditUsd <= 0) {
    $creditUsd = (float)$payment['amount_paid'];
  }

  $entryDate = (string)($payment['payment_date'] ?: $payment['created_at'] ?: date('Y-m-d H:i:s'));
  $reference = (string)($payment['reference_no'] ?: ($payment['invoice_number'] ?: '-'));
  $invoiceNo = (string)($payment['invoice_number'] ?: '-');
  $method = strtoupper(str_replace('_', ' ', (string)$payment['payment_method']));
  $currency = strtoupper((string)($payment['currency_code'] ?? 'USD'));
  $paymentId = (int)($payment['id'] ?? 0);
  $groupKey = $reference !== ''
    ? implode('|', ['R', $entryDate, $reference, $invoiceNo, $method, $currency])
    : implode('|', ['I', (string)$paymentId]);

  if (!isset($paymentLedgerGroups[$groupKey])) {
    $paymentLedgerGroups[$groupKey] = [
      'entry_date' => $entryDate,
      'method' => $method,
      'currency' => $currency,
      'invoice_number' => $invoiceNo,
      'reference' => $reference,
      'credit' => 0.0,
      'components' => []
    ];
  }

  $component = strtoupper((string)($payment['fee_component'] ?? 'GENERAL'));
  if ($component !== '' && !in_array($component, $paymentLedgerGroups[$groupKey]['components'], true)) {
    $paymentLedgerGroups[$groupKey]['components'][] = $component;
  }
  $paymentLedgerGroups[$groupKey]['credit'] += $creditUsd;
}

foreach ($paymentLedgerGroups as $group) {
  $componentLabel = !empty($group['components']) ? implode('/', $group['components']) : 'GENERAL';
  $ledgerEntries[] = [
    'entry_type' => 'payment',
    'entry_date' => (string)$group['entry_date'],
    'description' => 'Payment received - ' . $componentLabel
      . ' / ' . (string)$group['method']
      . ' (' . (string)$group['currency'] . ')',
    'debit' => 0.0,
    'credit' => (float)$group['credit'],
    'reference' => (string)$group['reference']
  ];
}

usort($ledgerEntries, static function (array $a, array $b): int {
  return strcmp((string)$a['entry_date'], (string)$b['entry_date']);
});

$runningBalance = 0.0;
foreach ($ledgerEntries as $idx => $entry) {
  $runningBalance += (float)$entry['debit'];
  $runningBalance -= (float)$entry['credit'];
  $ledgerEntries[$idx]['balance'] = round($runningBalance, 2);
}

$learnerTotalInvoiced = 0.0;
$learnerTotalPaid = 0.0;
foreach ($learnerInvoices as $invoice) {
  $learnerTotalInvoiced += (float)$invoice['amount_due'];
}
foreach ($learnerPayments as $payment) {
  $paidUsd = (float)($payment['amount_paid_usd'] ?? 0);
  if ($paidUsd <= 0) {
    $paidUsd = (float)$payment['amount_paid'];
  }
  $learnerTotalPaid += $paidUsd;
}
$learnerOutstandingBalance = max(0, $learnerTotalInvoiced - $learnerTotalPaid);

$learnerAnnouncementsStmt = $pdo->prepare("SELECT a.id, a.title, a.body, a.created_at, a.published_at,
    a.sender_role, a.target_class_id, a.target_learner_id,
    u.name AS sender_name
  FROM announcements a
  JOIN users u ON u.id = a.sender_id
  WHERE a.audience = 'learner'
    AND a.status = 'published'
    AND (a.target_class_id IS NULL OR a.target_class_id = ?)
    AND (a.target_learner_id IS NULL OR a.target_learner_id = ?)
  ORDER BY COALESCE(a.published_at, a.created_at) DESC
  LIMIT 150");
$learnerAnnouncementsStmt->execute([(int)$activeLearner['class_id'], $learnerId]);
$learnerAnnouncements = $learnerAnnouncementsStmt->fetchAll();
$learnerAnnouncementCount = count($learnerAnnouncements);
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Learner Desk - <?= h($schoolName) ?></title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Plus+Jakarta+Sans:wght@500;600;700;800&display=swap" rel="stylesheet">
  <style>
    :root {
      --bg-cream: <?= h($paletteBgCream) ?>;
      --bg-sand: <?= h($paletteBgSand) ?>;
      --ink: <?= h($paletteInk) ?>;
      --muted: <?= h($paletteMuted) ?>;
      --brand-navy: <?= h($paletteBrandNavy) ?>;
      --brand-navy-deep: <?= h($paletteBrandNavyDeep) ?>;
      --line: #d8d4c9;
      --danger: #9f1239;
      --success: #166534;
      --panel: #ffffff;
      --shadow: 0 18px 45px rgba(10, 47, 99, 0.14);
    }
    * { box-sizing: border-box; }
    body {
      margin: 0;
      min-height: 100vh;
      font-family: 'Outfit', sans-serif;
      color: var(--ink);
      background:
        radial-gradient(circle at 15% 18%, rgba(248, 180, 0, 0.2) 0, rgba(248, 180, 0, 0) 40%),
        radial-gradient(circle at 80% 20%, rgba(124, 201, 255, 0.24) 0, rgba(124, 201, 255, 0) 38%),
        linear-gradient(170deg, var(--bg-cream) 0%, var(--bg-sand) 100%);
      padding: 14px;
    }
    .shell { max-width: 1280px; margin: 0 auto; display: grid; gap: 12px; }
    .header {
      border-radius: 16px;
      padding: 16px;
      background: linear-gradient(122deg, var(--brand-navy-deep), var(--brand-navy) 55%, var(--patch-sky));
      color: #fff;
      box-shadow: var(--shadow);
      display: flex;
      justify-content: space-between;
      gap: 10px;
      flex-wrap: wrap;
      align-items: center;
    }
    .header h1 { margin: 0; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 1.3rem; }
    .header-brand { display:flex; align-items:center; gap:10px; }
    .header-logo { width:52px; height:52px; border-radius:12px; object-fit:contain; background:#fff; border:1px solid rgba(255,255,255,0.4); padding:4px; }
    .chip { border: 1px solid #f8b400; background: #ffd447; color: #12386d; border-radius: 999px; padding: 8px 12px; text-decoration: none; font-weight: 700; }
    .desk-layout { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 12px; }
    .sidebar { background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 10px; }
    .side-btn { width: 100%; text-align: left; border: 1px solid #d8def0; background: #f8fbff; color: #12386d; border-radius: 10px; padding: 8px 10px; margin-bottom: 6px; font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; font-size: 0.84rem; cursor: pointer; transition: background 160ms ease, color 160ms ease, transform 120ms ease; box-shadow: 0 0 0 1px rgba(124, 201, 255, 0.45), 0 0 0 3px rgba(255, 212, 71, 0.22); }
    .side-btn.active { background: linear-gradient(115deg, var(--brand-navy), var(--brand-navy-deep)); color: #fff; }
    .side-btn:hover { background: #7cc9ff; color: #083b78; transform: translateY(-1px); box-shadow: 0 0 0 2px rgba(124, 201, 255, 0.85), 0 0 0 5px rgba(255, 212, 71, 0.5); }
    .main { display: grid; gap: 10px; }
    .view {
      display: none !important;
      visibility: hidden;
      height: 0;
      overflow: hidden;
    }
    .view.active {
      display: grid !important;
      gap: 12px;
      visibility: visible;
      height: auto;
      overflow: visible;
    }
    .toggle-view { display: none; }
    .toggle-view.active { display: grid; gap: 10px; }
    .panel { background: #fff; border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 14px; }
    .stats { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 10px; }
    .stat { background: #fff; border: 1px solid #d8def0; border-radius: 12px; padding: 12px; }
    .stat small { color: var(--muted); display: block; }
    .stat strong { font-size: 1.4rem; }
    .field { display: grid; gap: 6px; margin-bottom: 10px; }
    label { font-size: 0.82rem; text-transform: uppercase; color: var(--muted); font-weight: 700; }
    input, textarea { border: 1px solid #c8c5bc; border-radius: 10px; padding: 10px; font-family: inherit; }
    textarea { min-height: 84px; resize: vertical; }
    .btn { border: 0; border-radius: 10px; padding: 10px 12px; font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 700; cursor: pointer; background: linear-gradient(115deg, #ffd447, #f8b400); color: #17253f; transition: background 160ms ease, color 160ms ease, transform 120ms ease; box-shadow: 0 0 0 2px rgba(124, 201, 255, 0.55), 0 0 0 4px rgba(255, 212, 71, 0.35); }
    .btn:hover { background: #7cc9ff; color: #083b78; transform: translateY(-1px); box-shadow: 0 0 0 2px rgba(124, 201, 255, 0.85), 0 0 0 5px rgba(255, 212, 71, 0.55); }
    .alert { padding: 9px 10px; border-radius: 9px; border: 1px solid; font-weight: 600; margin-bottom: 8px; }
    .alert.success { background: #ecfdf3; color: var(--success); border-color: #86efac; }
    .alert.error { background: #fff1f2; color: var(--danger); border-color: #fda4af; }
    .ledger-balance-banner {
      margin: 2px 0 10px;
      padding: 12px 14px;
      border-radius: 12px;
      border: 1px solid #fecaca;
      background: linear-gradient(120deg, #fff1f2 0%, #ffe4e6 100%);
      color: #991b1b;
      font-weight: 900;
      font-size: 1.15rem;
      letter-spacing: 0.2px;
    }
    .ledger-stream { display: grid; gap: 10px; }
    .ledger-row {
      border: 1px solid #d8def0;
      border-radius: 12px;
      background: linear-gradient(135deg, #ffffff 0%, #f8fbff 100%);
      padding: 10px;
      display: grid;
      gap: 8px;
      box-shadow: 0 4px 14px rgba(8, 34, 78, 0.05);
    }
    .ledger-row-head { display: flex; justify-content: space-between; gap: 8px; flex-wrap: wrap; align-items: center; }
    .ledger-row-date { font-weight: 800; color: #12386d; font-size: 0.9rem; }
    .ledger-row-ref {
      border: 1px solid #bfdbfe;
      background: #eff6ff;
      color: #1d4ed8;
      border-radius: 999px;
      padding: 3px 8px;
      font-size: 0.74rem;
      font-weight: 700;
    }
    .ledger-row-desc { color: #334155; font-size: 0.9rem; }
    .ledger-money-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; }
    .ledger-money-card { border: 1px solid #e2e8f0; background: #ffffff; border-radius: 10px; padding: 8px; }
    .ledger-money-card small {
      display: block;
      color: #64748b;
      font-size: 0.74rem;
      margin-bottom: 4px;
      text-transform: uppercase;
      letter-spacing: 0.4px;
      font-weight: 700;
    }
    .ledger-money-card strong { font-size: 0.95rem; }
    .ledger-money-card.debit strong { color: #b45309; }
    .ledger-money-card.credit strong { color: #065f46; }
    .ledger-money-card.balance strong { color: #991b1b; font-weight: 900; }
    .item { border: 1px solid #d8def0; border-radius: 12px; padding: 11px; margin-bottom: 10px; background: #f9fcff; }
    .item h3 { margin: 0 0 6px; }
    .muted { color: var(--muted); font-size: 0.9rem; }
    .badge { display: inline-block; border: 1px solid #bfd4f4; background: #eaf0fb; color: #1a4a8a; border-radius: 999px; padding: 3px 8px; font-weight: 700; font-size: 0.77rem; }

    .library-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 9px; margin-top: 8px; }
    .resource-card { border: 1px solid #d8def0; border-radius: 12px; background: #f8fbff; padding: 10px; }
    .resource-card h3 { margin: 0 0 8px; color: #0f3d7e; font-size: 1rem; }
    .resource-list { margin: 0; padding-left: 16px; display: grid; gap: 6px; }
    .resource-list li { color: #1f3359; font-size: 0.9rem; }
    .platform-links { margin: 0; padding-left: 16px; display: grid; gap: 8px; }
    .platform-links li { color: #1f3359; font-size: 0.9rem; }
    .meta-ai-box { border: 1px solid #bfd4f4; border-radius: 12px; background: #f2f8ff; padding: 12px; margin-bottom: 12px; }
    .meta-actions { display: flex; gap: 8px; flex-wrap: wrap; }
    .meta-actions .btn { width: auto; }
    .mono { font-family: 'Courier New', monospace; font-size: 0.8rem; color: #304a71; }

    .learner-report-header {
      display: grid;
      grid-template-columns: auto minmax(0, 1fr);
      gap: 12px;
      align-items: center;
      margin-bottom: 12px;
      border: 1px solid #d8def0;
      border-radius: 12px;
      background: #f8fbff;
      padding: 10px;
    }

    .learner-avatar {
      width: 74px;
      height: 74px;
      border-radius: 12px;
      border: 1px solid #bfd4f4;
      overflow: hidden;
      background: #e8eef8;
      display: grid;
      place-items: center;
      font-weight: 800;
      font-size: 1.05rem;
      color: #1a4a8a;
    }

    .learner-avatar img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

    .learner-report-meta {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
      gap: 6px 10px;
      font-size: 0.9rem;
      color: #1f3359;
    }

    .learner-report-meta strong {
      color: #0f3d7e;
    }

    .report-card-sheet {
      border: 1px solid #777;
      border-radius: 12px;
      background: #fff;
      padding: 10px;
      color: #111;
      font-family: Arial, sans-serif;
    }

    .report-card-sheet .rc-title {
      text-align: center;
      font-weight: 700;
      font-size: 1rem;
      margin: 4px 0;
    }

    .report-card-sheet .rc-sub {
      text-align: center;
      font-size: 0.8rem;
      color: #333;
      margin-bottom: 8px;
    }

    .report-card-sheet .rc-grid {
      display: grid;
      grid-template-columns: repeat(3, minmax(0, 1fr));
      gap: 8px;
      font-size: 0.8rem;
      margin-bottom: 8px;
    }

    .report-card-sheet table {
      width: 100%;
      border-collapse: collapse;
      font-size: 0.8rem;
      margin-bottom: 8px;
    }

    .report-card-sheet th,
    .report-card-sheet td {
      border: 1px solid #777;
      padding: 4px 6px;
      text-align: left;
    }

    .report-card-sheet th {
      background: #efefef;
    }

    .report-card-sheet .rc-lines {
      border: 1px solid #777;
      border-radius: 10px;
      padding: 8px;
      margin-top: 8px;
    }

    .report-card-sheet .rc-line {
      border-bottom: 1px solid #bbb;
      height: 18px;
    }

    a:hover,
    button:hover,
    input:hover,
    select:hover,
    textarea:hover,
    [role="button"]:hover,
    .button:hover,
    .btn:hover,
    .submit:hover,
    .chip:hover,
    .meta-chip:hover,
    .side-btn:hover,
    .desk-nav-btn:not(.active):hover,
    .menu button:hover,
    table tbody tr:hover td,
    table tbody tr:hover th,
    .card:hover,
    .panel:hover,
    .item:hover,
    .metric:hover,
    .stat:hover {
      background-color: #dff4ff !important;
      border-color: #7cc9ff !important;
      color: #083b78 !important;
    }

    @media (max-width: 900px) { .desk-layout { grid-template-columns: 1fr; } .stats { grid-template-columns: 1fr; } }

    @media (max-width: 560px) {
      body { padding: 10px; }
      .header { padding: 12px; }
      .panel { padding: 12px; }
      th, td { padding: 7px; }

      input,
      select,
      textarea {
        font-size: 16px;
      }

      .side-btn,
      .chip {
        min-height: 44px;
      }
    }

    @media (max-width: 760px) {
      .ledger-money-grid { grid-template-columns: 1fr; }
    }
  </style>
</head>
<body>
  <div class="shell">
    <header class="header">
      <div class="header-brand">
        <img class="header-logo" src="<?= h($resolvedSchoolLogoPath !== '' ? $resolvedSchoolLogoPath : $defaultSchoolLogoPath) ?>" alt="<?= h($schoolName) ?> logo">
        <div>
          <h1>Learner Desk</h1>
          <div><?= h($schoolName) ?> | Welcome <?= h($learnerDisplayName) ?> (<?= h($activeLearner['reg_number']) ?>)</div>
        </div>
      </div>
      <div style="display:flex; gap:8px; flex-wrap:wrap;">
        <span class="chip" id="learnerDeskProgressChip">Step 1 of 7</span>
        <span class="chip"><?= h($activeLearner['class_name']) ?></span>
        <a class="chip" href="logout.php">Logout</a>
      </div>
    </header>

    <?php foreach ($alerts as $alert): ?>
      <div class="alert <?= $alert['type'] === 'success' ? 'success' : 'error' ?>"><?= h($alert['text']) ?></div>
    <?php endforeach; ?>

    <div class="desk-layout">
      <aside class="sidebar">
        <button type="button" class="side-btn active" data-target="learnerHomeView">Summary</button>
        <button type="button" class="side-btn" data-target="learnerProfileView">My Profile</button>
        <button type="button" class="side-btn" data-target="learnerTasksView">E-learning Hub</button>
        <button type="button" class="side-btn" data-target="learnerReportCardView">My Report Card</button>
        <button type="button" class="side-btn" data-target="learnerStudentAccountView">Student Account</button>
        <button type="button" class="side-btn" data-target="learnerAnnouncementsView">Announcements</button>
        <button type="button" class="side-btn" data-target="learnerLibraryView">E-Library</button>
      </aside>

      <main class="main">
        <section id="learnerProfileView" class="view">
          <section class="panel">
            <h2 style="margin:0 0 8px;">My Profile</h2>
            <p class="muted">Your school record stays visible here. You can add a contact email and set or change your learner password.</p>

            <div class="stats" style="margin-bottom:10px;">
              <article class="stat"><small>Full Name</small><strong><?= h($learnerDisplayName) ?></strong></article>
              <article class="stat"><small>Reg Number</small><strong><?= h($activeLearner['reg_number']) ?></strong></article>
              <article class="stat"><small>Class</small><strong><?= h($activeLearner['class_name']) ?></strong></article>
            </div>

            <div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(260px, 1fr)); gap:10px;">
              <article class="panel" style="margin:0;">
                <h3 style="margin:0 0 8px;">Contact Email</h3>
                <form method="POST" autocomplete="off">
                  <div class="field">
                    <label for="learner_contact_email">Email Address</label>
                    <input id="learner_contact_email" type="email" name="contact_email" value="<?= h((string)($activeLearner['contact_email'] ?? '')) ?>" placeholder="Optional learner contact email">
                  </div>
                  <button class="btn" type="submit" name="save_learner_profile">Save Profile</button>
                </form>
              </article>

              <article class="panel" style="margin:0;">
                <h3 style="margin:0 0 8px;">Change Password</h3>
                <form method="POST" autocomplete="off">
                  <div class="field">
                    <label for="learner_current_password">Current Password</label>
                    <input id="learner_current_password" type="password" name="current_password" required>
                  </div>
                  <div class="field">
                    <label for="learner_new_password">New Password</label>
                    <input id="learner_new_password" type="password" name="new_password" minlength="10" autocomplete="new-password" required>
                  </div>
                  <div class="field">
                    <label for="learner_confirm_password">Confirm New Password</label>
                    <input id="learner_confirm_password" type="password" name="confirm_password" minlength="10" autocomplete="new-password" required>
                  </div>
                  <p class="muted" style="margin:-2px 0 10px;">If this is your first password change, enter your surname as the current password.</p>
                  <button class="btn" type="submit" name="change_learner_password">Change Password</button>
                </form>
              </article>
            </div>
          </section>
        </section>

        <section id="learnerHomeView" class="view active">
          <section class="learner-report-header" aria-label="Learner report header">
            <div class="learner-avatar">
              <?php if ($learnerPhotoExists): ?>
                <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
              <?php else: ?>
                <span><?= h($learnerInitials) ?></span>
              <?php endif; ?>
            </div>
            <div>
              <div style="font-family:'Plus Jakarta Sans',sans-serif;font-size:1.02rem;font-weight:700;color:#0f3d7e;margin-bottom:6px;">Learner Report Header</div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Birth Certificate No.:</strong> <?= h($activeLearner['birth_certificate_number'] ?: '-') ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Gender:</strong> <?= h($activeLearner['gender'] ?: '-') ?></div>
                <div><strong>Date of Admission:</strong> <?= h($activeLearner['date_of_admission'] ?: '-') ?></div>
                <div><strong>Date of Birth:</strong> <?= h($activeLearner['date_of_birth_info'] ?: '-') ?></div>
                <div><strong>Guardian:</strong> <?= h($activeLearner['guardian_name'] ?: '-') ?></div>
                <div><strong>Guardian Phone:</strong> <?= h($activeLearner['guardian_phone'] ?: '-') ?></div>
              </div>
            </div>
          </section>

          <section class="stats">
            <article class="stat"><small>Class</small><strong><?= h($activeLearner['class_name']) ?></strong></article>
            <article class="stat"><small>Total Hub Items</small><strong><?= h(count($hubItems)) ?></strong></article>
            <article class="stat"><small>Feedback Returned</small><strong><?= h($feedbackCount) ?></strong></article>
            <article class="stat"><small>Library Subjects</small><strong><?= h(count($learnerLibraryBySubject)) ?></strong></article>
            <article class="stat"><small>Library Files</small><strong><?= h(count($libraryUploads)) ?></strong></article>
            <article class="stat"><small>My Library Accesses</small><strong><?= h($learnerLibraryAccessCount) ?></strong></article>
            <article class="stat"><small>Announcements</small><strong><?= h($learnerAnnouncementCount) ?></strong></article>
          </section>
          <section class="panel" style="margin-top:10px;">
            <h2 style="margin:0 0 6px;">How It Works</h2>
            <p class="muted">Open E-learning Hub, submit your work for each item, then check status, score and teacher comments after marking.</p>
          </section>
        </section>

        <section id="learnerTasksView" class="view">
          <section class="panel">
            <section class="learner-report-header" aria-label="Learner report header">
              <div class="learner-avatar">
                <?php if ($learnerPhotoExists): ?>
                  <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
                <?php else: ?>
                  <span><?= h($learnerInitials) ?></span>
                <?php endif; ?>
              </div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Guardian:</strong> <?= h($activeLearner['guardian_name'] ?: '-') ?></div>
              </div>
            </section>

            <h2 style="margin:0 0 8px;">E-learning Hub Items</h2>
            <div style="display:flex; gap:8px; margin-bottom:8px; flex-wrap:wrap;">
              <button class="btn" type="button" id="toggleLearnerTasksContentBtn">Show E-learning Hub</button>
            </div>

            <div id="learnerTasksContentView" class="toggle-view">
            <?php if (empty($hubItems)): ?>
              <p class="muted">No learning posts available for your class yet.</p>
            <?php else: ?>
              <?php foreach ($hubItems as $item): ?>
                <article class="item">
                  <h3><?= h($item['title']) ?></h3>
                  <div class="muted" style="margin-bottom:6px;">
                    <span class="badge"><?= h(ucfirst($item['content_type'])) ?></span>
                    <span style="margin-left:8px;">Teacher: <?= h($item['teacher_name']) ?></span>
                    <span style="margin-left:8px;">Due: <?= h($item['due_date'] ?: '-') ?></span>
                  </div>
                  <?php if (!empty($item['instructions'])): ?>
                    <div class="muted" style="margin-bottom:6px;"><?= nl2br(h($item['instructions'])) ?></div>
                  <?php endif; ?>
                  <?php if (!empty($item['resource_url'])): ?>
                    <div style="margin-bottom:8px;"><a href="<?= h($item['resource_url']) ?>" target="_blank" rel="noopener">Open teacher resource</a></div>
                  <?php endif; ?>

                  <form method="POST" enctype="multipart/form-data">
                    <input type="hidden" name="post_id" value="<?= h($item['id']) ?>">
                    <div class="field">
                      <label>My Response</label>
                      <textarea name="response_text" placeholder="Type your response...\"><?= h($item['response_text']) ?></textarea>
                    </div>
                    <div class="field">
                      <label>Submission Link (Optional)</label>
                      <input name="response_link" placeholder="https://..." value="<?= h($item['response_link']) ?>">
                    </div>
                    <div class="field">
                      <label>Upload Assignment (PDF, Picture, Word)</label>
                      <input type="file" name="response_file" accept=".pdf,.doc,.docx,image/*">
                      <small class="muted">Allowed: PDF, DOC, DOCX, JPG, PNG, GIF, WEBP (max 10MB).</small>
                    </div>
                    <button class="btn" type="submit" name="submit_hub_response">Submit for Marking</button>
                  </form>

                  <div style="margin-top:8px;" class="muted">
                    Status: <span class="badge"><?= h(ucfirst($item['status'] ?: 'not submitted')) ?></span>
                    <?php if (!empty($item['attachment_path'])): ?>
                      <div style="margin-top:6px;">
                        Uploaded File:
                        <a href="<?= h($item['attachment_path']) ?>" target="_blank" rel="noopener"><?= h($item['attachment_name'] ?: 'Open file') ?></a>
                      </div>
                    <?php endif; ?>
                    <?php if ($item['score'] !== null): ?>
                      <span style="margin-left:10px;">Score: <strong><?= h($item['score']) ?></strong></span>
                    <?php endif; ?>
                    <?php if (!empty($item['teacher_comment'])): ?>
                      <div style="margin-top:6px;"><strong>Teacher Comment:</strong> <?= h($item['teacher_comment']) ?></div>
                    <?php endif; ?>
                  </div>
                </article>
              <?php endforeach; ?>
            <?php endif; ?>
            </div>
          </section>
        </section>

        <section id="learnerReportCardView" class="view">
          <section class="panel">
            <section class="learner-report-header" aria-label="Learner report header">
              <div class="learner-avatar">
                <?php if ($learnerPhotoExists): ?>
                  <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
                <?php else: ?>
                  <span><?= h($learnerInitials) ?></span>
                <?php endif; ?>
              </div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Date of Admission:</strong> <?= h($activeLearner['date_of_admission'] ?: '-') ?></div>
              </div>
            </section>

            <h2 style="margin:0 0 8px;">My Student Report Card</h2>
            <p class="muted">This tab shows your report card only. Choose an exam period and print when ready.</p>

            <div style="display:flex; gap:8px; margin-bottom:10px; flex-wrap:wrap; align-items:end;">
              <div class="field" style="margin:0; min-width:240px;">
                <label for="learnerReportExamType">Exam Period</label>
                <select id="learnerReportExamType">
                  <?php foreach ($reportCardExamOptions as $examKey => $examLabel): ?>
                    <option value="<?= h($examKey) ?>"><?= h($examLabel) ?></option>
                  <?php endforeach; ?>
                </select>
              </div>
              <button class="btn" type="button" id="printLearnerReportCardBtn">Print My Report Card</button>
            </div>

            <div id="learnerReportCardCanvas"></div>
          </section>
        </section>

        <section id="learnerStudentAccountView" class="view">
          <section class="panel">
            <section class="learner-report-header" aria-label="Learner report header">
              <div class="learner-avatar">
                <?php if ($learnerPhotoExists): ?>
                  <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
                <?php else: ?>
                  <span><?= h($learnerInitials) ?></span>
                <?php endif; ?>
              </div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Guardian:</strong> <?= h($activeLearner['guardian_name'] ?: '-') ?></div>
              </div>
            </section>

            <h2 style="margin:0 0 8px;">Student Account</h2>
            <p class="muted">View-only account records for this learner. Includes invoices, payments, and financial balance.</p>

            <section class="stats" style="margin-bottom:10px;">
              <article class="stat"><small>Total Invoiced</small><strong>$<?= h(number_format($learnerTotalInvoiced, 2)) ?></strong></article>
              <article class="stat"><small>Total Paid (USD Base)</small><strong>$<?= h(number_format($learnerTotalPaid, 2)) ?></strong></article>
              <article class="stat"><small>Outstanding Balance</small><strong>$<?= h(number_format($learnerOutstandingBalance, 2)) ?></strong></article>
            </section>

            <div style="display:flex; gap:8px; margin-bottom:10px; flex-wrap:wrap; align-items:end;">
              <button class="btn" type="button" id="printStudentLedgerBtn">Print Student Ledger</button>
              <button class="btn" type="button" id="printFinancialStatementBtn">Print Financial Statement</button>
              <div class="field" style="margin:0; min-width:280px;">
                <label for="receiptPaymentSelect">Paid Transaction Receipt</label>
                <select id="receiptPaymentSelect">
                  <option value="">Choose payment transaction</option>
                  <?php foreach ($learnerPayments as $payment): ?>
                    <option value="<?= h($payment['id']) ?>">
                      <?= h($payment['payment_date']) ?> - <?= h((string)$payment['currency_code']) ?> <?= h(number_format((float)$payment['amount_paid'], 2)) ?> (USD $<?= h(number_format((float)($payment['amount_paid_usd'] ?: $payment['amount_paid']), 2)) ?>) - <?= h(strtoupper((string)($payment['fee_component'] ?? '-'))) ?> - <?= h(strtoupper(str_replace('_', ' ', (string)$payment['payment_method']))) ?>
                    </option>
                  <?php endforeach; ?>
                </select>
              </div>
              <button class="btn" type="button" id="printReceiptBtn">Print Receipt</button>
            </div>

            <h3 style="margin:8px 0;">Invoice Register (View Only)</h3>
            <div style="overflow-x:auto; margin-bottom:10px;">
              <table style="width:100%; border-collapse:collapse;">
                <thead>
                  <tr>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Invoice #</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Description</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Amount Due</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">GPF</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">SSF</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">SDC</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Registration Fees</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">ZIMSEC</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Paid</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Outstanding</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Due Date</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Status</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if (empty($learnerInvoices)): ?>
                    <tr><td colspan="12" style="border:1px solid #d8def0; padding:8px;">No invoices available.</td></tr>
                  <?php else: ?>
                    <?php foreach ($learnerInvoices as $invoice): ?>
                      <?php $outstanding = max(0, (float)$invoice['amount_due'] - (float)$invoice['paid_total']); ?>
                      <tr>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($invoice['invoice_number']) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($invoice['description']) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)$invoice['amount_due'], 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($invoice['fee_gpf'] ?? 0), 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($invoice['fee_ssf'] ?? 0), 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($invoice['fee_sdc'] ?? 0), 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($invoice['fee_registration'] ?? 0), 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($invoice['fee_zimsec'] ?? 0), 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)$invoice['paid_total'], 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format($outstanding, 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($invoice['due_date'] ?: '-') ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h(strtoupper(str_replace('_', ' ', (string)$invoice['status']))) ?></td>
                      </tr>
                    <?php endforeach; ?>
                  <?php endif; ?>
                </tbody>
              </table>
            </div>

            <h3 style="margin:8px 0;">Payments Received (View Only)</h3>
            <div style="overflow-x:auto; margin-bottom:10px;">
              <table style="width:100%; border-collapse:collapse;">
                <thead>
                  <tr>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Date</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Invoice #</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Fee Type</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Method</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Reference</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Amount (Original)</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Rate to USD</th>
                    <th style="border:1px solid #d8def0; padding:8px; text-align:left; background:#f0f5ff;">Amount (USD)</th>
                  </tr>
                </thead>
                <tbody>
                  <?php if (empty($learnerPayments)): ?>
                    <tr><td colspan="8" style="border:1px solid #d8def0; padding:8px;">No payments recorded yet.</td></tr>
                  <?php else: ?>
                    <?php foreach ($learnerPayments as $payment): ?>
                      <tr>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($payment['payment_date']) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($payment['invoice_number'] ?: '-') ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h(strtoupper((string)($payment['fee_component'] ?? '-'))) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h(strtoupper(str_replace('_', ' ', (string)$payment['payment_method']))) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h($payment['reference_no'] ?: '-') ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h((string)$payment['currency_code']) ?> <?= h(number_format((float)$payment['amount_paid'], 2)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;"><?= h(number_format((float)$payment['fx_rate_to_usd'], 8)) ?></td>
                        <td style="border:1px solid #d8def0; padding:8px;">$<?= h(number_format((float)($payment['amount_paid_usd'] ?: $payment['amount_paid']), 2)) ?></td>
                      </tr>
                    <?php endforeach; ?>
                  <?php endif; ?>
                </tbody>
              </table>
            </div>

            <h3 style="margin:8px 0;">Student Ledger (View Only)</h3>
            <div class="ledger-balance-banner">
              Total Money Owed: $<?= h(number_format($learnerOutstandingBalance, 2)) ?>
            </div>
            <div class="ledger-stream" id="studentLedgerTable">
              <?php if (empty($ledgerEntries)): ?>
                <article class="ledger-row">
                  <div class="ledger-row-desc">No ledger transactions available.</div>
                </article>
              <?php else: ?>
                <?php foreach ($ledgerEntries as $entry): ?>
                  <article class="ledger-row">
                    <div class="ledger-row-head">
                      <div class="ledger-row-date"><?= h(substr((string)$entry['entry_date'], 0, 10)) ?></div>
                      <div class="ledger-row-ref">Ref: <?= h($entry['reference']) ?></div>
                    </div>
                    <div class="ledger-row-desc"><?= h($entry['description']) ?></div>
                    <div class="ledger-money-grid">
                      <div class="ledger-money-card debit">
                        <small>Debit</small>
                        <strong>$<?= h(number_format((float)$entry['debit'], 2)) ?></strong>
                      </div>
                      <div class="ledger-money-card credit">
                        <small>Credit</small>
                        <strong>$<?= h(number_format((float)$entry['credit'], 2)) ?></strong>
                      </div>
                      <div class="ledger-money-card balance">
                        <small>Balance</small>
                        <strong>$<?= h(number_format((float)$entry['balance'], 2)) ?></strong>
                      </div>
                    </div>
                  </article>
                <?php endforeach; ?>
              <?php endif; ?>
            </div>
          </section>
        </section>

        <section id="learnerAnnouncementsView" class="view">
          <section class="panel">
            <section class="learner-report-header" aria-label="Learner report header">
              <div class="learner-avatar">
                <?php if ($learnerPhotoExists): ?>
                  <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
                <?php else: ?>
                  <span><?= h($learnerInitials) ?></span>
                <?php endif; ?>
              </div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Total Announcements:</strong> <?= h($learnerAnnouncementCount) ?></div>
              </div>
            </section>

            <h2 style="margin:0 0 8px;">Announcements for Learners</h2>
            <p class="muted">Messages from admin and teachers intended for all learners.</p>

            <?php if (empty($learnerAnnouncements)): ?>
              <p class="muted">No announcements posted for learners yet.</p>
            <?php else: ?>
              <?php foreach ($learnerAnnouncements as $announcement): ?>
                <article class="item">
                  <h3><?= h($announcement['title']) ?></h3>
                  <div class="muted" style="margin-bottom:6px;">
                    <span class="badge">From: <?= h(ucfirst((string)$announcement['sender_role'])) ?> - <?= h($announcement['sender_name']) ?></span>
                    <span style="margin-left:8px;">Posted: <?= h($announcement['published_at'] ?: $announcement['created_at']) ?></span>
                  </div>
                  <div class="muted"><?= nl2br(h($announcement['body'])) ?></div>
                </article>
              <?php endforeach; ?>
            <?php endif; ?>
          </section>
        </section>

        <section id="learnerLibraryView" class="view">
          <section class="panel">
            <section class="learner-report-header" aria-label="Learner report header">
              <div class="learner-avatar">
                <?php if ($learnerPhotoExists): ?>
                  <img src="<?= h($learnerPhotoPath) ?>" alt="Learner photo">
                <?php else: ?>
                  <span><?= h($learnerInitials) ?></span>
                <?php endif; ?>
              </div>
              <div class="learner-report-meta">
                <div><strong>Name:</strong> <?= h($learnerDisplayName) ?></div>
                <div><strong>Reg Number:</strong> <?= h($activeLearner['reg_number']) ?></div>
                <div><strong>Class:</strong> <?= h($activeLearner['class_name']) ?></div>
                <div><strong>Date of Admission:</strong> <?= h($activeLearner['date_of_admission'] ?: '-') ?></div>
              </div>
            </section>

            <h2 style="margin:0 0 8px;">E-Library</h2>
            <p class="muted">Resources below are based on subjects in your class, plus trusted learning platforms.</p>

            <div style="display:flex; gap:8px; margin-bottom:8px; flex-wrap:wrap;">
              <button class="btn" type="button" id="toggleLearnerLibraryContentBtn">Show E-Library</button>
            </div>

            <div id="learnerLibraryContentView" class="toggle-view">

            <div class="meta-ai-box">
              <div class="field">
                <label for="learnerMetaSubject">Subject for Meta AI</label>
                <select id="learnerMetaSubject">
                  <?php if (empty($learnerLibraryBySubject)): ?>
                    <option value="General">General</option>
                  <?php else: ?>
                    <?php foreach (array_keys($learnerLibraryBySubject) as $subjectName): ?>
                      <option value="<?= h($subjectName) ?>"><?= h($subjectName) ?></option>
                    <?php endforeach; ?>
                  <?php endif; ?>
                </select>
              </div>
              <div class="field">
                <label for="learnerMetaPrompt">Study Prompt</label>
                <textarea id="learnerMetaPrompt">Explain General for a Zimbabwe primary learner using simple examples. Then give 5 quiz questions and answers.</textarea>
              </div>
              <div class="meta-actions">
                <a class="btn" href="https://www.meta.ai/" target="_blank" rel="noopener">Open Meta AI</a>
                <button class="btn" type="button" id="copyLearnerMetaPromptBtn">Copy Prompt</button>
              </div>
            </div>

            <h3 style="margin:0 0 8px;">Subject Resource Shelf</h3>
            <?php if (empty($learnerLibraryBySubject)): ?>
              <p class="muted">No subject list found for your class yet. Ask your teacher or admin to assign class subjects.</p>
            <?php else: ?>
              <div class="library-grid">
                <?php foreach ($learnerLibraryBySubject as $subjectName => $resources): ?>
                  <article class="resource-card">
                    <h3><?= h($subjectName) ?></h3>
                    <ul class="resource-list">
                      <?php foreach ($resources as $resource): ?>
                        <li>
                          <a href="<?= h($resource['url']) ?>" target="_blank" rel="noopener"><?= h($resource['title']) ?></a>
                          <span class="muted">(<?= h($resource['type']) ?>)</span>
                        </li>
                      <?php endforeach; ?>
                    </ul>
                  </article>
                <?php endforeach; ?>
              </div>
            <?php endif; ?>

            <h3 style="margin:12px 0 8px;">Learning Platform Links</h3>
            <ul class="platform-links">
              <?php foreach ($coreLearningPlatforms as $platform): ?>
                <li>
                  <a href="<?= h($platform['url']) ?>" target="_blank" rel="noopener"><?= h($platform['name']) ?></a>
                  <span class="muted"> - <?= h($platform['description']) ?></span>
                </li>
              <?php endforeach; ?>
            </ul>

            <h3 style="margin:14px 0 8px;">Class Library Files (Uploaded by Teachers)</h3>
            <?php if (empty($libraryUploads)): ?>
              <p class="muted">No teacher uploads in your class e-library yet.</p>
            <?php else: ?>
              <div class="meta-actions" id="learnerLibraryFilterBar" style="margin-bottom:10px;">
                <button class="btn" type="button" data-doc-filter="all">All</button>
                <?php foreach ($libraryDocumentTypes as $docKey => $docLabel): ?>
                  <button class="btn" type="button" data-doc-filter="<?= h($docKey) ?>"><?= h($docLabel) ?></button>
                <?php endforeach; ?>
              </div>
              <?php foreach ($libraryUploads as $upload): ?>
                <article class="item learner-library-item" data-doc-type="<?= h($upload['document_type']) ?>">
                  <h3><?= h($upload['title']) ?></h3>
                  <div class="muted" style="margin-bottom:6px;">
                    <span class="badge"><?= h($upload['subject_name'] ?: 'General') ?></span>
                    <span class="badge" style="margin-left:6px;"><?= h($libraryDocumentTypes[$upload['document_type']] ?? 'Other Document') ?></span>
                    <span style="margin-left:8px;">Teacher: <?= h($upload['teacher_name']) ?></span>
                    <span style="margin-left:8px;">Size: <?= h(format_file_size($upload['file_size'])) ?></span>
                  </div>
                  <?php if (!empty($upload['description'])): ?>
                    <div class="muted" style="margin-bottom:6px;"><?= nl2br(h($upload['description'])) ?></div>
                  <?php endif; ?>
                  <div class="mono" style="margin-bottom:8px;"><?= h($upload['original_name']) ?></div>
                  <div style="display:flex; gap:8px; flex-wrap:wrap;">
                    <a class="btn" href="learner.php?library_action=view&amp;resource_id=<?= h($upload['id']) ?>" target="_blank" rel="noopener">Read / Open</a>
                    <a class="btn" href="learner.php?library_action=download&amp;resource_id=<?= h($upload['id']) ?>">Download</a>
                  </div>
                </article>
              <?php endforeach; ?>
            <?php endif; ?>
            </div>
          </section>
        </section>
      </main>
    </div>
  </div>

  <script>
    const reportCardDataset = <?= json_encode($reportCardDataset, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
    const learnerInvoiceData = <?= json_encode($learnerInvoices, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
    const learnerPaymentData = <?= json_encode($learnerPayments, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
    const learnerLedgerData = <?= json_encode($ledgerEntries, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>;
    const learnerReportMeta = {
      schoolName: <?= json_encode($schoolName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      schoolAddress: <?= json_encode($schoolAddress, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      schoolContact: <?= json_encode($schoolContact, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      schoolMotto: <?= json_encode($schoolMotto, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      palette: {
        ink: <?= json_encode($paletteInk, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
        muted: <?= json_encode($paletteMuted, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
        brandNavy: <?= json_encode($paletteBrandNavy, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
        brandNavyDeep: <?= json_encode($paletteBrandNavyDeep, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
        brandIce: <?= json_encode($paletteBrandIce, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
        line: <?= json_encode($palettePatchSky, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>
      },
      learnerName: <?= json_encode($learnerDisplayName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      logoDataUri: <?= json_encode($schoolLogoDataUri, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      logoPath: <?= json_encode($resolvedSchoolLogoPath !== '' ? $resolvedSchoolLogoPath : $defaultSchoolLogoPath, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      regNumber: <?= json_encode((string)$activeLearner['reg_number'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      birthCertificateNo: <?= json_encode((string)($activeLearner['birth_certificate_number'] ?? ''), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      className: <?= json_encode((string)$activeLearner['class_name'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      gender: <?= json_encode((string)($activeLearner['gender'] ?? ''), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      admissionDate: <?= json_encode((string)($activeLearner['date_of_admission'] ?? ''), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      dateOfBirth: <?= json_encode((string)($activeLearner['date_of_birth_info'] ?? ''), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      learnerPhotoPath: <?= json_encode($learnerPhotoExists ? $learnerPhotoPath : '', JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      learnerInitials: <?= json_encode($learnerInitials, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>
    };

    const learnerStatementTotals = {
      totalInvoiced: <?= json_encode(round($learnerTotalInvoiced, 2), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      totalPaid: <?= json_encode(round($learnerTotalPaid, 2), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>,
      outstanding: <?= json_encode(round($learnerOutstandingBalance, 2), JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) ?>
    };

    const learnerNavBtns = Array.from(document.querySelectorAll('.side-btn'));
    const learnerViews = Array.from(document.querySelectorAll('.view'));
    const learnerDeskProgressChip = document.getElementById('learnerDeskProgressChip');

    function openLearnerView(viewId) {
      // Explicitly remove active class from ALL views
      learnerViews.forEach(view => {
        view.classList.remove('active');
      });
      
      // Explicitly add active class to target view
      if (viewId) {
        const targetView = document.getElementById(viewId);
        if (targetView) {
          targetView.classList.add('active');
        }
      }
      
      // Explicitly remove active class from ALL buttons
      learnerNavBtns.forEach(btn => {
        btn.classList.remove('active');
      });
      
      // Explicitly add active class to matching button
      if (viewId) {
        const matchingBtn = learnerNavBtns.find(btn => btn.dataset.target === viewId);
        if (matchingBtn) {
          matchingBtn.classList.add('active');
        }
      }

      const activeIndex = learnerNavBtns.findIndex(btn => btn.classList.contains('active'));
      if (learnerDeskProgressChip && activeIndex >= 0) {
        const activeBtn = learnerNavBtns[activeIndex] || null;
        const activeLabel = String(activeBtn?.childNodes?.[0]?.textContent || activeBtn?.textContent || 'Current Section').trim().replace(/\s+/g, ' ');
        learnerDeskProgressChip.textContent = `Step ${activeIndex + 1} of ${learnerNavBtns.length} | ${activeLabel}`;
      }
    }

    const learnerMetaSubject = document.getElementById('learnerMetaSubject');
    const learnerMetaPrompt = document.getElementById('learnerMetaPrompt');
    const copyLearnerMetaPromptBtn = document.getElementById('copyLearnerMetaPromptBtn');
    const learnerTasksContentView = document.getElementById('learnerTasksContentView');
    const toggleLearnerTasksContentBtn = document.getElementById('toggleLearnerTasksContentBtn');
    const learnerLibraryContentView = document.getElementById('learnerLibraryContentView');
    const toggleLearnerLibraryContentBtn = document.getElementById('toggleLearnerLibraryContentBtn');
    const learnerLibraryFilterBtns = Array.from(document.querySelectorAll('#learnerLibraryFilterBar [data-doc-filter]'));
    const learnerLibraryItems = Array.from(document.querySelectorAll('.learner-library-item'));
    const learnerReportExamType = document.getElementById('learnerReportExamType');
    const learnerReportCardCanvas = document.getElementById('learnerReportCardCanvas');
    const printLearnerReportCardBtn = document.getElementById('printLearnerReportCardBtn');
    const printStudentLedgerBtn = document.getElementById('printStudentLedgerBtn');
    const printFinancialStatementBtn = document.getElementById('printFinancialStatementBtn');
    const receiptPaymentSelect = document.getElementById('receiptPaymentSelect');
    const printReceiptBtn = document.getElementById('printReceiptBtn');

    const learnerToggleRegistry = [];

    function closeOtherLearnerTogglePanels(activeViewEl) {
      if (!activeViewEl) {
        return;
      }

      learnerToggleRegistry.forEach(entry => {
        if (!entry || entry.viewEl === activeViewEl) {
          return;
        }
        entry.viewEl.classList.remove('active');
        entry.btnEl.textContent = entry.showLabel;
      });
    }

    function wireLearnerToggle(viewEl, btnEl, showLabel, hideLabel) {
      if (!viewEl || !btnEl) {
        return;
      }

      learnerToggleRegistry.push({ viewEl, btnEl, showLabel, hideLabel });

      btnEl.addEventListener('click', function () {
        const show = !viewEl.classList.contains('active');
        if (show) {
          closeOtherLearnerTogglePanels(viewEl);
        }
        viewEl.classList.toggle('active', show);
        btnEl.textContent = show ? hideLabel : showLabel;
      });
    }

    function applyLearnerLibraryFilter(filterType) {
      if (!learnerLibraryItems.length) {
        return;
      }

      learnerLibraryItems.forEach(item => {
        const itemType = String(item.dataset.docType || 'other');
        const show = filterType === 'all' || itemType === filterType;
        item.style.display = show ? '' : 'none';
      });

      learnerLibraryFilterBtns.forEach(btn => {
        const active = btn.dataset.docFilter === filterType;
        btn.style.opacity = active ? '1' : '0.7';
      });
    }

    learnerLibraryFilterBtns.forEach(btn => {
      btn.addEventListener('click', function () {
        applyLearnerLibraryFilter(String(this.dataset.docFilter || 'all'));
      });
    });

    applyLearnerLibraryFilter('all');

    wireLearnerToggle(learnerTasksContentView, toggleLearnerTasksContentBtn, 'Show E-learning Hub', 'Hide E-learning Hub');
    wireLearnerToggle(learnerLibraryContentView, toggleLearnerLibraryContentBtn, 'Show E-Library', 'Hide E-Library');

    function updateLearnerMetaPrompt() {
      if (!learnerMetaPrompt) {
        return;
      }
      const selectedSubject = String(learnerMetaSubject?.value || 'General').trim() || 'General';
      learnerMetaPrompt.value = `Explain ${selectedSubject} for a Zimbabwe primary learner using simple examples. Then give 5 quiz questions and answers.`;
    }

    if (learnerMetaSubject) {
      learnerMetaSubject.addEventListener('change', updateLearnerMetaPrompt);
    }

    if (copyLearnerMetaPromptBtn && learnerMetaPrompt) {
      copyLearnerMetaPromptBtn.addEventListener('click', function () {
        learnerMetaPrompt.select();
        learnerMetaPrompt.setSelectionRange(0, learnerMetaPrompt.value.length);
        const copied = document.execCommand('copy');
        if (copied) {
          copyLearnerMetaPromptBtn.textContent = 'Copied';
          setTimeout(() => {
            copyLearnerMetaPromptBtn.textContent = 'Copy Prompt';
          }, 1200);
        }
      });
    }

    function rcEscape(value) {
      return String(value ?? '')
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#39;');
    }

    const learnerReportPalette = {
      ink: String(learnerReportMeta?.palette?.ink || '#173122'),
      muted: String(learnerReportMeta?.palette?.muted || '#4F6656'),
      brandNavy: String(learnerReportMeta?.palette?.brandNavy || '#2E8B45'),
      brandNavyDeep: String(learnerReportMeta?.palette?.brandNavyDeep || '#1D6A33'),
      brandIce: String(learnerReportMeta?.palette?.brandIce || '#EEF7EF'),
      line: String(learnerReportMeta?.palette?.line || '#D6EEFF')
    };

    function buildReportCardMarkup(examData) {
      const rowsHtml = (examData.subject_rows || []).length > 0
        ? examData.subject_rows.map(row => `
            <tr>
              <td>${rcEscape(String(row.subject || '').toUpperCase())}</td>
              <td>${rcEscape(row.obtained)}</td>
              <td>${rcEscape(row.total)}</td>
              <td>${rcEscape(row.percent)}%</td>
              <td>${rcEscape(row.grade)}</td>
              <td>${rcEscape(row.status)}</td>
              <td></td>
            </tr>
          `).join('')
        : '<tr><td colspan="7">No marks have been recorded for this examination.</td></tr>';

      const photoHtml = learnerReportMeta.learnerPhotoPath
        ? `<img src="${rcEscape(learnerReportMeta.learnerPhotoPath)}" alt="Learner photo" style="width:56px;height:56px;border-radius:50%;object-fit:cover;border:1px solid #8c8c8c;">`
        : `<div style="width:56px;height:56px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:1px solid #8c8c8c;background:#efefef;font-weight:700;font-size:11px;">${rcEscape(learnerReportMeta.learnerInitials)}</div>`;
      const logoSrc = learnerReportMeta.logoDataUri && learnerReportMeta.logoDataUri !== ''
        ? learnerReportMeta.logoDataUri
        : learnerReportMeta.logoPath;

      return `
        <article class="report-card-sheet" style="font-family:Arial,sans-serif;background:#fff;border:1px solid ${learnerReportPalette.line};border-radius:16px;padding:8mm;max-width:190mm;width:100%;margin:0 auto;color:${learnerReportPalette.ink};">
          <div style="text-align:center;">
            <div style="margin-bottom:2px;"><img src="${rcEscape(logoSrc)}" alt="School logo" style="width:22mm;height:22mm;object-fit:contain;"></div>
            <div style="font-weight:700;font-size:19px;color:${learnerReportPalette.brandNavyDeep};">${rcEscape(learnerReportMeta.schoolName)}</div>
            <div style="font-size:11px;color:${learnerReportPalette.muted};">${rcEscape(learnerReportMeta.schoolMotto || 'Education for Self Reliance')}</div>
            <div style="font-size:11px;color:${learnerReportPalette.muted};">Zimbabwe</div>
            <div style="font-weight:700;font-size:16px;margin-top:3px;">Student Report Card</div>
          </div>

          <div style="border:1px solid ${learnerReportPalette.line};border-radius:12px;margin-top:8px;overflow:hidden;">
            <div style="display:grid;grid-template-columns:64px 1fr 1fr 1fr;gap:8px;padding:8px;font-size:11px;align-items:center;">
              <div style="display:flex;justify-content:center;">${photoHtml}</div>
              <div>
                <div><strong>REGISTRATION</strong> ${rcEscape(learnerReportMeta.regNumber)}</div>
                <div><strong>NAME</strong> ${rcEscape(learnerReportMeta.learnerName)}</div>
                <div><strong>CLASS</strong> ${rcEscape(learnerReportMeta.className)}</div>
              </div>
              <div>
                <div><strong>DATE OF BIRTH</strong> ${rcEscape(learnerReportMeta.dateOfBirth || 'N/A')}</div>
                <div><strong>BIRTH CERTIFICATE NO</strong> ${rcEscape(learnerReportMeta.birthCertificateNo || 'N/A')}</div>
                <div><strong>GENDER</strong> ${rcEscape(learnerReportMeta.gender || '-')}</div>
                <div><strong>ADMISSION DATE</strong> ${rcEscape(learnerReportMeta.admissionDate || '-')}</div>
              </div>
              <div>
                <div><strong>EXAM</strong> ${rcEscape(examData.exam_label || '-')}</div>
                <div><strong>ATTENDANCE</strong> 0%</div>
                <div><strong>ABSENTS</strong> 0</div>
              </div>
            </div>
          </div>

          <div style="border:1px solid ${learnerReportPalette.line};border-radius:12px;margin-top:8px;overflow:hidden;">
            <div style="background:${learnerReportPalette.brandIce};border-bottom:1px solid ${learnerReportPalette.line};font-size:11px;font-weight:700;padding:4px 6px;color:${learnerReportPalette.brandNavyDeep};">COGNITIVE DOMAIN - ${rcEscape(String(examData.exam_label || '').toUpperCase())}</div>
            <table style="width:100%;border-collapse:collapse;font-size:11px;">
              <thead>
                <tr>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">ASSESSMENTS / SUBJECT</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">OBTAINED</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">TOTAL</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">%</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">GRADE</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">STATUS</th>
                  <th style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;text-align:left;">TEACHER'S COMMENT</th>
                </tr>
              </thead>
              <tbody>${rowsHtml}</tbody>
            </table>

            <table style="width:100%;border-collapse:collapse;font-size:11px;">
              <tbody>
                <tr>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;"><strong>TOTAL MARKS</strong><br>${rcEscape(examData.total_obtained)}</td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;"><strong>TOTAL SCORE</strong><br>${rcEscape(examData.total_obtained)} / ${rcEscape(examData.total_possible)}</td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;"><strong>PERCENTAGE</strong><br>${rcEscape(examData.overall_percent)}%</td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;"><strong>GRADE</strong><br>${rcEscape(examData.overall_grade)}</td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;"><strong>STATUS</strong><br>${rcEscape(examData.overall_status)}</td>
                </tr>
              </tbody>
            </table>

            <table style="width:100%;border-collapse:collapse;font-size:10px;">
              <tbody>
                <tr>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">COMPARISON WITH CLASS</td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">CLASS STRENGTH<br><strong>${rcEscape(examData.class_strength)} STUDENTS</strong></td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">CLASS AVERAGE<br><strong>${rcEscape(examData.class_average)}%</strong></td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">CLASS MAX AVERAGE<br><strong>${rcEscape(examData.class_max)}%</strong></td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">CLASS MIN AVERAGE<br><strong>${rcEscape(examData.class_min)}%</strong></td>
                  <td style="border:1px solid ${learnerReportPalette.line};padding:4px 6px;">STUDENT POSITION<br><strong>${rcEscape(examData.position)}</strong></td>
                </tr>
              </tbody>
            </table>
          </div>

          <div style="border:1px solid ${learnerReportPalette.line};border-radius:12px;margin-top:8px;padding:6px;">
            <div style="font-size:11px;font-weight:700;margin-bottom:4px;">CLASS TEACHER'S OVERALL OBSERVATION</div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
          </div>

          <div style="border:1px solid ${learnerReportPalette.line};border-radius:12px;margin-top:8px;padding:6px;">
            <div style="font-size:11px;font-weight:700;margin-bottom:4px;">HEAD TEACHER'S / PRINCIPAL'S COMMENT</div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
            <div style="border-bottom:1px solid #bbb;height:16px;"></div>
          </div>

          <div style="border:1px solid ${learnerReportPalette.line};border-radius:12px;margin-top:8px;overflow:hidden;">
            <div style="font-size:11px;font-weight:700;padding:4px 6px;border-bottom:1px solid ${learnerReportPalette.line};background:${learnerReportPalette.brandIce};color:${learnerReportPalette.brandNavyDeep};">NEXT TERM INFORMATION</div>
            <div style="font-size:11px;padding:4px 6px;"><strong>Next Term Opens:</strong> __ / __ / ____ &nbsp;&nbsp; <strong>Promotion Status:</strong> PROMOTED / REPEAT &nbsp;&nbsp; <strong>Next Class:</strong> __________</div>
          </div>

          <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px;text-align:center;font-size:10px;margin-top:12px;">
            <div style="border-top:1px solid #777;padding-top:3px;">DATE</div>
            <div style="border-top:1px solid #777;padding-top:3px;">SIGNATURE</div>
            <div style="border-top:1px solid #777;padding-top:3px;">STAMP</div>
            <div style="border-top:1px solid #777;padding-top:3px;">${rcEscape(examData.exam_label || '')}</div>
          </div>
        </article>
      `;
    }

    function renderLearnerReportCard() {
      if (!learnerReportCardCanvas) {
        return;
      }

      const examKey = String(learnerReportExamType?.value || 'end_of_term_exam_term1');
      const examData = reportCardDataset[examKey] || null;
      if (!examData) {
        learnerReportCardCanvas.innerHTML = '<p class="muted">Report card data not available.</p>';
        return;
      }

      learnerReportCardCanvas.innerHTML = buildReportCardMarkup(examData);
    }

    function printLearnerReportCard() {
      const examKey = String(learnerReportExamType?.value || 'end_of_term_exam_term1');
      const examData = reportCardDataset[examKey] || null;
      if (!examData) {
        window.alert('No report card data available for selected exam.');
        return;
      }

      const win = window.open('', '_blank');
      if (!win) {
        window.alert('Popup blocked. Please allow popups and try again.');
        return;
      }

      win.document.write(`
        <html>
          <head>
            <title>${rcEscape(learnerReportMeta.schoolName)} - ${rcEscape(examData.exam_label)}</title>
            <style>
              @page { size: A4 portrait; margin: 8mm; }
              body { font-family: Arial, sans-serif; margin: 0; padding: 0; background: #fff; color: ${learnerReportPalette.ink}; }
              .page-frame {
                position: fixed;
                inset: 2mm;
                border: 0.8px solid ${learnerReportPalette.line};
                border-radius: 8px;
                pointer-events: none;
                z-index: 0;
              }
              .report-card-sheet { page-break-inside: avoid; }
              @media print {
                body { padding: 0; }
                .report-card-sheet {
                  width: 194mm !important;
                  max-width: 194mm !important;
                  border-radius: 0 !important;
                  margin: 0 auto !important;
                }
              }
            </style>
          </head>
          <body>
            <div class="page-frame"></div>
            ${buildReportCardMarkup(examData)}
            <script>window.onload = function(){ window.print(); }<\/script>
          </body>
        </html>
      `);
      win.document.close();
    }

    function buildPrintDoc(title, bodyHtml) {
      const logoSrc = learnerReportMeta.logoDataUri && learnerReportMeta.logoDataUri !== ''
        ? learnerReportMeta.logoDataUri
        : learnerReportMeta.logoPath;
      return `
        <html>
          <head>
            <title>${rcEscape(learnerReportMeta.schoolName)} - ${rcEscape(title)}</title>
            <style>
              @page { size: A4 portrait; margin: 10mm; }
              body {
                font-family: 'Segoe UI', Arial, sans-serif;
                margin: 0;
                padding: 0;
                color: ${learnerReportPalette.ink};
                background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
              }
              .page-frame {
                position: fixed;
                inset: 2mm;
                border: 0.8px solid ${learnerReportPalette.line};
                border-radius: 8px;
                pointer-events: none;
                z-index: 0;
              }
              .sheet {
                max-width: 190mm;
                margin: 0 auto;
                border: 1px solid ${learnerReportPalette.line};
                border-radius: 12px;
                padding: 8mm;
                background: #fff;
                box-shadow: 0 10px 24px rgba(15, 23, 42, 0.08);
                position: relative;
                z-index: 1;
              }
              .head { text-align:center; margin-bottom:10px; position: relative; padding-top: 6px; }
              .head::before {
                content: '';
                display: block;
                height: 4px;
                border-radius: 999px;
                margin-bottom: 8px;
                background: linear-gradient(90deg, ${learnerReportPalette.brandNavyDeep}, #d1a95a 50%, ${learnerReportPalette.brandNavyDeep});
              }
              .head img { width:20mm; height:20mm; object-fit:contain; }
              .head h1 { margin:3px 0; font-size:19px; color:${learnerReportPalette.brandNavyDeep}; }
              .head .sub { font-size:11px; color:${learnerReportPalette.muted}; margin:0; }
              .meta { display:grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap:6px; margin:8px 0; font-size:12px; }
              table { width:100%; border-collapse: collapse; font-size:12px; margin-top:8px; }
              th, td { border:1px solid ${learnerReportPalette.line}; padding:6px; text-align:left; }
              th { background:${learnerReportPalette.brandIce}; color:${learnerReportPalette.brandNavyDeep}; }
              .right { text-align:right; }
              .section-title {
                margin: 10px 0 6px;
                font-family: Georgia, 'Times New Roman', serif;
                color: ${learnerReportPalette.brandNavyDeep};
                font-size: 16px;
                letter-spacing: 0.3px;
              }
              .debt-banner {
                margin: 4px 0 10px;
                padding: 9px 11px;
                border-radius: 10px;
                border: 1px solid #fecaca;
                background: linear-gradient(120deg, #fff1f2 0%, #ffe4e6 100%);
                color: #991b1b;
                font-size: 14px;
                font-weight: 900;
              }
              .ledger-grid { display: grid; gap: 8px; }
              .ledger-item {
                border: 1px solid ${learnerReportPalette.line};
                border-radius: 10px;
                padding: 8px;
                background: linear-gradient(145deg, #ffffff 0%, #f8fbff 100%);
              }
              .ledger-head {
                display: flex;
                justify-content: space-between;
                align-items: center;
                gap: 8px;
                margin-bottom: 5px;
              }
              .ledger-date { font-weight: 800; color: ${learnerReportPalette.brandNavyDeep}; }
              .ledger-ref {
                border: 1px solid #bfdbfe;
                background: #eff6ff;
                color: #1d4ed8;
                border-radius: 999px;
                font-size: 10px;
                padding: 2px 7px;
                font-weight: 700;
              }
              .ledger-desc { font-size: 12px; color: #334155; margin-bottom: 6px; }
              .ledger-money {
                display: grid;
                grid-template-columns: repeat(3, minmax(0, 1fr));
                gap: 6px;
              }
              .ledger-money div {
                border: 1px solid #e2e8f0;
                border-radius: 8px;
                padding: 5px;
                background: #fff;
              }
              .ledger-money small {
                display: block;
                color: #64748b;
                font-size: 9px;
                text-transform: uppercase;
                letter-spacing: 0.3px;
              }
              .ledger-money strong { font-size: 11px; }
              .ledger-money .debit strong { color: #b45309; }
              .ledger-money .credit strong { color: #065f46; }
              .ledger-money .balance strong { color: #991b1b; font-weight: 900; }
            </style>
          </head>
          <body>
            <div class="page-frame"></div>
            <article class="sheet">
              <div class="head">
                <img src="${rcEscape(logoSrc)}" alt="School logo">
                <h1>${rcEscape(learnerReportMeta.schoolName)}</h1>
                <p class="sub">${rcEscape(learnerReportMeta.schoolAddress)}</p>
                <p class="sub"><strong>${rcEscape(title)}</strong></p>
              </div>
              ${bodyHtml}
            </article>
            <script>window.onload = function(){ window.print(); }<\/script>
          </body>
        </html>
      `;
    }

    function printStudentLedger() {
      const rowsHtml = learnerLedgerData.length > 0
        ? learnerLedgerData.map(entry => `
            <article class="ledger-item">
              <div class="ledger-head">
                <div class="ledger-date">${rcEscape(String(entry.entry_date || '').slice(0, 10))}</div>
                <div class="ledger-ref">Ref: ${rcEscape(entry.reference || '-')}</div>
              </div>
              <div class="ledger-desc">${rcEscape(entry.description || '')}</div>
              <div class="ledger-money">
                <div class="debit"><small>Debit</small><strong>$${Number(entry.debit || 0).toFixed(2)}</strong></div>
                <div class="credit"><small>Credit</small><strong>$${Number(entry.credit || 0).toFixed(2)}</strong></div>
                <div class="balance"><small>Balance</small><strong>$${Number(entry.balance || 0).toFixed(2)}</strong></div>
              </div>
            </article>
          `).join('')
        : '<article class="ledger-item"><div class="ledger-desc">No ledger transactions available.</div></article>';

      const bodyHtml = `
        <div class="meta">
          <div><strong>Learner:</strong> ${rcEscape(learnerReportMeta.learnerName)}</div>
          <div><strong>Reg Number:</strong> ${rcEscape(learnerReportMeta.regNumber)}</div>
          <div><strong>Birth Certificate No.:</strong> ${rcEscape(learnerReportMeta.birthCertificateNo || '-')}</div>
          <div><strong>Class:</strong> ${rcEscape(learnerReportMeta.className)}</div>
          <div><strong>Generated:</strong> ${rcEscape(new Date().toLocaleString())}</div>
        </div>
        <div class="section-title">Student Ledger</div>
        <div class="debt-banner">
          Total Money Owed: $${Number(learnerStatementTotals.outstanding || 0).toFixed(2)}
        </div>
        <div class="ledger-grid">${rowsHtml}</div>
      `;

      const win = window.open('', '_blank');
      if (!win) {
        window.alert('Popup blocked. Please allow popups and try again.');
        return;
      }
      win.document.write(buildPrintDoc('Student Ledger', bodyHtml));
      win.document.close();
    }

    function printFinancialStatement() {
      const bodyHtml = `
        <div class="meta">
          <div><strong>Learner:</strong> ${rcEscape(learnerReportMeta.learnerName)}</div>
          <div><strong>Reg Number:</strong> ${rcEscape(learnerReportMeta.regNumber)}</div>
          <div><strong>Birth Certificate No.:</strong> ${rcEscape(learnerReportMeta.birthCertificateNo || '-')}</div>
          <div><strong>Class:</strong> ${rcEscape(learnerReportMeta.className)}</div>
          <div><strong>Statement Date:</strong> ${rcEscape(new Date().toLocaleDateString())}</div>
        </div>
        <table>
          <tbody>
            <tr><th>Total Invoiced</th><td class="right">$${Number(learnerStatementTotals.totalInvoiced || 0).toFixed(2)}</td></tr>
            <tr><th>Total Paid (USD Base)</th><td class="right">$${Number(learnerStatementTotals.totalPaid || 0).toFixed(2)}</td></tr>
            <tr><th>Outstanding Balance</th><td class="right">$${Number(learnerStatementTotals.outstanding || 0).toFixed(2)}</td></tr>
          </tbody>
        </table>
        <table>
          <thead>
            <tr><th>Invoice #</th><th>Description</th><th>Amount Due</th><th>GPF</th><th>SSF</th><th>SDC</th><th>Registration Fees</th><th>ZIMSEC</th><th>Paid</th><th>Status</th></tr>
          </thead>
          <tbody>
            ${learnerInvoiceData.length > 0 ? learnerInvoiceData.map(inv => `
              <tr>
                <td>${rcEscape(inv.invoice_number || '')}</td>
                <td>${rcEscape(inv.description || '')}</td>
                <td class="right">$${Number(inv.amount_due || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.fee_gpf || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.fee_ssf || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.fee_sdc || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.fee_registration || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.fee_zimsec || 0).toFixed(2)}</td>
                <td class="right">$${Number(inv.paid_total || 0).toFixed(2)}</td>
                <td>${rcEscape(String(inv.status || '').toUpperCase().replace(/_/g, ' '))}</td>
              </tr>
            `).join('') : '<tr><td colspan="10">No invoices available.</td></tr>'}
          </tbody>
        </table>
      `;

      const win = window.open('', '_blank');
      if (!win) {
        window.alert('Popup blocked. Please allow popups and try again.');
        return;
      }
      win.document.write(buildPrintDoc('Student Financial Statement', bodyHtml));
      win.document.close();
    }

    function printReceipt() {
      const paymentId = Number(receiptPaymentSelect?.value || 0);
      if (paymentId <= 0) {
        window.alert('Choose a paid transaction first.');
        return;
      }

      const payment = learnerPaymentData.find(row => Number(row.id || 0) === paymentId);
      if (!payment) {
        window.alert('Selected payment transaction not found.');
        return;
      }

      const bodyHtml = `
        <div class="meta">
          <div><strong>Learner:</strong> ${rcEscape(learnerReportMeta.learnerName)}</div>
          <div><strong>Reg Number:</strong> ${rcEscape(learnerReportMeta.regNumber)}</div>
          <div><strong>Birth Certificate No.:</strong> ${rcEscape(learnerReportMeta.birthCertificateNo || '-')}</div>
          <div><strong>Class:</strong> ${rcEscape(learnerReportMeta.className)}</div>
          <div><strong>Receipt Date:</strong> ${rcEscape(String(payment.payment_date || '').slice(0, 10))}</div>
        </div>
        <div class="section-title">Payment Receipt</div>
        <div class="ledger-grid">
          <article class="ledger-item">
            <div class="ledger-head">
              <div class="ledger-date">Receipt # ${rcEscape(payment.reference_no || '-')}</div>
              <div class="ledger-ref">Invoice: ${rcEscape(payment.invoice_number || '-')}</div>
            </div>
            <div class="ledger-money">
              <div class="debit"><small>Amount (Original)</small><strong>${rcEscape(String(payment.currency_code || 'USD'))} ${Number(payment.amount_paid || 0).toFixed(2)}</strong></div>
              <div class="credit"><small>Rate to USD</small><strong>${Number(payment.fx_rate_to_usd || 1).toFixed(8)}</strong></div>
              <div class="balance"><small>Amount (USD)</small><strong>$${Number(payment.amount_paid_usd || payment.amount_paid || 0).toFixed(2)}</strong></div>
            </div>
          </article>
          <article class="ledger-item">
            <div class="ledger-desc"><strong>Fee Type:</strong> ${rcEscape(String(payment.fee_component || '-').toUpperCase())}</div>
            <div class="ledger-desc"><strong>Payment Method:</strong> ${rcEscape(String(payment.payment_method || '').toUpperCase().replace(/_/g, ' '))}</div>
            <div class="ledger-desc"><strong>Receipt Number:</strong> ${rcEscape(payment.reference_no || '-')}</div>
            <div class="ledger-desc"><strong>Note:</strong> ${rcEscape(payment.note || '-')}</div>
          </article>
        </div>
      `;

      const win = window.open('', '_blank');
      if (!win) {
        window.alert('Popup blocked. Please allow popups and try again.');
        return;
      }
      win.document.write(buildPrintDoc('Student Payment Receipt', bodyHtml));
      win.document.close();
    }

    if (learnerReportExamType) {
      learnerReportExamType.addEventListener('change', renderLearnerReportCard);
    }

    if (printLearnerReportCardBtn) {
      printLearnerReportCardBtn.addEventListener('click', printLearnerReportCard);
    }

    if (printStudentLedgerBtn) {
      printStudentLedgerBtn.addEventListener('click', printStudentLedger);
    }

    if (printFinancialStatementBtn) {
      printFinancialStatementBtn.addEventListener('click', printFinancialStatement);
    }

    if (printReceiptBtn) {
      printReceiptBtn.addEventListener('click', printReceipt);
    }

    renderLearnerReportCard();

    learnerNavBtns.forEach(btn => {
      btn.addEventListener('click', function () {
        if (this.dataset.target) {
          openLearnerView(this.dataset.target);
        }
      });
    });
  </script>
  <?php output_auto_capitalize_script(); ?>
  <?php output_select_search_script(); ?>
  <footer style="text-align:center;padding:14px 0 10px;font-size:0.8rem;color:#8a9ab0;opacity:0.7;">Developed by Ngwenya Jonas &copy; 2026</footer>
</body>
</html>
