PNG IHDR x sBIT|d pHYs + tEXtSoftware www.inkscape.org< ,tEXtComment
<?php
// 🔴 DEBUG MODE ON
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
session_start();
// 👇 FIX: Force the server to use Nigerian Time
date_default_timezone_set('Africa/Lagos');
// 1. Ensure the user is logged in
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
// 2. Database Configuration
$host = 'localhost';
$dbname = 'u264723324_C1Vdb';
$user = 'u264723324_C1Vun';
$pass = '@RTdAq123&a';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// =======================================================
// AJAX HANDLERS: TRANSFER, PIN, OTP, CARDS & NAME RESOLUTION
// =======================================================
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax_action'])) {
ob_clean();
header('Content-Type: application/json');
$userId = $_SESSION['user_id'];
// Helper function to check hashed PIN and increment failed attempts via Session
function verifyPinOrBlock($pdo, $userId, $inputPin) {
$stmt = $pdo->prepare("SELECT pin_hash FROM users WHERE id = :id LIMIT 1");
$stmt->execute([':id' => $userId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) {
return ['success' => false, 'message' => 'User not found.'];
}
if (!isset($_SESSION['failed_pin_attempts'])) {
$_SESSION['failed_pin_attempts'] = 0;
}
// Securely verify the hashed PIN
if (password_verify($inputPin, $user['pin_hash'])) {
$_SESSION['failed_pin_attempts'] = 0;
return ['success' => true];
} else {
$_SESSION['failed_pin_attempts'] += 1;
if ($_SESSION['failed_pin_attempts'] >= 4) {
$blockReason = 'Account locked due to multiple incorrect PIN attempts.';
$pdo->prepare("UPDATE users SET access_status = 'blocked', block_reason = ? WHERE id = ?")->execute([$blockReason, $userId]);
$_SESSION['failed_pin_attempts'] = 0;
return ['success' => false, 'message' => 'blocked'];
} else {
$attemptsLeft = 4 - $_SESSION['failed_pin_attempts'];
return ['success' => false, 'message' => "Incorrect PIN. You have $attemptsLeft attempt(s) left."];
}
}
}
// --- A. RESOLVE INTERNAL ACCOUNT NAME ---
if ($_POST['ajax_action'] === 'resolve_account') {
$accNum = htmlspecialchars(trim($_POST['account_number']));
$stmt = $pdo->prepare("SELECT u.first_name, u.last_name FROM accounts a JOIN users u ON a.user_id = u.id WHERE a.account_number = ? LIMIT 1");
$stmt->execute([$accNum]);
$res = $stmt->fetch(PDO::FETCH_ASSOC);
if ($res) { echo json_encode(['success' => true, 'name' => $res['first_name'] . ' ' . $res['last_name']]); }
else { echo json_encode(['success' => false]); }
exit;
}
// --- B. VERIFY PIN ONLY (Step 1 of Transfer OR View Card Details) ---
if ($_POST['ajax_action'] === 'verify_pin_only' || $_POST['ajax_action'] === 'verify_card_pin') {
$inputPin = trim($_POST['pin']);
try {
$pdo->beginTransaction();
$pinCheck = verifyPinOrBlock($pdo, $userId, $inputPin);
$pdo->commit();
if ($pinCheck['success']) {
if ($_POST['ajax_action'] === 'verify_card_pin') {
// Return card details if it was a card verification request
echo json_encode([
'success' => true,
'card_number' => '4231 5678 9012 3061',
'cvv' => '482',
'expiry' => '11/28'
]);
} else {
// Just return success for transfer PIN check
echo json_encode(['success' => true]);
}
} else {
if ($pinCheck['message'] === 'blocked') {
session_destroy();
echo json_encode(['success' => false, 'action' => 'redirect', 'message' => 'Account blocked due to security reasons.']);
} else {
echo json_encode(['success' => false, 'message' => $pinCheck['message']]);
}
}
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Server error']);
}
exit;
}
// --- C. CHANGE CARD PIN ---
if ($_POST['ajax_action'] === 'change_card_pin') {
$currentPin = trim($_POST['current_pin']);
$newPin = trim($_POST['new_pin']);
if (strlen($newPin) < 4) {
echo json_encode(['success' => false, 'message' => 'New PIN must be at least 4 digits.']);
exit;
}
try {
$pdo->beginTransaction();
$pinCheck = verifyPinOrBlock($pdo, $userId, $currentPin);
if ($pinCheck['success']) {
$hashedNewPin = password_hash($newPin, PASSWORD_DEFAULT);
$updateStmt = $pdo->prepare("UPDATE users SET pin_hash = :new_pin WHERE id = :id");
$updateStmt->execute([':new_pin' => $hashedNewPin, ':id' => $userId]);
$pdo->commit();
echo json_encode(['success' => true, 'message' => 'PIN changed successfully.']);
} else {
$pdo->commit();
if ($pinCheck['message'] === 'blocked') {
session_destroy();
echo json_encode(['success' => false, 'action' => 'redirect', 'message' => 'Account blocked due to security reasons.']);
} else {
echo json_encode(['success' => false, 'message' => $pinCheck['message']]);
}
}
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Server error']);
}
exit;
}
// --- D. PROCESS TRANSFER WITH OTP (Step 2 of Transfer) ---
if ($_POST['ajax_action'] === 'process_transfer_with_otp') {
// Block transfer if KYC is not verified
if ($_SESSION['kyc_status'] !== 'verified') {
echo json_encode(['success' => false, 'message' => 'Your account is under review. Transfers are disabled.']);
exit;
}
$amount = filter_input(INPUT_POST, 'amount', FILTER_VALIDATE_FLOAT);
$bankName = htmlspecialchars(trim($_POST['bank_name']));
$accNumber = htmlspecialchars(trim($_POST['account_number']));
$accName = htmlspecialchars(trim($_POST['account_name']));
$transferType = htmlspecialchars($_POST['transfer_type']);
$otpInput = trim($_POST['otp']);
if (!$amount || $amount <= 0 || empty($bankName) || empty($accNumber) || empty($accName) || empty($otpInput)) {
echo json_encode(['success' => false, 'message' => 'Missing transfer details or OTP.']);
exit;
}
try {
$pdo->beginTransaction();
// 1. VERIFY OTP FROM DATABASE
$stmtOtp = $pdo->prepare("SELECT otp FROM users WHERE id = :id LIMIT 1");
$stmtOtp->execute([':id' => $userId]);
$userOtpRow = $stmtOtp->fetch(PDO::FETCH_ASSOC);
if (!$userOtpRow || $otpInput !== $userOtpRow['otp']) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => 'Invalid OTP code provided.']);
exit;
}
// 2. Fetch sender account
$stmtAcc = $pdo->prepare("
SELECT a.id, a.balance, a.account_number, u.email, u.first_name, u.last_name
FROM accounts a
JOIN users u ON a.user_id = u.id
WHERE a.user_id = ? AND a.account_type IN ('savings', 'current')
LIMIT 1 FOR UPDATE
");
$stmtAcc->execute([$userId]);
$sender = $stmtAcc->fetch(PDO::FETCH_ASSOC);
if (!$sender) throw new Exception("Active account not found.");
if ($sender['balance'] < $amount) throw new Exception("Insufficient funds. Your available balance is $" . number_format($sender['balance'], 2));
// 3. Deduct
$newBalance = $sender['balance'] - $amount;
$stmtUpdate = $pdo->prepare("UPDATE accounts SET balance = ? WHERE id = ?");
$stmtUpdate->execute([$newBalance, $sender['id']]);
// 4. Log Tx - REAL TIME
$realTimeDb = date('Y-m-d H:i:s');
$realTimeReceipt = date('F d, Y - h:i A');
$txIdStr = "TXN" . strtoupper(substr(uniqid(), -8));
$description = ($transferType === 'wire' ? "Wire Transfer to " : "Transfer to ") . $accName . " (" . $bankName . ")";
$stmtTx = $pdo->prepare("INSERT INTO transactions (account_id, transaction_type, amount, description, status, created_at) VALUES (?, 'debit', ?, ?, 'completed', ?)");
$stmtTx->execute([$sender['id'], $amount, $description, $realTimeDb]);
// 5. Internal Transfer Check (Only if local)
if ($transferType === 'local' && stripos($bankName, 'oceanic') !== false) {
$stmtCheckRec = $pdo->prepare("SELECT id, balance FROM accounts WHERE account_number = ? LIMIT 1 FOR UPDATE");
$stmtCheckRec->execute([$accNumber]);
$receiver = $stmtCheckRec->fetch(PDO::FETCH_ASSOC);
if ($receiver && $receiver['id'] !== $sender['id']) { // Ensure not sending to self
$recNewBalance = $receiver['balance'] + $amount;
$stmtUpdateRec = $pdo->prepare("UPDATE accounts SET balance = ? WHERE id = ?");
$stmtUpdateRec->execute([$recNewBalance, $receiver['id']]);
$recDesc = "Transfer from " . $sender['first_name'] . " " . $sender['last_name'];
$stmtTxRec = $pdo->prepare("INSERT INTO transactions (account_id, transaction_type, amount, description, status, created_at) VALUES (?, 'credit', ?, ?, 'completed', ?)");
$stmtTxRec->execute([$receiver['id'], $amount, $recDesc, $realTimeDb]);
}
}
// 6. Send email alert
$to = $sender['email'];
$subject = "Debit Alert: [$txIdStr] Successful Transfer";
$headers = "MIME-Version: 1.0\r\nContent-type: text/html; charset=UTF-8\r\nFrom: Capitalvalts Support <no-reply@Capitalvalts.com>\r\n";
$emailTemplate = "
<div style='font-family: Arial, sans-serif; max-width: 600px; margin: auto; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden;'>
<div style='background-color: #0ea5e9; padding: 20px; text-align: center; color: white;'><h2>Capitalvalts Alerts</h2></div>
<div style='padding: 20px; color: #334155;'>
<p>Dear {$sender['first_name']},</p>
<p>This is to inform you that a debit transaction has occurred on your account.</p>
<table style='width: 100%; border-collapse: collapse; margin-top: 20px;'>
<tr style='background: #f8fafc;'><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Amount:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0; color: #ef4444;'><strong>-$" . number_format($amount, 2) . "</strong></td></tr>
<tr><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Account Name:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0;'>$accName</td></tr>
<tr style='background: #f8fafc;'><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Bank:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0;'>$bankName</td></tr>
<tr><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Account Number:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0;'>$accNumber</td></tr>
<tr style='background: #f8fafc;'><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Transaction Ref:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0;'>$txIdStr</td></tr>
<tr><td style='padding: 10px; border: 1px solid #e2e8f0;'><strong>Available Balance:</strong></td><td style='padding: 10px; border: 1px solid #e2e8f0;'>$" . number_format($newBalance, 2) . "</td></tr>
</table>
</div>
</div>";
@mail($to, $subject, $emailTemplate, $headers);
$pdo->commit();
echo json_encode([
'success' => true,
'new_balance' => number_format($newBalance, 2),
'receipt' => [
'tx_id' => $txIdStr,
'date' => $realTimeReceipt,
'amount' => number_format($amount, 2),
'recipient' => $accName,
'bank' => $bankName,
'acc_num' => $accNumber
]
]);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
exit;
}
// --- E. MARK NOTIFICATIONS AS READ ---
if ($_POST['ajax_action'] === 'mark_notifications_read') {
try {
$stmtUpdateNotif = $pdo->prepare("UPDATE notifications SET is_read = 1 WHERE user_id = :id AND is_read = 0");
$stmtUpdateNotif->execute([':id' => $userId]);
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
}
exit;
}
}
// 3. LIVE QUERY: Fetch User Status
$stmt = $pdo->prepare("SELECT first_name, last_name, kyc_status, access_status, block_reason FROM users WHERE id = :id LIMIT 1");
$stmt->execute([':id' => $_SESSION['user_id']]);
$userRow = $stmt->fetch(PDO::FETCH_ASSOC);
if ($userRow) {
$_SESSION['kyc_status'] = $userRow['kyc_status'];
$_SESSION['access_status'] = $userRow['access_status'] ?? 'active';
$_SESSION['block_reason'] = $userRow['block_reason'] ?? 'Please contact support.';
$_SESSION['first_name'] = $userRow['first_name'];
$_SESSION['last_name'] = $userRow['last_name'];
} else {
session_destroy();
header("Location: login.php");
exit();
}
// 4. LIVE QUERY: Fetch Sender's Account Balance & ID
$stmtAcc = $pdo->prepare("SELECT id, account_number, balance FROM accounts WHERE user_id = :id AND account_type IN ('savings', 'current') LIMIT 1");
$stmtAcc->execute([':id' => $_SESSION['user_id']]);
$accRow = $stmtAcc->fetch(PDO::FETCH_ASSOC);
$accountId = $accRow ? $accRow['id'] : null;
$fiatBalance = $accRow ? number_format($accRow['balance'], 2) : "0.00";
$accNumber = $accRow ? $accRow['account_number'] : "Pending";
$hiddenAcc = $accRow ? substr($accNumber, -4) : "****";
// 5. LIVE QUERY: Financial Insights & Recent Txs
$incomeThisMonth = 0;
$expenseThisMonth = 0;
$recentTransactions = [];
if ($accountId) {
$stmtInsights = $pdo->prepare("
SELECT transaction_type, SUM(amount) as total
FROM transactions
WHERE account_id = :acc_id
AND MONTH(created_at) = MONTH(CURRENT_DATE())
AND YEAR(created_at) = YEAR(CURRENT_DATE())
GROUP BY transaction_type
");
$stmtInsights->execute([':acc_id' => $accountId]);
while ($row = $stmtInsights->fetch(PDO::FETCH_ASSOC)) {
if ($row['transaction_type'] === 'credit') {
$incomeThisMonth = $row['total'];
} elseif ($row['transaction_type'] === 'debit') {
$expenseThisMonth = $row['total'];
}
}
$stmtTx = $pdo->prepare("SELECT id, transaction_type, amount, description, created_at, status FROM transactions WHERE account_id = :acc_id ORDER BY created_at DESC LIMIT 5");
$stmtTx->execute([':acc_id' => $accountId]);
$recentTransactions = $stmtTx->fetchAll(PDO::FETCH_ASSOC);
}
// 6. LIVE QUERY: Fetch Notifications
$notifications = [];
$unreadCount = 0;
try {
$stmtNotif = $pdo->prepare("SELECT id, title, message, type, is_read, created_at FROM notifications WHERE user_id = :id ORDER BY created_at DESC LIMIT 15");
$stmtNotif->execute([':id' => $_SESSION['user_id']]);
$notifications = $stmtNotif->fetchAll(PDO::FETCH_ASSOC);
foreach ($notifications as $notif) {
if ($notif['is_read'] == 0) { $unreadCount++; }
}
} catch (PDOException $e) { $notifications = []; }
$incomeFormatted = number_format($incomeThisMonth, 2);
$expenseFormatted = number_format($expenseThisMonth, 2);
$balanceRatio = ($incomeThisMonth > 0) ? (($expenseThisMonth / $incomeThisMonth) * 100) : 0;
$ratioFormatted = number_format($balanceRatio, 1) . '%';
$healthStatus = $balanceRatio > 80 ? 'Poor' : ($balanceRatio > 50 ? 'Fair' : 'Good');
} catch (PDOException $e) {
$_SESSION['kyc_status'] = 'pending';
$_SESSION['access_status'] = 'active';
$_SESSION['block_reason'] = '';
$fiatBalance = "0.00";
$hiddenAcc = "****";
$incomeFormatted = "0.00";
$expenseFormatted = "0.00";
$ratioFormatted = "0.0%";
$healthStatus = "N/A";
$recentTransactions = [];
$notifications = [];
$unreadCount = 0;
}
$kyc_status = $_SESSION['kyc_status'];
$access_status = $_SESSION['access_status'];
$block_reason = $_SESSION['block_reason'];
$firstName = $_SESSION['first_name'];
$lastName = $_SESSION['last_name'];
$userInitial = strtoupper(substr($firstName, 0, 1));
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Transfer Funds - Capitalvalts</title>
<style>
/* =========================================
CSS VARIABLES & RESET
========================================= */
:root {
--bg-dark: #0a0e17; --surface-dark: #131a2a; --surface-light: #1e2738;
--accent-blue: #0ea5e9; --text-main: #f8fafc; --text-muted: #94a3b8;
--danger: #ef4444; --success: #22c55e;
--card-gradient: linear-gradient(135deg, #0284c7, #0f172a);
}
* { margin: 0; padding: 0; box-sizing: border-box; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; -webkit-tap-highlight-color: transparent;}
body { background-color: var(--bg-dark); color: var(--text-main); }
.app-container { display: flex; flex-direction: column; min-height: 100vh; }
.sidebar { display: none; }
.main-content { flex: 1; padding: 20px; padding-bottom: 90px; }
.top-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; }
.page-title { font-size: 1.5rem; font-weight: 700; }
.user-greeting { display: flex; align-items: center; gap: 12px; }
.avatar { width: 40px; height: 40px; background: #fff; color: #000; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 1.2rem; }
.user-greeting p { font-size: 0.75rem; color: var(--text-muted); }
.user-greeting h2 { font-size: 1.1rem; font-weight: 600; text-transform: capitalize; }
.notifications { position: relative; background: var(--surface-dark); width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: 0.2s; }
.notifications:active { transform: scale(0.95); }
.notifications::after {
content: attr(data-count);
position: absolute; top: 0; right: 0;
background: var(--danger); color: white;
font-size: 0.6rem; width: 16px; height: 16px;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.notifications[data-count="0"]::after { display: none; }
.balance-preview-card { background: linear-gradient(135deg, #0284c7, #0f172a); border-radius: 16px; padding: 24px; margin-bottom: 24px; border: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center;}
.balance-preview-card span { font-size: 0.85rem; color: rgba(255,255,255,0.8); display: block; margin-bottom: 4px;}
.balance-preview-card h2 { font-size: 1.8rem; font-weight: 700; color: white; }
.acc-badge { background: rgba(255,255,255,0.1); padding: 6px 12px; border-radius: 8px; font-size: 0.8rem; backdrop-filter: blur(4px);}
.transfer-tabs { display: flex; gap: 8px; margin-bottom: 24px; background: var(--surface-dark); padding: 6px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); }
.tab-btn { flex: 1; text-align: center; padding: 12px; border-radius: 8px; font-size: 0.9rem; font-weight: 600; cursor: pointer; color: var(--text-muted); transition: 0.2s; }
.tab-btn.active { background: var(--surface-light); color: var(--accent-blue); box-shadow: 0 4px 6px rgba(0,0,0,0.2); }
.transfer-form-card { background: var(--surface-dark); border-radius: 16px; padding: 24px; border: 1px solid rgba(255,255,255,0.05); }
.tab-content { display: none; }
.tab-content.active { display: block; animation: fadeIn 0.3s ease; }
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
.form-group { margin-bottom: 16px; }
.form-label { display: block; font-size: 0.85rem; color: var(--text-muted); margin-bottom: 8px; }
.form-input { width: 100%; background: var(--bg-dark); border: 1px solid rgba(255,255,255,0.1); color: var(--text-main); padding: 16px; border-radius: 12px; font-size: 1rem; outline: none; transition: 0.2s; }
.form-input:focus { border-color: var(--accent-blue); }
select.form-input { appearance: none; background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right 1rem center; background-size: 1em; cursor: pointer;}
.amount-input-wrapper { position: relative; display: flex; align-items: center; background: var(--bg-dark); border-radius: 12px; padding: 0 16px; border: 1px solid rgba(255,255,255,0.1); margin-bottom: 16px;}
.amount-input-wrapper span { font-size: 1.5rem; color: var(--text-muted); }
.amount-input-wrapper input { background: transparent; border: none; color: white; font-size: 1.5rem; padding: 16px 8px; width: 100%; outline: none; }
.primary-btn { width: 100%; background: var(--accent-blue); color: white; border: none; padding: 16px; border-radius: 12px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: 0.2s; margin-top: 8px;}
.primary-btn:active { transform: scale(0.98); }
.primary-btn:disabled { opacity: 0.6; cursor: not-allowed; }
.toast { position: fixed; top: -100px; left: 50%; transform: translateX(-50%); background: var(--success); color: white; padding: 12px 24px; border-radius: 30px; font-size: 0.9rem; font-weight: 600; box-shadow: 0 4px 12px rgba(0,0,0,0.3); transition: 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); z-index: 2000; }
.toast.show { top: 40px; }
.toast.error { background: var(--danger); }
.bottom-nav { position: fixed; bottom: 0; width: 100%; background: rgba(19, 26, 42, 0.95); backdrop-filter: blur(10px); display: flex; justify-content: space-around; padding: 12px 0 24px 0; border-top: 1px solid rgba(255,255,255,0.05); z-index: 100; }
.nav-item { display: flex; flex-direction: column; align-items: center; color: var(--text-muted); text-decoration: none; font-size: 0.7rem; gap: 4px; }
.nav-item.active { color: var(--accent-blue); }
/* Dashboard specific styles embedded in transfer page (as per user request) */
.section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; margin-top: 32px; }
.section-header h3 { font-size: 1.1rem; font-weight: 600; }
.section-header a { color: var(--accent-blue); text-decoration: none; font-size: 0.85rem; }
.balance-slider { display: flex; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; -webkit-overflow-scrolling: touch; gap: 16px; padding-bottom: 10px; }
.balance-slider::-webkit-scrollbar { display: none; }
.balance-slide { min-width: 100%; scroll-snap-align: start; }
.balance-card { padding: 20px; border-radius: 20px; border: 1px solid rgba(255,255,255,0.05); height: 100%; display: flex; flex-direction: column; justify-content: space-between; min-height: 200px;}
.fiat-card { background: var(--card-gradient); }
.crypto-card { background: linear-gradient(135deg, #3f3f46, #18181b); }
.card-header { display: flex; justify-content: space-between; font-size: 0.75rem; color: rgba(255,255,255,0.7); margin-bottom: 16px; text-transform: uppercase; letter-spacing: 0.5px; }
.balance-card h1 { font-size: 2.2rem; font-weight: 700; margin-bottom: 4px; text-align: center; }
.sub-balance { text-align: center; font-size: 0.85rem; color: rgba(255,255,255,0.7); margin-bottom: 16px; }
.card-footer { display: flex; justify-content: space-between; align-items: center; font-size: 0.75rem; }
.status { background: rgba(255,255,255,0.1); padding: 4px 10px; border-radius: 20px; color: var(--success); display: flex; align-items: center; gap: 4px; }
.status.neutral { color: var(--text-main); }
.swipe-indicators { display: flex; justify-content: center; gap: 6px; margin-top: 8px; }
.dot { width: 6px; height: 6px; background: rgba(255,255,255,0.2); border-radius: 50%; transition: 0.3s; }
.dot.active { background: var(--text-main); width: 16px; border-radius: 4px; }
.swipe-hint { text-align: center; font-size: 0.75rem; color: var(--text-muted); margin-top: 12px; }
.quick-actions { display: flex; justify-content: space-between; padding: 0 10px; }
.action-btn { background: none; border: none; color: var(--text-main); display: flex; flex-direction: column; align-items: center; gap: 8px; cursor: pointer; font-size: 0.8rem; }
.action-btn .icon { width: 50px; height: 50px; background: var(--surface-light); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; transition: 0.2s; }
.action-btn:active .icon { transform: scale(0.95); }
.icon.yellow { color: var(--accent-yellow); font-size: 1.5rem; }
.quick-transfer-list { display: flex; gap: 16px; overflow-x: auto; scrollbar-width: none; padding-bottom: 8px; }
.quick-transfer-list::-webkit-scrollbar { display: none; }
.transfer-item { display: flex; flex-direction: column; align-items: center; gap: 8px; font-size: 0.75rem; color: var(--text-muted); cursor: pointer; }
.add-new-circle { width: 48px; height: 48px; border-radius: 50%; border: 1px dashed var(--text-muted); display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: var(--text-muted); transition: 0.2s; }
.transfer-item:active .add-new-circle { transform: scale(0.95); background: rgba(255,255,255,0.05); }
.no-saved { display: flex; align-items: center; height: 48px; padding: 0 16px; background: var(--surface-dark); border-radius: 24px; white-space: nowrap; }
.credit-card { background: var(--card-gradient); border-radius: 16px; padding: 20px; position: relative; overflow: hidden; border: 1px solid rgba(255,255,255,0.1); margin-bottom: 12px; }
.credit-card::after { content: ''; position: absolute; top: -50px; right: -50px; width: 150px; height: 150px; background: rgba(255,255,255,0.05); border-radius: 50%; }
.cc-top { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 24px; }
.cc-top h4 { font-size: 1.1rem; }
.cc-top p { font-size: 0.7rem; color: rgba(255,255,255,0.7); }
.cc-chip { width: 40px; height: 30px; background: linear-gradient(135deg, #eab308, #ca8a04); border-radius: 6px; }
.cc-number { font-size: 1.4rem; letter-spacing: 2px; margin-bottom: 24px; font-family: monospace; }
.cc-bottom { display: flex; justify-content: space-between; font-size: 0.7rem; color: rgba(255,255,255,0.7); }
.cc-bottom h5 { font-size: 0.9rem; color: white; margin-top: 4px; text-transform: uppercase; }
.manage-card-btn { width: 100%; background: var(--surface-dark); border: 1px solid rgba(255,255,255,0.05); padding: 12px; border-radius: 12px; color: var(--text-main); cursor: pointer; font-weight: 600; transition: 0.2s;}
.manage-card-btn:active { transform: scale(0.98); }
.services-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.service-card { background: var(--surface-dark); padding: 16px; border-radius: 16px; border: 1px solid rgba(255,255,255,0.02); }
.service-card .icon-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.service-card h4 { font-size: 0.95rem; margin-bottom: 4px; }
.badge { font-size: 0.7rem; padding: 2px 6px; border-radius: 4px; background: rgba(255,255,255,0.05); }
.badge.available { color: var(--success); }
.badge.rejected { color: var(--danger); }
.service-btn { width: 100%; padding: 8px; border-radius: 8px; border: none; background: rgba(14, 165, 233, 0.1); color: var(--accent-blue); margin-top: 12px; font-weight: 600; cursor: pointer; transition: 0.2s;}
.service-btn.green { background: rgba(34, 197, 94, 0.1); color: var(--success); }
.insight-card { background: var(--surface-dark); border-radius: 16px; padding: 16px; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; border: 1px solid rgba(255,255,255,0.02); }
.insight-card h4 { font-size: 0.9rem; color: var(--text-muted); margin-bottom: 4px;}
.insight-card h2 { font-size: 1.2rem; }
.insight-card .ratio { text-align: right; }
.insight-card .ratio h2 { color: var(--accent-blue); }
.income-expense-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px;}
.ie-card { background: var(--surface-dark); padding: 16px; border-radius: 16px; text-align: center; }
.ie-card p { font-size: 0.8rem; color: var(--text-muted); margin-bottom: 8px; }
.ie-card h3 { font-size: 1.1rem; }
.ie-card.income h3 { color: var(--success); }
.ie-card.expense h3 { color: var(--danger); }
.tip-card { background: rgba(14, 165, 233, 0.1); border-left: 4px solid var(--accent-blue); padding: 12px 16px; border-radius: 8px; font-size: 0.8rem; display: flex; align-items: center; gap: 12px; }
.activity-item { display: flex; align-items: center; justify-content: space-between; padding: 16px; background: var(--surface-dark); border-radius: 12px; margin-bottom: 8px; border: 1px solid rgba(255,255,255,0.02); }
.activity-left { display: flex; align-items: center; gap: 12px; }
.tx-icon { width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.2rem; }
.tx-icon.in { background: rgba(34, 197, 94, 0.1); color: var(--success); }
.tx-icon.out { background: rgba(239, 68, 68, 0.1); color: var(--danger); }
.tx-icon.achievement { background: rgba(234, 179, 8, 0.1); color: var(--accent-yellow); }
.activity-details h4 { font-size: 0.9rem; margin-bottom: 2px; }
.activity-details p { font-size: 0.75rem; color: var(--text-muted); }
.activity-right { text-align: right; }
.activity-right a { font-size: 0.75rem; color: var(--accent-blue); text-decoration: none; }
.help-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 24px; }
.help-card { background: var(--surface-dark); padding: 16px; border-radius: 16px; text-align: center; border: 1px solid rgba(255,255,255,0.02); cursor: pointer;}
.help-card .icon { font-size: 1.5rem; margin-bottom: 8px; }
.help-card h4 { font-size: 0.9rem; margin-bottom: 4px; }
.help-card p { font-size: 0.7rem; color: var(--text-muted); }
/* MODALS */
.modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); backdrop-filter: blur(4px); display: flex; align-items: flex-end; justify-content: center; opacity: 0; pointer-events: none; transition: 0.3s ease-in-out; z-index: 1000; }
.modal-overlay.active { opacity: 1; pointer-events: all; }
.modal-container { background: var(--surface-dark); width: 100%; max-width: 500px; border-radius: 24px 24px 0 0; padding: 24px; transform: translateY(100%); transition: 0.3s ease-in-out; border-top: 1px solid rgba(255,255,255,0.05); max-height: 90vh; overflow-y: auto;}
.modal-overlay.active .modal-container { transform: translateY(0); }
.modal-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
.modal-header h3 { font-size: 1.2rem; }
.close-btn { background: rgba(255,255,255,0.1); border: none; color: white; width: 30px; height: 30px; border-radius: 50%; font-size: 1.2rem; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.modal-subtitle { color: var(--text-muted); font-size: 0.85rem; margin-bottom: 24px; }
.list-option { background: var(--surface-light); border-radius: 16px; padding: 16px; display: flex; align-items: center; gap: 16px; margin-bottom: 12px; cursor: pointer; border: 1px solid transparent; transition: 0.2s;}
.list-option:active { transform: scale(0.98); background: rgba(255,255,255,0.05); }
.option-icon { font-size: 1.5rem; }
.option-details h4 { font-size: 1rem; margin-bottom: 4px; }
.option-details p { font-size: 0.75rem; color: var(--text-muted); }
.arrow { margin-left: auto; color: var(--text-muted); }
.bank-details-card { background: var(--surface-light); border-radius: 16px; padding: 20px; margin-bottom: 24px; border: 1px solid rgba(255,255,255,0.05); }
.bank-details-card h4 { font-size: 1rem; margin-bottom: 16px; color: var(--accent-blue); display: flex; align-items: center; gap: 8px; }
.detail-row { display: flex; justify-content: space-between; align-items: center; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.detail-row:last-child { border-bottom: none; padding-bottom: 0; }
.detail-row span { font-size: 0.8rem; color: var(--text-muted); }
.detail-row strong { font-size: 0.9rem; display: flex; align-items: center; gap: 8px; }
.copy-icon-btn { background: rgba(255,255,255,0.1); border: none; color: var(--text-main); padding: 4px 8px; border-radius: 6px; cursor: pointer; font-size: 0.7rem; }
.toggle-switch { position: relative; display: inline-block; width: 50px; height: 28px; }
.toggle-switch input { opacity: 0; width: 0; height: 0; }
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(255,255,255,0.1); transition: .4s; border-radius: 34px; }
.slider:before { position: absolute; content: ""; height: 20px; width: 20px; left: 4px; bottom: 4px; background-color: white; transition: .4s; border-radius: 50%; }
input:checked + .slider { background-color: var(--danger); }
input:checked + .slider:before { transform: translateX(22px); }
.crypto-selector { display: flex; flex-direction: column; gap: 10px; }
.crypto-radio { display: flex; align-items: center; justify-content: space-between; padding: 16px; background: var(--surface-light); border-radius: 12px; border: 1px solid rgba(255,255,255,0.05); cursor: pointer; }
.crypto-radio.selected { border-color: var(--accent-blue); background: rgba(14, 165, 233, 0.1); }
.quick-amounts { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 8px; scrollbar-width: none;}
.quick-amounts::-webkit-scrollbar { display: none; }
.chip { background: var(--surface-light); padding: 8px 16px; border-radius: 20px; font-size: 0.85rem; border: 1px solid rgba(255,255,255,0.05); cursor: pointer; white-space: nowrap; }
.qr-container { background: white; padding: 16px; border-radius: 16px; width: 200px; height: 200px; margin: 0 auto 24px auto; display: flex; align-items: center; justify-content: center; }
.qr-placeholder { border: 4px dashed #ccc; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #999; text-align: center; font-size: 0.8rem;}
.address-box { background: var(--bg-dark); padding: 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: space-between; word-break: break-all; gap: 12px;}
.address-text { font-size: 0.8rem; color: var(--text-muted); }
/* Receipt Modal Styles */
.receipt-modal .modal-container { border-radius: 24px; top: 5%; bottom: auto; align-self: center; transform: scale(0.9); }
.modal-overlay.active .receipt-modal .modal-container { transform: scale(1); }
.receipt-header { text-align: center; border-bottom: 1px dashed rgba(255,255,255,0.1); padding-bottom: 20px; margin-bottom: 20px; }
.success-circle { width: 70px; height: 70px; border-radius: 50%; background: rgba(34, 197, 94, 0.1); border: 2px solid var(--success); display: flex; align-items: center; justify-content: center; margin: 0 auto 16px auto; animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); }
.success-circle svg { width: 35px; height: 35px; stroke: var(--success); stroke-width: 3; fill: none; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 100; stroke-dashoffset: 100; animation: drawCheck 0.6s 0.3s forwards; }
@keyframes popIn { 0% { transform: scale(0); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
@keyframes drawCheck { 100% { stroke-dashoffset: 0; } }
.receipt-amount { font-size: 2.5rem; font-weight: bold; margin-bottom: 8px; color: var(--text-main); }
.receipt-row { display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.03); }
.receipt-row span { color: var(--text-muted); font-size: 0.85rem; }
.receipt-row strong { font-size: 0.9rem; text-align: right; }
.btn-print { background: transparent; border: 1px solid var(--accent-blue); color: var(--accent-blue); }
@media print {
body * { visibility: hidden; }
#receiptModal, #receiptModal * { visibility: visible; }
#receiptModal { position: absolute; left: 0; top: 0; width: 100%; height: 100%; align-items: flex-start; background: white;}
#receiptModal .modal-container { background: white; color: black; box-shadow: none; max-width: 100%; }
.receipt-row span { color: #555; }
.no-print { display: none !important; }
}
@media (min-width: 1024px) {
.app-container { flex-direction: row; }
.bottom-nav { display: none; }
.sidebar { display: flex; flex-direction: column; width: 260px; background: var(--surface-dark); border-right: 1px solid rgba(255,255,255,0.05); padding: 32px 24px; height: 100vh; position: sticky; top: 0; }
.sidebar .logo { font-size: 1.5rem; font-weight: bold; margin-bottom: 48px; color: var(--accent-blue); }
.side-nav { display: flex; flex-direction: column; gap: 12px; }
.side-nav a { color: var(--text-muted); text-decoration: none; padding: 12px 16px; border-radius: 12px; transition: 0.2s; display: flex; align-items: center; gap: 12px; }
.side-nav a:hover, .side-nav a.active { background: rgba(14, 165, 233, 0.1); color: var(--accent-blue); }
.main-content { padding: 40px 60px; max-width: 800px; margin: 0 auto; }
.modal-overlay { align-items: center; }
.modal-container { border-radius: 24px; border: 1px solid rgba(255,255,255,0.1); }
}
</style>
</head>
<body>
<?php if ($access_status === 'blocked' || $kyc_status === 'pending' || $kyc_status === 'rejected'): ?>
<style>
body { overflow: hidden !important; }
.restriction-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(10, 14, 23, 0.85); backdrop-filter: blur(12px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; }
.restriction-box { background: var(--surface-dark); border: 1px solid rgba(255,255,255,0.1); padding: 40px; border-radius: 24px; text-align: center; max-width: 450px; box-shadow: 0 20px 50px rgba(0,0,0,0.8); }
.restriction-icon { font-size: 3rem; margin-bottom: 16px; }
.restriction-box h2 { margin-bottom: 12px; color: var(--text-main); font-size: 1.5rem;}
.restriction-box p { color: var(--text-muted); font-size: 0.95rem; line-height: 1.6; margin-bottom: 24px;}
.logout-btn-large { display: inline-block; width: 100%; background: rgba(239, 68, 68, 0.1); color: var(--danger); border: 1px solid rgba(239, 68, 68, 0.3); padding: 16px; border-radius: 12px; text-decoration: none; font-weight: 600; transition: 0.2s; }
.logout-btn-large:active { transform: scale(0.98); background: rgba(239, 68, 68, 0.2); }
</style>
<div class="restriction-overlay">
<div class="restriction-box">
<?php if ($access_status === 'blocked'): ?>
<div class="restriction-icon">🔒</div>
<h2>Account Suspended</h2>
<p><?php echo htmlspecialchars($block_reason); ?></p>
<?php elseif ($kyc_status === 'pending'): ?>
<div class="restriction-icon">⏳</div>
<h2>Transfers Locked</h2>
<p>Welcome, <?php echo htmlspecialchars($firstName); ?>. Your account is currently under review by our compliance team. Outbound transfers are disabled until verification is complete.</p>
<?php else: ?>
<div class="restriction-icon">🚫</div>
<h2>Account Restricted</h2>
<p>Your application could not be verified. Transfers are permanently locked.</p>
<?php endif; ?>
<a href="logout.php" class="logout-btn-large">Sign Out</a>
</div>
</div>
<?php endif; ?>
<div id="toast" class="toast"></div>
<div class="app-container">
<aside class="sidebar">
<div class="logo">Capitalvalts</div>
<nav class="side-nav">
<a href="index.php"><span>🏠</span> Home</a>
<a href="activity.php"><span>📊</span> Activity</a>
<a href="transfer.php" class="active"><span>💸</span> Transfer</a>
<a href="cards.php"><span>💳</span> Cards</a>
<a href="profile.php"><span>👤</span> Profile</a>
</nav>
</aside>
<main class="main-content">
<header class="top-header">
<h1 class="page-title">Send Money</h1>
<div class="notifications" id="bellIcon" data-target="notificationModal" data-count="<?php echo $unreadCount; ?>"><span>🔔</span></div>
</header>
<div class="balance-preview-card">
<div>
<span>Available Balance</span>
<h2 id="uiMainBalance">$<?php echo htmlspecialchars($fiatBalance); ?></h2>
</div>
<div class="acc-badge">Acct ****<?php echo htmlspecialchars($hiddenAcc); ?></div>
</div>
<div class="transfer-tabs">
<div class="tab-btn active" onclick="switchTab('local')">Local Transfer</div>
<div class="tab-btn" onclick="switchTab('wire')">Intl. Wire</div>
</div>
<div class="transfer-form-card">
<div id="localTab" class="tab-content active">
<form id="localTransferForm" onsubmit="initiateTransfer(event, 'local')">
<input type="hidden" name="transfer_type" value="local">
<label class="form-label">Transfer Amount</label>
<div class="amount-input-wrapper">
<span>$</span>
<input type="number" name="amount" step="0.01" min="1" placeholder="0.00" required>
</div>
<div class="form-group">
<label class="form-label">Beneficiary Bank</label>
<input type="text" name="bank_name" id="localBankInput" class="form-input" placeholder="Enter Bank Name (e.g. Capitalvalts, Chase Bank)" required>
</div>
<div class="form-group">
<label class="form-label">Account Number</label>
<input type="number" name="account_number" id="accNumberInput" class="form-input" placeholder="Enter 10-digit account number" required>
</div>
<div class="form-group">
<label class="form-label">Account Name</label>
<input type="text" name="account_name" id="accNameInput" class="form-input" placeholder="Enter account name" required>
</div>
<div class="form-group">
<label class="form-label">Transfer Description (Optional)</label>
<input type="text" name="description" class="form-input" placeholder="What is this for?">
</div>
<button type="submit" class="primary-btn">Proceed</button>
</form>
</div>
<div id="wireTab" class="tab-content">
<form id="wireTransferForm" onsubmit="initiateTransfer(event, 'wire')">
<input type="hidden" name="transfer_type" value="wire">
<label class="form-label">Transfer Amount</label>
<div class="amount-input-wrapper">
<span>$</span>
<input type="number" name="amount" step="0.01" min="100" placeholder="0.00" required>
</div>
<div class="form-group">
<label class="form-label">Recipient's Country</label>
<input type="text" name="bank_name" class="form-input" placeholder="Enter Country (e.g. United Kingdom, Canada)" required>
</div>
<div class="form-group">
<label class="form-label">SWIFT / BIC Code</label>
<input type="text" class="form-input" placeholder="e.g. BOFAUS3N" required style="text-transform: uppercase;">
</div>
<div class="form-group">
<label class="form-label">IBAN / Account Number</label>
<input type="text" name="account_number" class="form-input" placeholder="Enter IBAN" required>
</div>
<div class="form-group">
<label class="form-label">Beneficiary Full Name</label>
<input type="text" name="account_name" class="form-input" placeholder="Name on the bank account" required>
</div>
<button type="submit" class="primary-btn">Proceed</button>
</form>
</div>
</div>
<section class="active-cards-section" style="margin-top: 40px;">
<div class="section-header"><h3>Your Active Cards</h3><a href="cards.php">Manage ></a></div>
<div class="credit-card">
<div class="cc-top">
<div><h4>Capitalvalts</h4><p>Virtual Banking</p></div>
<div class="cc-chip"></div>
</div>
<div class="cc-number">**** **** **** 3061</div>
<div class="cc-bottom">
<div><p>Card Holder</p><h5><?php echo htmlspecialchars($firstName . ' ' . $lastName); ?></h5></div>
<div><p>Valid</p><h5>11/28</h5></div>
</div>
</div>
<button class="manage-card-btn" data-target="manageCardModal">Manage Card</button>
</section>
</main>
<nav class="bottom-nav">
<a href="activity.php" class="nav-item"><span class="nav-icon">📊</span>Activity</a>
<a href="transfer.php" class="nav-item active"><span class="nav-icon">💸</span>Transfer</a>
<a href="index.php" class="nav-item"><span class="nav-icon">🏠</span>Home</a>
<a href="cards.php" class="nav-item"><span class="nav-icon">💳</span>Cards</a>
<a href="profile.php" class="nav-item"><span class="nav-icon">👤</span>Profile</a>
</nav>
</div>
<div class="modal-overlay" id="notificationModal">
<div class="modal-container">
<div class="modal-header">
<h3>Notifications</h3>
<button class="close-btn">×</button>
</div>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<p class="modal-subtitle" id="unreadSubtitle" style="margin-bottom: 0;">You have <?php echo $unreadCount; ?> unread alerts</p>
<?php if ($unreadCount > 0): ?>
<button id="markReadBtn" style="background: none; border: none; color: var(--accent-blue); cursor: pointer; font-size: 0.8rem;" onclick="markAllAsRead()">Mark all read</button>
<?php endif; ?>
</div>
<div style="max-height: 400px; overflow-y: auto; padding-right: 4px;">
<?php if (empty($notifications)): ?>
<div style="text-align: center; padding: 20px 0; color: var(--text-muted);">
<div style="font-size: 2rem; margin-bottom: 8px;">📭</div>
<p>No new notifications</p>
</div>
<?php else: ?>
<?php foreach ($notifications as $notif): ?>
<?php
// Set styles based on notification type
$iconStr = '🔔';
$color = 'var(--accent-blue)';
$bg = 'rgba(14, 165, 233, 0.1)';
if ($notif['type'] == 'security') {
$iconStr = '🔐';
} elseif ($notif['type'] == 'credit') {
$iconStr = '💵';
$color = 'var(--success)';
$bg = 'rgba(34, 197, 94, 0.1)';
} elseif ($notif['type'] == 'debit') {
$iconStr = '💸';
$color = 'var(--danger)';
$bg = 'rgba(239, 68, 68, 0.1)';
}
$timeAgo = date('d M, h:i A', strtotime($notif['created_at']));
$borderLeft = $notif['is_read'] ? 'transparent' : $color;
?>
<div class="list-option notif-row" style="border-left: 3px solid <?php echo $borderLeft; ?>;">
<div class="option-icon" style="background: <?php echo $bg; ?>; color: <?php echo $color; ?>; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.2rem;"><?php echo $iconStr; ?></div>
<div class="option-details" style="flex: 1;">
<h4 style="display: flex; justify-content: space-between;"><?php echo htmlspecialchars($notif['title']); ?> <span style="font-size: 0.65rem; color: var(--text-muted); font-weight: normal;"><?php echo $timeAgo; ?></span></h4>
<p style="margin-top: 4px;"><?php echo htmlspecialchars($notif['message']); ?></p>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="modal-overlay" id="transferPinModal">
<div class="modal-container">
<div class="modal-header">
<h3 style="margin-bottom: 8px;">Security Verification</h3>
<button class="close-btn" onclick="closeModal('transferPinModal')">×</button>
</div>
<p class="modal-subtitle">Enter your Account PIN to authorize this transfer.</p>
<form onsubmit="verifyTransferPin(event)">
<div class="form-group">
<label class="form-label">Account PIN</label>
<input type="password" id="t_pin" name="pin" class="form-input" placeholder="****" required>
</div>
<button type="submit" class="primary-btn">Verify PIN</button>
</form>
</div>
</div>
<div class="modal-overlay" id="transferOtpModal">
<div class="modal-container">
<div class="modal-header">
<h3 style="margin-bottom: 8px;">OTP Verification</h3>
<button class="close-btn" onclick="closeModal('transferOtpModal')">×</button>
</div>
<p class="modal-subtitle">An OTP code has been sent to your email. Enter it to complete the transaction.</p>
<form onsubmit="processFinalTransfer(event)">
<div class="form-group">
<label class="form-label">One-Time Password (OTP)</label>
<input type="text" id="t_otp" name="otp" class="form-input" placeholder="Enter 6-digit OTP" required>
</div>
<button type="submit" class="primary-btn">Complete Transfer</button>
</form>
</div>
</div>
<div class="modal-overlay receipt-modal" id="receiptModal">
<div class="modal-container">
<div class="no-print" style="text-align: right; margin-bottom: -20px;">
<button class="close-btn" style="display:inline-flex;" onclick="closeModal('receiptModal')">×</button>
</div>
<div class="receipt-header">
<div class="success-circle">
<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"></polyline></svg>
</div>
<h2 style="color: var(--success); margin-bottom: 4px;">Transfer Successful</h2>
<p style="color: var(--text-muted); font-size: 0.85rem;">Your transaction has been processed.</p>
</div>
<div style="text-align: center; margin-bottom: 24px;">
<div class="receipt-amount" id="rcptAmt">$0.00</div>
<span style="background: rgba(34, 197, 94, 0.1); color: var(--success); padding: 4px 12px; border-radius: 12px; font-size: 0.75rem; font-weight: bold;">COMPLETED</span>
</div>
<div style="background: rgba(255,255,255,0.02); border: 1px solid rgba(255,255,255,0.05); padding: 16px; border-radius: 12px; margin-bottom: 24px;">
<div class="receipt-row"><span>Date & Time</span><strong id="rcptDate">--</strong></div>
<div class="receipt-row"><span>Transaction Ref</span><strong id="rcptTxId">--</strong></div>
<div class="receipt-row"><span>Beneficiary</span><strong id="rcptName">--</strong></div>
<div class="receipt-row"><span>Bank Name / Country</span><strong id="rcptBank">--</strong></div>
<div class="receipt-row" style="border:none;"><span>Account Number</span><strong id="rcptAcc">--</strong></div>
</div>
<div class="no-print" style="display: flex; gap: 12px;">
<button class="primary-btn btn-print" style="margin-top:0;" onclick="window.print()">Print Receipt</button>
<button class="primary-btn" style="margin-top:0;" onclick="window.location.reload()">Done</button>
</div>
</div>
</div>
<div class="modal-overlay" id="manageCardModal">
<div class="modal-container">
<div class="modal-header"><h3>Card Settings</h3><button class="close-btn">×</button></div>
<p class="modal-subtitle">Manage your virtual Capitalvalts card.</p>
<div class="credit-card" style="margin-bottom: 24px;">
<div class="cc-top">
<div><h4>Capitalvalts</h4><p>Virtual Banking</p></div>
<div class="cc-chip"></div>
</div>
<div class="cc-number">**** **** **** 3061</div>
<div class="cc-bottom">
<div><p>Card Holder</p><h5><?php echo htmlspecialchars($firstName . ' ' . $lastName); ?></h5></div>
<div><p>Valid</p><h5>11/28</h5></div>
</div>
</div>
<div class="detail-row" style="padding: 16px 0;">
<div>
<strong style="color: white; font-size: 1rem;">Freeze Card</strong>
<p style="font-size: 0.75rem; color: var(--text-muted); margin-top: 4px;">Temporarily disable all transactions</p>
</div>
<label class="toggle-switch">
<input type="checkbox" onchange="showToast(this.checked ? 'Card Frozen ❄️' : 'Card Unfrozen ✅')">
<span class="slider"></span>
</label>
</div>
<div class="list-option" style="margin-top: 16px;" onclick="switchModal('manageCardModal', 'verifyPinModal')">
<div class="option-icon" style="font-size: 1.2rem;">👁️</div>
<div class="option-details"><h4>View Card Details</h4><p>Reveal CVV and full card number</p></div>
</div>
<div class="list-option" onclick="switchModal('manageCardModal', 'changePinModal')">
<div class="option-icon" style="font-size: 1.2rem;">🔢</div>
<div class="option-details"><h4>Change PIN</h4><p>Set a new card PIN</p></div>
</div>
</div>
</div>
<div class="modal-overlay" id="verifyPinModal">
<div class="modal-container">
<div class="modal-header">
<button style="background:none; border:none; color:var(--accent-blue); font-size:1rem; cursor:pointer;" onclick="switchModal('verifyPinModal', 'manageCardModal')">← Back</button>
<button class="close-btn" onclick="closeModal('verifyPinModal')">×</button>
</div>
<h3 style="margin-top: 16px; margin-bottom: 8px;">Security Check</h3>
<p class="modal-subtitle">Enter your PIN to view sensitive card details.</p>
<form onsubmit="verifyCardPin(event)">
<div class="form-group">
<label class="form-label">Enter Account PIN</label>
<input type="password" name="pin" class="form-input" placeholder="****" required>
</div>
<button type="submit" class="primary-btn">Verify & View</button>
</form>
</div>
</div>
<div class="modal-overlay" id="viewCardDetailsModal">
<div class="modal-container">
<div class="modal-header">
<h3>Card Details</h3>
<button class="close-btn" onclick="closeModal('viewCardDetailsModal')">×</button>
</div>
<p class="modal-subtitle">Your sensitive card information.</p>
<div class="bank-details-card">
<div class="detail-row">
<span>Card Number</span>
<strong id="fullCardNumber">....</strong>
</div>
<div class="detail-row">
<span>Expiry Date</span>
<strong id="cardExpiry">....</strong>
</div>
<div class="detail-row" style="border:none;">
<span>CVV Code</span>
<strong id="cardCvv">....</strong>
</div>
</div>
<button class="primary-btn" style="margin-top: 0;" onclick="closeModal('viewCardDetailsModal')">Done</button>
</div>
</div>
<div class="modal-overlay" id="changePinModal">
<div class="modal-container">
<div class="modal-header">
<button style="background:none; border:none; color:var(--accent-blue); font-size:1rem; cursor:pointer;" onclick="switchModal('changePinModal', 'manageCardModal')">← Back</button>
<button class="close-btn" onclick="closeModal('changePinModal')">×</button>
</div>
<h3 style="margin-top: 16px; margin-bottom: 8px;">Change PIN</h3>
<p class="modal-subtitle">Enter your current PIN to set a new one.</p>
<form onsubmit="changeCardPin(event)">
<div class="form-group">
<label class="form-label">Current PIN</label>
<input type="password" name="current_pin" class="form-input" placeholder="****" required>
</div>
<div class="form-group">
<label class="form-label">New PIN</label>
<input type="password" name="new_pin" class="form-input" placeholder="****" required>
</div>
<button type="submit" class="primary-btn">Update PIN</button>
</form>
</div>
</div>
<script>
// General Utilities
function showToast(message, isError = false) {
const toast = document.getElementById('toast');
toast.textContent = message;
toast.className = 'toast show ' + (isError ? 'error' : '');
setTimeout(() => { toast.classList.remove('show'); }, 4000);
}
function closeModal(id) {
document.getElementById(id).classList.remove('active');
}
function switchModal(closeId, openId) {
document.getElementById(closeId).classList.remove('active');
document.getElementById(openId).classList.add('active');
}
// Tab Switching Logic
function switchTab(tabId) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
event.target.classList.add('active');
document.getElementById(tabId + 'Tab').classList.add('active');
}
// --- REAL-TIME ACCOUNT NAME RESOLUTION (AJAX) ---
const localAccInput = document.getElementById('accNumberInput');
const localNameInput = document.getElementById('accNameInput');
const localBankInput = document.getElementById('localBankInput');
if(localAccInput && localNameInput && localBankInput) {
localAccInput.addEventListener('input', (e) => {
const val = e.target.value;
const bankVal = localBankInput.value.toLowerCase();
if (val.length >= 10 && bankVal.includes('oceanic')) {
localNameInput.value = "Resolving name from database...";
localNameInput.style.color = "var(--text-muted)";
localNameInput.style.background = "rgba(255,255,255,0.02)";
localNameInput.readOnly = true;
const fd = new FormData();
fd.append('ajax_action', 'resolve_account');
fd.append('account_number', val);
fetch(window.location.href, { method: 'POST', body: fd })
.then(r => r.json())
.then(data => {
if(data.success) {
localNameInput.value = data.name;
localNameInput.style.color = "var(--success)";
} else {
localNameInput.value = "";
localNameInput.placeholder = "Account not found. Enter manually.";
localNameInput.style.color = "var(--text-main)";
localNameInput.readOnly = false;
localNameInput.style.background = "var(--bg-dark)";
}
})
.catch(() => {
localNameInput.value = "";
localNameInput.style.color = "var(--text-main)";
localNameInput.readOnly = false;
localNameInput.style.background = "var(--bg-dark)";
});
} else if (val.length >= 10) {
// If not internal bank, allow manual entry
localNameInput.readOnly = false;
localNameInput.style.background = "var(--bg-dark)";
localNameInput.style.color = "var(--text-main)";
}
});
}
// --- STEP 1: INITIATE TRANSFER (Save Data & Open PIN Modal) ---
let pendingTransferData = {};
function initiateTransfer(event, type) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
// Store form data globally to be used later
pendingTransferData = {
amount: formData.get('amount'),
bank_name: formData.get('bank_name'),
account_number: formData.get('account_number'),
account_name: formData.get('account_name'),
transfer_type: formData.get('transfer_type')
};
document.getElementById('transferPinModal').classList.add('active');
}
// --- STEP 2: VERIFY PIN ---
function verifyTransferPin(event) {
event.preventDefault();
const form = event.target;
const btn = form.querySelector('button');
btn.disabled = true;
btn.innerText = "Verifying...";
const pin = document.getElementById('t_pin').value;
const fd = new FormData();
fd.append('ajax_action', 'verify_pin_only');
fd.append('pin', pin);
fetch(window.location.href, { method: 'POST', body: fd })
.then(r => r.json())
.then(data => {
if (data.success) {
// PIN is good, switch to OTP modal
switchModal('transferPinModal', 'transferOtpModal');
form.reset();
} else {
if (data.action === 'redirect') {
window.location.href = 'login.php';
} else {
showToast(data.message, true);
}
}
})
.catch(() => showToast("Network error. Please try again.", true))
.finally(() => {
btn.disabled = false;
btn.innerText = "Verify PIN";
});
}
// --- STEP 3: VERIFY OTP & PROCESS FINAL TRANSFER ---
function processFinalTransfer(event) {
event.preventDefault();
const form = event.target;
const btn = form.querySelector('button');
btn.disabled = true;
btn.innerText = "Processing...";
const otp = document.getElementById('t_otp').value;
const fd = new FormData();
fd.append('ajax_action', 'process_transfer_with_otp');
fd.append('otp', otp);
// Append all saved form data
for (let key in pendingTransferData) {
fd.append(key, pendingTransferData[key]);
}
fetch(window.location.href, { method: 'POST', body: fd })
.then(r => r.json())
.then(data => {
if(data.success) {
closeModal('transferOtpModal');
form.reset();
document.getElementById('localTransferForm').reset();
document.getElementById('wireTransferForm').reset();
// Update UI Balance live
document.getElementById('uiMainBalance').innerText = '$' + data.new_balance;
// Populate Receipt
document.getElementById('rcptAmt').innerText = '$' + data.receipt.amount;
document.getElementById('rcptDate').innerText = data.receipt.date;
document.getElementById('rcptTxId').innerText = data.receipt.tx_id;
document.getElementById('rcptName').innerText = data.receipt.recipient;
document.getElementById('rcptBank').innerText = data.receipt.bank;
document.getElementById('rcptAcc').innerText = data.receipt.acc_num;
document.getElementById('receiptModal').classList.add('active');
} else {
showToast(data.message, true);
}
})
.catch(() => showToast("Network Error: Transfer failed.", true))
.finally(() => {
btn.disabled = false;
btn.innerText = "Complete Transfer";
});
}
// --- CARD MANAGEMENT AJAX ---
function verifyCardPin(event) {
event.preventDefault();
const form = event.target;
const btn = form.querySelector('button');
btn.disabled = true;
btn.innerText = "Verifying...";
const formData = new FormData(form);
formData.append('ajax_action', 'verify_card_pin');
fetch(window.location.href, { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) {
document.getElementById('fullCardNumber').innerText = data.card_number;
document.getElementById('cardExpiry').innerText = data.expiry;
document.getElementById('cardCvv').innerText = data.cvv;
switchModal('verifyPinModal', 'viewCardDetailsModal');
form.reset();
} else {
if (data.action === 'redirect') {
window.location.href = 'login.php';
} else {
showToast(data.message, true);
}
}
})
.catch(() => showToast("Network error. Please try again.", true))
.finally(() => {
btn.disabled = false;
btn.innerText = "Verify & View";
});
}
function changeCardPin(event) {
event.preventDefault();
const form = event.target;
const btn = form.querySelector('button');
btn.disabled = true;
btn.innerText = "Updating...";
const formData = new FormData(form);
formData.append('ajax_action', 'change_card_pin');
fetch(window.location.href, { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) {
showToast(data.message);
closeModal('changePinModal');
form.reset();
} else {
if (data.action === 'redirect') {
window.location.href = 'login.php';
} else {
showToast(data.message, true);
}
}
})
.catch(() => showToast("Network error. Please try again.", true))
.finally(() => {
btn.disabled = false;
btn.innerText = "Update PIN";
});
}
// AJAX: Mark Notifications as Read
function markAllAsRead() {
const formData = new FormData();
formData.append('ajax_action', 'mark_notifications_read');
fetch(window.location.href, { method: 'POST', body: formData })
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('bellIcon').setAttribute('data-count', '0');
document.getElementById('unreadSubtitle').innerText = 'You have 0 unread alerts';
const btn = document.getElementById('markReadBtn');
if (btn) btn.style.display = 'none';
document.querySelectorAll('.notif-row').forEach(row => {
row.style.borderLeftColor = 'transparent';
});
}
})
.catch(error => console.error('Error:', error));
}
const openButtons = document.querySelectorAll('[data-target]');
openButtons.forEach(btn => {
btn.addEventListener('click', () => {
document.getElementById(btn.getAttribute('data-target')).classList.add('active');
});
});
const closeButtons = document.querySelectorAll('.close-btn');
closeButtons.forEach(btn => {
btn.addEventListener('click', (e) => { e.target.closest('.modal-overlay').classList.remove('active'); });
});
</script>
</body>
</html>
b IDATxytVսϓ22 A@IR:hCiZ[v*E:WũZA ^dQeQ @ !jZ'>gsV仿$|?g)&x-E