<?php
session_start();

/* Don't let browsers / the iOS home-screen app cache this login page, so code
   updates take effect immediately instead of showing a stale copy. */
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Expires: 0');

/* version token baked into the page so it can detect when a newer app is live */
require_once '../config/app_version.php';
$appVersion = app_version();

require_once '../config/database.php';

$error        = "";
$loginUser    = null;
$profilePhoto = null;
$customerCode = null;
$prefillLogin = htmlspecialchars($_POST["login"] ?? "");

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $login    = trim($_POST["login"] ?? "");
    $password = $_POST["password"] ?? "";
    $isAjax   = (($_POST['ajax'] ?? '') === '1') || (($_SERVER['HTTP_X_REQUESTED_WITH'] ?? '') === 'fetch');
    try {
        $colCheck = $pdo->query("SHOW COLUMNS FROM users LIKE 'username'")->fetch();
        if ($colCheck) {
            $stmt = $pdo->prepare("SELECT u.*, c.customer_code FROM users u LEFT JOIN customers c ON c.user_id=u.id WHERE (u.email=? OR u.username=?) AND u.status='active' LIMIT 1");
            $stmt->execute([$login, $login]);
        } else {
            $stmt = $pdo->prepare("SELECT u.*, c.customer_code FROM users u LEFT JOIN customers c ON c.user_id=u.id WHERE u.email=? AND u.status='active' LIMIT 1");
            $stmt->execute([$login]);
        }
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Throwable $e) { $user = null; $error = "Database error."; }

    if ($user && password_verify($password, $user["password"])) {
        $_SESSION["user_id"]       = $user["id"];
        $_SESSION["name"]          = $user["name"];
        $_SESSION["role"]          = $user["role"];
        $_SESSION["company_id"]    = null;
        $_SESSION["company_name"]  = null;
        $_SESSION["staff_role"]    = null;
        $_SESSION["profile_photo"] = $user["profile_photo"] ?? null;
        if (in_array($user["role"], ["company_admin","staff","customer"])) {
            try {
                $cs = $pdo->prepare("SELECT cs.company_id,cs.staff_role,c.name AS company_name FROM company_staff cs JOIN companies c ON c.id=cs.company_id WHERE cs.user_id=? LIMIT 1");
                $cs->execute([$user["id"]]);
                $csRow = $cs->fetch(PDO::FETCH_ASSOC);
                if ($csRow) { $_SESSION["company_id"]=$csRow["company_id"]; $_SESSION["company_name"]=$csRow["company_name"]; $_SESSION["staff_role"]=$csRow["staff_role"]; }
            } catch (Throwable $e) {}
        }
        $ua = $_SERVER["HTTP_USER_AGENT"] ?? "";
        $isMobile = (bool)preg_match('/android|iphone|ipad|ipod|blackberry|windows phone|mobile/i', $ua);

        /* Decide where to send the user, then redirect on the CLIENT side. */
        $redirectTo = null;
        switch ($user["role"]) {
            case "admin":         $redirectTo = "../admin/dashboard.php"; break;
            case "company_admin": $redirectTo = $isMobile ? "../customer/dashboard.php" : "../company/dashboard.php"; break;
            case "staff":         $redirectTo = $isMobile ? "../customer/dashboard.php" : "../customer/desktop.php"; break;
            case "partner":       $redirectTo = "../partner/dashboard.php"; break;
            case "customer":      $redirectTo = $isMobile ? "../customer/dashboard.php" : "../customer/desktop.php"; break;
            default:              $error = "Your account has no assigned role.";
        }

        if ($redirectTo) {
            if ($isAjax) {
                header('Content-Type: application/json');
                echo json_encode(['success' => true, 'redirect' => $redirectTo]);
                exit;
            }
            /* Client-side redirect (NOT an HTTP 302) so iOS 26 keeps us inside the
               installed home-screen web app instead of popping the in-app browser. */
            echo "<!doctype html><meta charset='utf-8'>";
            echo "<meta name='apple-mobile-web-app-capable' content='yes'>";
            echo "<meta name='viewport' content='width=device-width,initial-scale=1,viewport-fit=cover'>";
            echo "<script>location.replace(" . json_encode($redirectTo) . ");</script>";
            echo "<noscript><a href='" . htmlspecialchars($redirectTo, ENT_QUOTES) . "'>Continue</a></noscript>";
            exit;
        }
    } elseif (empty($error)) {
        $error = "Invalid username/email or password.";
        try {
            $st = $pdo->prepare("SELECT u.name,u.profile_photo,c.customer_code FROM users u LEFT JOIN customers c ON c.user_id=u.id WHERE (u.username=? OR u.email=?) AND u.status='active' LIMIT 1");
            $st->execute([$login,$login]);
            $loginUser = $st->fetch(PDO::FETCH_ASSOC);
            $profilePhoto = $loginUser["profile_photo"] ?? null;
            $customerCode = $loginUser["customer_code"] ?? null;
        } catch (Throwable $e) {}
    }

    if ($isAjax) {
        header('Content-Type: application/json');
        echo json_encode(['success' => false, 'error' => ($error !== '' ? $error : 'Login failed.')]);
        exit;
    }
}

$photoUrl    = $profilePhoto ? "/seven-credit-system/assets/profile_photos/".htmlspecialchars($profilePhoto) : null;
$displayName = $loginUser["name"] ?? null;
$qrContent   = $customerCode ?? "SEVEN-DIGITAL";

/* ── Desktop / laptop login page (served when NOT on a phone) ── */
$ua = $_SERVER["HTTP_USER_AGENT"] ?? "";
$isDesktopView = !preg_match('/android|iphone|ipad|ipod|blackberry|windows phone|mobile/i', $ua);
if ($isDesktopView):
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login — Seven Digital</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="apple-touch-icon" href="/seven-credit-system/assets/sc_logo1.png">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Lao:wght@400;600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<script src="../assets/lang.js" defer></script>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
body{font-family:"Noto Sans Lao","Inter",Arial,sans-serif;min-height:100vh;display:flex;align-items:center;justify-content:center;
     background:linear-gradient(135deg,#7f1010,#b91c1c 55%,#cf2626);padding:24px;color:#111827}
.shell{width:100%;max-width:900px;min-height:520px;background:#fff;border-radius:22px;overflow:hidden;display:grid;grid-template-columns:1fr 1fr;
       box-shadow:0 30px 80px rgba(0,0,0,.35)}
.brand{background:linear-gradient(160deg,#cf2626,#8f1414);color:#fff;padding:48px 42px;display:flex;flex-direction:column;justify-content:center;position:relative;overflow:hidden}
.brand::before{content:"";position:absolute;width:320px;height:320px;border-radius:50%;background:rgba(255,255,255,.07);right:-120px;top:-80px}
.brand img{width:64px;height:64px;border-radius:14px;background:#fff;padding:6px;object-fit:contain;margin-bottom:22px}
.brand h1{font-size:30px;font-weight:800;line-height:1.15;margin-bottom:12px}
.brand p{font-size:15px;color:rgba(255,255,255,.9);line-height:1.6;max-width:320px}
.brand .feat{margin-top:26px;display:flex;flex-direction:column;gap:10px}
.brand .feat span{font-size:14px;font-weight:600;color:rgba(255,255,255,.95)}
.formside{padding:48px 46px;display:flex;flex-direction:column;justify-content:center}
.formside .top{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
.formside h2{font-size:24px;font-weight:800}
.langs{display:flex;gap:6px}
.langbtn{padding:5px 11px;border-radius:999px;border:1.5px solid #e5e7eb;background:#fff;font-size:12px;font-weight:700;color:#6b7280;cursor:pointer;font-family:inherit}
.langbtn.active{background:#b91c1c;border-color:#b91c1c;color:#fff}
.sub{color:#6b7280;font-size:14px;margin-bottom:24px}
label{display:block;font-size:13px;font-weight:700;color:#374151;margin:14px 0 6px}
.inp{width:100%;padding:13px 14px;border:1.6px solid #e5e7eb;border-radius:12px;font-size:15px;font-family:inherit}
.inp:focus{outline:none;border-color:#b91c1c;box-shadow:0 0 0 3px rgba(185,28,28,.12)}
.btn{width:100%;margin-top:22px;padding:14px;border:none;border-radius:12px;background:#b91c1c;color:#fff;font-weight:800;font-size:16px;cursor:pointer;font-family:inherit}
.btn:hover{background:#a11717}
.err{background:#fee2e2;color:#b91c1c;border-radius:10px;padding:11px 14px;font-size:14px;font-weight:600;margin-bottom:16px}
.forgot{display:block;text-align:center;margin-top:16px;color:#6b7280;font-size:13px;font-weight:700;text-decoration:none}
.forgot:hover{color:#b91c1c;text-decoration:underline}
@media(max-width:760px){.shell{grid-template-columns:1fr;max-width:440px}.brand{display:none}}
</style>
</head>
<body>
<div class="shell">
    <div class="brand">
        <img src="../assets/sc_logo1.png" alt="Seven Digital">
        <h1>Seven Digital<br>Credit &amp; HR System</h1>
        <p data-lo="ລະບົບຄຸ້ມຄອງເຄຣດິດ ແລະ ຊັບພະຍາກອນມະນຸດ ສຳລັບບໍລິສັດຂອງທ່ານ." data-en="Wallet, attendance, payroll and approvals — all in one place for your company.">Wallet, attendance, payroll and approvals — all in one place for your company.</p>
        <div class="feat">
            <span>💳 <span data-lo="ກະເປົາເງິນ ແລະ ການໂອນ" data-en="Wallet &amp; transfers">Wallet &amp; transfers</span></span>
            <span>🕐 <span data-lo="ການເຂົ້າວຽກ ແລະ ເງິນເດືອນ" data-en="Attendance &amp; payroll">Attendance &amp; payroll</span></span>
            <span>✅ <span data-lo="ການອະນຸມັດ ລາພັກ/ກູ້ຢືມ/OT" data-en="Leave / loan / OT approvals">Leave / loan / OT approvals</span></span>
        </div>
    </div>
    <div class="formside">
        <div class="top">
            <h2 data-lo="ເຂົ້າສູ່ລະບົບ" data-en="Sign in">Sign in</h2>
            <div class="langs">
                <button class="langbtn" id="btn-lo" type="button" onclick="setLang('lo')">🇱🇦 ລາວ</button>
                <button class="langbtn" id="btn-en" type="button" onclick="setLang('en')">🇬🇧 EN</button>
            </div>
        </div>
        <div class="sub" data-lo="ໃຊ້ຄອມພິວເຕີ ຫຼື ແລັບທັອບ" data-en="Welcome back — please log in to continue.">Welcome back — please log in to continue.</div>
        <?php if (!empty($error)): ?><div class="err"><?= htmlspecialchars($error) ?></div><?php endif; ?>
        <form method="POST" action="login.php">
            <label data-lo="ຊື່ຜູ້ໃຊ້ ຫຼື ອີເມວ" data-en="Username or email">Username or email</label>
            <input class="inp" type="text" name="login" value="<?= $prefillLogin ?>" autofocus required>
            <label data-lo="ລະຫັດຜ່ານ" data-en="Password">Password</label>
            <input class="inp" type="password" name="password" placeholder="••••••••" autocomplete="current-password" required>
            <button class="btn" type="submit" data-lo="ເຂົ້າສູ່ລະບົບ" data-en="Sign in">Sign in</button>
        </form>
        <a class="forgot" href="forgot_password.php" data-lo="ລືມລະຫັດຜ່ານ?" data-en="Forgot password?">Forgot password?</a>
    </div>
</div>
</body>
</html>
<?php exit; endif; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login — Seven Wallet</title>
<link rel="manifest" href="/seven-credit-system/manifest.json">
<link rel="apple-touch-icon" href="/seven-credit-system/assets/sc_logo1.png?v=2">
<meta name="theme-color" content="#ffffff">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover,user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Seven Digital">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Lao:wght@400;600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Great+Vibes&family=Playfair+Display:wght@600;700&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.js"></script>
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{--red:#b91c1c;--surface:#fff;--border:#e5e7eb;--text:#111827;--muted:#6b7280;--red-lt:#fee2e2;--gold1:#c79b3b;--gold2:#f4d27a;--gold3:#b8862f;--red-grad:linear-gradient(180deg,#cf2626 0%,#b91c1c 48%,#8f1414 100%)}
html,body{margin:0;padding:0;width:100%;height:100%;font-family:"Noto Sans Lao","Inter",Arial,sans-serif;background:#8f1414;overflow:hidden;overscroll-behavior:none;-webkit-text-size-adjust:100%;text-size-adjust:100%}
.page{height:100vh;height:100dvh;display:flex;flex-direction:column;align-items:center;max-width:480px;margin:0 auto;background:#fff;position:relative;overflow:hidden}
.qr-section{flex:1;min-height:0;display:flex;flex-direction:column;align-items:center}

/* TOP BAR — slim grey divider only (logo + language moved below it) */
.top-bar{width:100%;background:#fff;padding:calc(4px + env(safe-area-inset-top)) 20px 4px;border-bottom:none;position:sticky;top:0;z-index:60}
/* logo + language overlaid at the sides so they don't push the QR down */
.qr-head{position:absolute;top:6px;left:0;right:0;display:flex;align-items:center;justify-content:space-between;padding:0 18px;z-index:5}
.logo-wrap{display:flex;align-items:center;gap:10px}
.logo-box{width:42px;height:42px;border-radius:10px;object-fit:contain;flex-shrink:0;display:block}
.logo-name{font-size:15px;font-weight:800;color:var(--text);white-space:nowrap}
.logo-sub{font-size:11px;color:var(--muted);white-space:nowrap}
.lang-icon{width:42px;height:42px;border-radius:12px;border:1.5px solid var(--border);background:#fff;font-size:21px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .15s}
.lang-icon:hover{border-color:var(--red)}
.lang-icon:active{transform:scale(.94)}

/* ── LOGIN-SCREEN PAYMENT NOTIFICATIONS — stacked cards under the tagline, one per payment ── */
.login-notif-list{display:none;flex-direction:column;gap:8px;width:100%;padding:0 16px;margin-top:10px;max-height:34vh;overflow-y:auto;-webkit-overflow-scrolling:touch;position:relative;z-index:40}
.login-notif{display:flex;align-items:center;gap:10px;background:#dcfce7;border:1px solid #86efac;border-radius:14px;padding:10px 12px;text-decoration:none;box-shadow:0 4px 14px rgba(0,0,0,.10);cursor:pointer;text-align:left;animation:notifPop .25s ease}
@keyframes notifPop{from{transform:translateY(-6px);opacity:0}to{transform:translateY(0);opacity:1}}
.login-notif .ln-emoji{font-size:22px;line-height:1;flex-shrink:0}
.login-notif .ln-main{flex:1;min-width:0}
.login-notif .ln-title{font-size:13px;font-weight:800;color:#166534;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.login-notif .ln-sub{margin-top:2px;font-size:11px;color:#15803d}
.login-notif .ln-amount{font-size:14px;font-weight:800;color:#15803d;white-space:nowrap;flex-shrink:0}
.login-notif .ln-close{flex-shrink:0;width:26px;height:26px;border-radius:50%;border:none;background:rgba(22,101,52,.12);color:#166534;font-size:13px;font-weight:800;line-height:1;padding:0;margin-left:2px;cursor:pointer;display:flex;align-items:center;justify-content:center}
.login-notif .ln-close:active{background:rgba(22,101,52,.28)}

/* LANGUAGE POPUP */
.lang-overlay{position:fixed;inset:0;background:rgba(17,24,39,.45);display:none;align-items:center;justify-content:center;z-index:100;padding:24px}
.lang-overlay.open{display:flex}
.lang-card{width:100%;max-width:340px;background:#fff;border-radius:24px;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.32);animation:langPop .2s ease}
@keyframes langPop{from{transform:scale(.9);opacity:0}to{transform:scale(1);opacity:1}}
.lang-row{width:100%;display:flex;align-items:center;gap:16px;padding:18px 22px;background:none;border:none;border-bottom:1px solid var(--border);font-family:inherit;cursor:pointer;text-align:left;transition:background .12s}
.lang-row:last-child{border-bottom:none}
.lang-row:hover{background:#f9fafb}
.lang-row:active{background:#f3f4f6}
.lang-flag{width:48px;height:48px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:42px;line-height:1;overflow:hidden;background:#f3f4f6;flex-shrink:0;border:1px solid var(--border)}
.lang-text{font-size:18px;font-weight:700;color:var(--text)}
.lang-row.active .lang-text{color:var(--red)}

/* QR CODE SECTION — real scannable QR with photo in the center */
.qr-section{width:100%;background:#fff;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:6px 20px 8px;position:relative}
/* notifications present → push the QR back up to the top */
.qr-section.has-notif{justify-content:flex-start}
.qr-wrap{position:relative;width:min(384px,90vw);height:min(384px,90vw);margin-bottom:0;display:flex;align-items:center;justify-content:center}
.qr-splat{position:absolute;inset:0;width:100%;height:100%;overflow:visible;z-index:0}
.qr-box{position:relative;z-index:1;width:60%;aspect-ratio:1;display:flex;align-items:center;justify-content:center}
#qrCode{width:100%;height:100%;display:flex;align-items:center;justify-content:center}
#qrCode img,#qrCode canvas{width:100%!important;height:100%!important;display:block;image-rendering:pixelated;background:transparent}
.photo-circle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:30%;aspect-ratio:1;border-radius:50%;overflow:hidden;background:#fff;box-shadow:0 0 0 5px #fff,0 2px 8px rgba(0,0,0,.18);z-index:2}
/* when notifications are showing (QR moved up), use the compact size */
.qr-section.has-notif .qr-wrap{width:250px;height:250px}
.qr-section.has-notif .tag-svg{width:300px}
.photo-circle img{width:100%;height:100%;object-fit:cover}
.photo-initial{width:100%;height:100%;background:linear-gradient(135deg,#7f1d1d,var(--red));display:flex;align-items:center;justify-content:center;color:#fff;font-size:26px;font-weight:800}
.scan-hint{font-size:11px;color:var(--muted);text-align:center;background:#f9fafb;border:1px solid var(--border);padding:4px 14px;border-radius:999px;display:none}

/* TAGLINE — "Inspire Possibility" curved into an upward arch */
.tagline{text-align:center;margin-top:-10px}
.tag-svg{display:block;margin:0 auto;width:min(430px,94vw);height:auto;overflow:visible}
.tag-arc{font-family:"Great Vibes",cursive;font-size:26px;fill:var(--red);stroke:var(--red);stroke-width:0.9;paint-order:stroke fill;stroke-linejoin:round}

/* RED PANEL — flat-top red area (gradient + texture) with a layered wave divider */
.red-panel{width:100%;position:relative;z-index:10}
/* when the calendar is expanded, lift the whole red panel above the notification
   cards so the calendar overlay covers them instead of showing through */
.red-panel.cal-on{z-index:60}
/* wave divider sits in normal flow at the very top of the red area */
.red-wave{display:block;width:100%;height:44px;line-height:0;margin-bottom:-1px}
.red-wave svg{display:block;width:100%;height:100%}
/* red body: gradient + texture, content lives here */
.red-body{position:relative;width:100%;background:var(--red-grad)}
/* soft top highlight */
.red-body::before{
    content:"";position:absolute;left:0;right:0;top:0;height:150px;
    background:radial-gradient(120% 100% at 50% 0%,rgba(255,255,255,.14),rgba(255,255,255,0) 65%);
    pointer-events:none;z-index:0;
}
/* faint wave / contour texture */
.red-body::after{
    content:"";position:absolute;inset:0;pointer-events:none;opacity:.55;z-index:0;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='420' height='150' viewBox='0 0 420 150'%3E%3Cg fill='none' stroke='%23ffffff' stroke-opacity='0.07' stroke-width='2'%3E%3Cpath d='M-20,30 Q90,-2 210,30 T440,30'/%3E%3Cpath d='M-20,60 Q90,28 210,60 T440,60'/%3E%3Cpath d='M-20,90 Q90,58 210,90 T440,90'/%3E%3Cpath d='M-20,120 Q90,88 210,120 T440,120'/%3E%3C/g%3E%3C/svg%3E");
    background-size:420px 150px;
}

/* CALENDAR — transparent bar over the red panel; body expands UP as overlay */
.cal-wrap{width:100%;background:transparent;position:relative;z-index:30;padding-top:22px}

/* Toggle button */
.cal-toggle{
    display:flex;align-items:center;justify-content:space-between;
    padding:6px 20px;cursor:pointer;
    border:none;background:none;width:100%;font-family:inherit;
}
.cal-toggle-left{display:flex;align-items:flex-start;gap:10px}
.cal-big-num{font-size:38px;font-weight:800;color:#fff;line-height:1;flex-shrink:0}
.cal-info{text-align:left;padding-top:1px}
.cal-day-name{font-size:13px;font-weight:800;color:#fff}
.cal-bud-year{font-size:11px;font-weight:700;color:rgba(255,255,255,.9);margin-top:2px}
.cal-lunar{font-size:11px;font-weight:700;color:rgba(255,255,255,.85);margin-top:2px;line-height:1.45}
.cal-toggle-right{display:flex;flex-direction:column;align-items:center;gap:0;color:rgba(255,255,255,.7);flex-shrink:0;padding-top:4px}
.cal-chevron{font-size:14px;line-height:0.8;transition:transform .3s;display:block;color:rgba(255,255,255,.7)}
.cal-chevron.up{transform:rotate(180deg)}

/* Collapsible body — absolute overlay anchored to the TOP of the bar, grows upward */
.cal-body{
    position:absolute;left:0;right:0;bottom:100%;
    background:var(--red-grad);
    overflow:hidden;max-height:0;
    transition:max-height .35s ease;
    box-shadow:0 -10px 30px rgba(0,0,0,.28);
    border-radius:18px 18px 0 0;
    z-index:45;
}
.cal-body.open{max-height:480px}
.cal-inner{padding:14px 16px 16px}

.cal-month-row{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px}
.cal-month-label{font-size:13px;font-weight:800;color:#fff}
.cal-arrows{display:flex;gap:4px}
.cal-arr{width:28px;height:28px;border-radius:50%;border:1.5px solid rgba(255,255,255,.4);background:rgba(255,255,255,.15);font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;transition:all .15s}
.cal-arr:hover{background:rgba(255,255,255,.3)}

.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:3px;margin-bottom:8px}
.cal-dow{font-size:11px;font-weight:700;color:rgba(255,255,255,.6);text-align:center;padding:3px 0}
.cal-cell{
    aspect-ratio:1;display:flex;flex-direction:column;align-items:center;
    justify-content:center;border-radius:6px;font-size:13px;font-weight:600;
    color:#fff;cursor:pointer;position:relative;gap:1px;transition:background .1s;
}
.cal-cell:hover{background:rgba(255,255,255,.15)}
.cal-cell.today{
    background:#fff;color:var(--red);font-weight:800;border-radius:8px;
}
.cal-cell.other{color:rgba(255,255,255,.3)}
.cal-cell.sun{color:#fca5a5}
.cal-cell.sat{color:#93c5fd}
.cal-cell.today.sun,.cal-cell.today.sat{color:var(--red)}
.cal-dot{width:4px;height:4px;border-radius:50%;background:rgba(255,255,255,.8);flex-shrink:0}
.cal-cell.today .cal-dot{background:var(--red)}

.cal-hlist{border-top:1px solid rgba(255,255,255,.2);padding-top:8px;margin-top:4px}
.cal-hitem{display:flex;align-items:center;gap:8px;font-size:11px;color:rgba(255,255,255,.8);padding:3px 0}
.cal-hdot{width:5px;height:5px;border-radius:50%;background:rgba(255,255,255,.8);flex-shrink:0}

/* BOTTOM RED SECTION */
.bottom-section{width:100%;background:transparent;padding:0px 28px calc(10px + env(safe-area-inset-bottom));position:relative;z-index:10}
.error-box{display:flex;align-items:center;gap:8px;background:rgba(255,255,255,.15);border:1.5px solid rgba(255,255,255,.3);color:#fff;padding:11px 14px;border-radius:10px;margin-bottom:14px;font-size:13px;font-weight:600}
.form-group{margin-bottom:12px}
.field-label{display:block;font-size:11px;font-weight:700;color:rgba(255,255,255,.75);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px}
.input-wrap{position:relative}
.input-wrap input{width:100%;padding:13px 44px 13px 16px;border:none;border-radius:12px;font-size:15px;font-family:inherit;color:var(--text);background:#fff;outline:none}
.input-wrap input::placeholder{color:#9ca3af}
.input-icon{position:absolute;right:14px;top:50%;transform:translateY(-50%);font-size:18px;cursor:pointer;color:var(--muted);background:none;border:none;padding:2px}

/* Login form (hidden by default) */
.pw-form{display:none;margin-bottom:14px}

/* TWO BUTTONS — smaller / more compact, BCEL bottom-bar size */
.btn-row{display:flex;gap:8px}
.btn-bio{
    flex:1;padding:10px 8px;
    background:rgba(139,0,0,.55);
    border:2px solid rgba(255,255,255,.35);
    border-radius:14px;color:#fff;
    font-size:12px;font-weight:800;font-family:inherit;
    cursor:pointer;display:flex;flex-direction:column;
    align-items:center;gap:4px;transition:all .15s;
    box-shadow:inset 0 1px 0 rgba(255,255,255,.15);
}
.btn-bio:hover{background:rgba(139,0,0,.75)}
.btn-bio:active{transform:scale(.97)}
.btn-bio .bio-icon{font-size:22px;line-height:1}
.btn-login{
    flex:1.3;padding:5px 12px;
    background:rgba(139,0,0,.55);
    border:2px solid rgba(255,255,255,.35);
    border-radius:14px;color:#fff;
    font-size:14px;font-weight:800;font-family:inherit;
    cursor:pointer;display:flex;align-items:center;
    justify-content:center;gap:8px;transition:all .15s;
    box-shadow:inset 0 1px 0 rgba(255,255,255,.15);
}
.btn-login:hover{background:rgba(139,0,0,.75)}
.btn-login:active{transform:scale(.98)}

/* Submit button inside form */
.btn-submit{width:100%;padding:14px;background:#fff;color:var(--red);border:none;border-radius:12px;font-size:16px;font-weight:800;font-family:inherit;cursor:pointer;margin-top:10px;transition:opacity .15s}
.btn-submit:hover{opacity:.9}
.btn-back{display:block;width:100%;background:none;border:none;color:rgba(255,255,255,.7);font-size:14px;font-weight:700;font-family:inherit;cursor:pointer;padding:10px;text-align:center;margin-top:4px}
.btn-back:hover{color:#fff}
.btn-forgot{display:block;text-align:center;color:rgba(255,255,255,.9);font-size:13px;font-weight:700;text-decoration:underline;margin-top:10px}

/* ── RESPONSIVE: scale the QR/spacing down on shorter screens so nothing clips ── */
@media (max-height:740px){
    .qr-wrap{width:min(300px,80vw);height:min(300px,80vw)}
    .qr-section.has-notif .qr-wrap{width:215px;height:215px}
    .qr-section{padding-top:12px}
    .tag-svg{width:min(320px,86vw)}
    .login-notif-list{max-height:30vh}
}
@media (max-height:640px){
    .qr-wrap{width:min(250px,72vw);height:min(250px,72vw)}
    .qr-section.has-notif .qr-wrap{width:185px;height:185px}
    .qr-section{padding:10px 20px 6px}
    .tagline{margin-top:2px}
    .tag-svg{width:min(280px,80vw)}
}
/* very narrow phones: keep the header on one line */
@media (max-width:360px){
    .logo-name{font-size:14px}
}
</style>
</head>
<body>
<div class="page">

<!-- TOP BAR: slim grey divider only -->
<div class="top-bar"></div>

<!-- LANGUAGE POPUP -->
<div class="lang-overlay" id="langOverlay" onclick="closeLangSheet(event)">
    <div class="lang-card">
        <button class="lang-row" data-lang="lo" onclick="pickLang('lo')">
            <span class="lang-flag">🇱🇦</span>
            <span class="lang-text">ພາສາລາວ</span>
        </button>
        <button class="lang-row" data-lang="en" onclick="pickLang('en')">
            <span class="lang-flag">🇺🇸</span>
            <span class="lang-text">English</span>
        </button>
    </div>
</div>

<!-- QR RING -->
<div class="qr-section">
    <!-- logo (left) under the grey line + language (top-right of the QR) -->
    <div class="qr-head">
        <img class="logo-box" src="/seven-credit-system/assets/7.png?v=1" alt="Seven Digital">
        <button class="lang-icon" id="langIcon" onclick="openLangSheet()" aria-label="Language"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="9.2" stroke="#dc2626"/><line x1="3" y1="12" x2="21" y2="12" stroke="#16a34a"/><line x1="5.2" y1="7" x2="18.8" y2="7" stroke="#16a34a"/><line x1="5.2" y1="17" x2="18.8" y2="17" stroke="#16a34a"/><ellipse cx="12" cy="12" rx="4.2" ry="9.2" stroke="#2563eb"/><path d="M12 2.8v18.4" stroke="#2563eb"/></svg></button>
    </div>
    <div class="qr-wrap">
        <svg class="qr-splat" id="qrSplat" viewBox="0 0 250 250" preserveAspectRatio="xMidYMid meet" aria-hidden="true"></svg>
        <div class="qr-box" id="qrBox">
            <div id="qrCode"></div>
            <div class="photo-circle" id="photoCircle">
                <?php if ($photoUrl): ?>
                    <img src="<?= $photoUrl ?>?v=<?= date('Ymd') ?>" alt="Profile">
                <?php else: ?>
                    <img src="/seven-credit-system/assets/7.png" style="object-fit:contain;padding:5px" alt="Seven Digital">
                <?php endif; ?>
            </div>
        </div>
    </div>
    <!-- Brand tagline — curved upward arch around the QR -->
    <div class="tagline">
        <svg class="tag-svg" viewBox="0 0 340 104" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-label="Inspire Possibility">
            <defs><path id="taglineArc" d="M8,24 Q170,120 332,24" fill="none"/></defs>
            <text class="tag-arc" text-anchor="middle" letter-spacing="2.5" word-spacing="6"><textPath xlink:href="#taglineArc" href="#taglineArc" startOffset="50%">Inspire Possibility<tspan font-size="12" dy="-9">©</tspan></textPath></text>
        </svg>
    </div>

    <!-- PAYMENT NOTIFICATIONS — one card per unread received payment, newest first -->
    <div class="login-notif-list" id="loginNotifList"></div>
</div>
<script>(function(){try{if(localStorage.getItem('seven_had_notif')==='1'){var s=document.querySelector('.qr-section');if(s)s.classList.add('has-notif');}}catch(e){}})();</script>

<!-- RED PANEL — curved top + gradient + wave texture wraps calendar & buttons -->
<div class="red-panel">
<div class="red-wave">
  <svg viewBox="0 0 480 44" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
    <path d="M0,17 C95,3 180,25 270,15 C350,6 420,21 480,13 L480,44 L0,44 Z" fill="#f4c9c9"/>
    <path d="M0,29 C100,15 190,33 290,25 C380,18 440,31 480,27 L480,44 L0,44 Z" fill="#cf2626"/>
  </svg>
</div>
<div class="red-body">

<!-- CALENDAR — body pops UP as overlay -->
<div class="cal-wrap">

    <!-- Collapsible body (overlay, expands upward) -->
    <div class="cal-body" id="calBody">
        <div class="cal-inner">
            <div class="cal-month-row">
                <div class="cal-month-label" id="calMonthLabel">ມີຖຸນາ 2026 | 2569</div>
                <div class="cal-arrows">
                    <button class="cal-arr" onclick="changeMonth(-1)">‹</button>
                    <button class="cal-arr" onclick="changeMonth(1)">›</button>
                </div>
            </div>
            <div class="cal-grid" id="calGrid"></div>
            <div class="cal-hlist" id="calHList" style="display:none"></div>
        </div>
    </div>

    <!-- Toggle header: always visible -->
    <button class="cal-toggle" onclick="toggleCal()" aria-label="Toggle calendar">
        <div class="cal-toggle-left">
            <div class="cal-big-num" id="calNum">21</div>
            <div class="cal-info">
                <div class="cal-day-name" id="calDayName">ວັນອາທິດ</div>
                <div class="cal-bud-year" id="calBudYear">ມີຖຸນາ 2026 | 2569</div>
                <div class="cal-lunar" id="calLunar">ຂຶ້ນ 7 ຄ່ຳ, ເດືອນ 8.1 ລາວ, ປີມະເມຍ</div>
            </div>
        </div>
        <!-- Double chevron up — tap to expand upward -->
        <div class="cal-toggle-right" id="calChevrons">
            <span class="cal-chevron" id="chev1">∧</span>
            <span class="cal-chevron" id="chev2">∧</span>
        </div>
    </button>
</div>

<!-- BOTTOM RED -->
<div class="bottom-section">

    <?php if (!empty($error)): ?>
    <div class="error-box">⚠️ <?= htmlspecialchars($error) ?></div>
    <?php endif; ?>

    <form method="POST" id="loginForm">
        <!-- Hidden login form, shown when Login tapped -->
        <div class="pw-form" id="pwForm">
            <div class="form-group">
                <label class="field-label" data-lo="ຊື່ຜູ້ໃຊ້ / ອີເມວ" data-en="Username / Email">Username / Email</label>
                <div class="input-wrap">
                    <input type="text" name="login" id="loginInput" placeholder="username or email"
                        value="<?= $prefillLogin ?>" autocomplete="username" oninput="debounce(this.value)">
                    <button type="button" class="input-icon" onclick="clearUser()">✕</button>
                </div>
            </div>
            <div class="form-group">
                <label class="field-label" data-lo="ລະຫັດຜ່ານ" data-en="Password">Password</label>
                <div class="input-wrap">
                    <input type="password" name="password" id="pwInput" placeholder="••••••••" autocomplete="current-password">
                    <button type="button" class="input-icon" onclick="togglePw()">👁</button>
                </div>
            </div>
            <button class="btn-submit" type="submit">
                <span data-lo="ເຂົ້າລະບົບ" data-en="Sign In">Sign In</span> →
            </button>
            <a href="forgot_password.php" class="btn-forgot" data-lo="ລືມລະຫັດຜ່ານ?" data-en="Forgot password?">Forgot password?</a>
            <button type="button" class="btn-back" onclick="hideForm()">
                ← <span data-lo="ກັບຄືນ" data-en="Back">Back</span>
            </button>
        </div>
    </form>

    <!-- Two main buttons -->
    <div class="btn-row" id="mainBtns">
        <button class="btn-bio" id="bioBtn" onclick="loginWithFaceID()">
            <span class="bio-icon" id="bioIcon">👆</span>
            <span id="bioLabel" data-lo="ສະແກນໜ້າ" data-en="Biometrics">Biometrics</span>
        </button>
        <button class="btn-login" onclick="showForm()">
            🔑 <span data-lo="ເຂົ້າດ້ວຍລະຫັດ" data-en="Login">Login</span>
        </button>
    </div>

</div><!-- /bottom-section -->
</div><!-- /red-body -->
</div><!-- /red-panel -->
</div>

<script>
/* ═══ LANGUAGE ═══ */
const LANG_KEY='seven_lang';
function setLang(lang, persist){
    if(persist===undefined) persist=true;
    document.querySelectorAll('[data-lo],[data-en]').forEach(el=>{
        const t=el.getAttribute('data-'+lang);if(t!==null)el.textContent=t;
    });
    document.querySelectorAll('.lang-row').forEach(r=>r.classList.toggle('active',r.dataset.lang===lang));
    if(persist) localStorage.setItem(LANG_KEY,lang);
    renderCalendar();
    updateHero(new Date(calYear,calMonth,selectedDay||new Date().getDate()));
    /* re-localise the dynamic payment-notification text (count message) */
    if(notifLogin) checkLoginNotif(notifLogin);
}
function openLangSheet(){
    document.querySelectorAll('.lang-row').forEach(r=>r.classList.toggle('active',r.dataset.lang===(localStorage.getItem(LANG_KEY)||'en')));
    document.getElementById('langOverlay').classList.add('open');
}
function closeLangSheet(e){
    if(e && e.target.id!=='langOverlay') return;   // only close when tapping the dark backdrop
    document.getElementById('langOverlay').classList.remove('open');
}
function pickLang(lang){
    try{localStorage.setItem('seven_lang_set','1');}catch(e){}   // remember it was an explicit choice
    setLang(lang);
    document.getElementById('langOverlay').classList.remove('open');
}

/* ═══ SHOW/HIDE FORM ═══ */
function showForm(){
    document.getElementById('pwForm').style.display='block';
    document.getElementById('mainBtns').style.display='none';
    const li=document.getElementById('loginInput');
    setTimeout(()=>li.value?document.getElementById('pwInput').focus():li.focus(),50);
}
function hideForm(){
    document.getElementById('pwForm').style.display='none';
    document.getElementById('mainBtns').style.display='flex';
}
function togglePw(){const i=document.getElementById('pwInput');i.type=i.type==='password'?'text':'password';}

/* Submit the password login via fetch (NOT a top-level form POST), then navigate
   client-side. A top-level POST makes iOS 26 open the result in Safari's in-app
   browser and drop out of the installed app; fetch keeps us inside it. */
function showLoginError(msg){
    let box=document.getElementById('loginErrBox');
    if(!box){
        box=document.createElement('div');
        box.id='loginErrBox';
        box.className='error-box';
        const pw=document.getElementById('pwForm');
        if(pw) pw.insertBefore(box, pw.firstChild);
    }
    box.textContent='⚠️ '+msg;
    box.style.display='flex';
}
(function(){
    const form=document.getElementById('loginForm');
    if(!form) return;
    form.addEventListener('submit', async function(e){
        e.preventDefault();
        const btn=form.querySelector('.btn-submit');
        if(btn) btn.style.opacity='.6';
        const fd=new FormData(form);
        fd.set('ajax','1');
        try{
            const r=await fetch('login.php',{method:'POST',headers:{'X-Requested-With':'fetch'},body:fd,credentials:'same-origin'});
            const d=await r.json();
            if(d && d.success && d.redirect){ window.location.href=d.redirect; return; }
            showLoginError((d && d.error) || 'Invalid username/email or password.');
        }catch(err){
            showLoginError('Network error. Please try again.');
        }
        if(btn) btn.style.opacity='1';
    });
})();

/* Auto-show form on error */
<?php if(!empty($error)||$prefillLogin): ?>
document.addEventListener('DOMContentLoaded',()=>{showForm();});
<?php endif; ?>

/* ═══ REAL SCANNABLE QR + REMEMBER LAST USER ═══ */
let currentQR=<?=json_encode($qrContent)?>;
const LAST_USER_KEY='seven_last_user';
const PHOTO_BASE='/seven-credit-system/assets/profile_photos/';
const DEFAULT_AVATAR='/seven-credit-system/assets/7.png';   // ← change this file/path to set the default QR-centre logo
/* When nobody is identified yet, the QR encodes this generic app link instead of a
   meaningless placeholder. Scanning it just opens the app (never a broken transfer). */
const APP_LOGIN_URL='https://7d-lao.com/seven-credit-system/auth/login.php';

/* render a real, scannable QR — RED circular dots, with the three corner "eyes"
   each a different colour (top-left green, top-right blue, bottom-left red).
   Drawn by hand from the QR matrix so each eye can be coloured independently.
   Transparent background sits on the white circle; photo overlays the centre. */
const EYE_TL='#16a34a';   // top-left  = green  ← change these to recolour the eyes
const EYE_TR='#2563eb';   // top-right = blue
const EYE_BL='#b91c1c';   // bottom-left = red
const DOT_COLORS=['#16a34a','#b91c1c','#2563eb','#b8862f']; // data dots: green / red / blue / gold mix
function renderQR(txt){
    const box=document.getElementById('qrCode');
    if(!box) return;
    box.innerHTML='';
    if(!txt || txt==='SEVEN-DIGITAL') txt=APP_LOGIN_URL;   // generic, always scannable
    if(window.qrcode){
        try{
            const qr=qrcode(0,'H'); qr.addData(txt); qr.make();
            const n=qr.getModuleCount();
            const isEye=(r,c)=>(r<7&&c<7)||(r<7&&c>=n-7)||(r>=n-7&&c<7);
            let dots='';
            let dseed=99173; const drnd=()=>{dseed=(dseed*9301+49297)%233280;return dseed/233280;};
            for(let r=0;r<n;r++){for(let c=0;c<n;c++){
                if(qr.isDark(r,c)&&!isEye(r,c)){
                    const col=DOT_COLORS[Math.floor(drnd()*DOT_COLORS.length)];
                    dots+='<circle cx="'+(c+0.5)+'" cy="'+(r+0.5)+'" r="0.42" fill="'+col+'"/>';
                }
            }}
            const eye=(r0,c0,col)=>'<rect x="'+c0+'" y="'+r0+'" width="7" height="7" rx="2.3" fill="'+col+'"/>'
                +'<rect x="'+(c0+1)+'" y="'+(r0+1)+'" width="5" height="5" rx="1.6" fill="#ffffff"/>'
                +'<rect x="'+(c0+2)+'" y="'+(r0+2)+'" width="3" height="3" rx="1.1" fill="'+col+'"/>';
            box.innerHTML='<svg viewBox="0 0 '+n+' '+n+'" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" shape-rendering="geometricPrecision">'
                + dots
                + eye(0,0,EYE_TL)
                + eye(0,n-7,EYE_TR)
                + eye(n-7,0,EYE_BL)
                + '</svg>';
            return;
        }catch(e){}
    }
    /* fallback: solid red QR on white */
    if(window.QRCode){
        try{ new QRCode(box,{text:txt,width:300,height:300,colorDark:'#b91c1c',colorLight:'#ffffff',correctLevel:QRCode.CorrectLevel.H}); }catch(e){}
    }
}

/* draw the colorful paint-splatter ring + white centre circle behind the QR (once) */
function buildSplatter(){
    const svg=document.getElementById('qrSplat');
    if(!svg) return;
    svg.innerHTML='';
    const NS='http://www.w3.org/2000/svg';
    const COLORS=['#e2433f','#3a7bd5','#46a23b','#f2b417','#7b3fa0','#e07b39','#19a7c4'];
    let seed=7; const rnd=()=>{seed=(seed*9301+49297)%233280;return seed/233280;};
    const cx=125,cy=125,ring=116,count=13;
    const add=(tag,attrs)=>{const e=document.createElementNS(NS,tag);for(const k in attrs)e.setAttribute(k,attrs[k]);svg.appendChild(e);return e;};
    const blob=(bx,by,r)=>{let d='',i;const n=9;for(i=0;i<=n;i++){const a=(i%n)/n*Math.PI*2,rr=r*(0.55+rnd()*0.7);d+=(i===0?'M':'L')+(bx+Math.cos(a)*rr).toFixed(1)+','+(by+Math.sin(a)*rr).toFixed(1);}return d+'Z';};
    for(let i=0;i<count;i++){
        const a=(i/count)*Math.PI*2+rnd()*0.3;
        const px=cx+Math.cos(a)*ring, py=cy+Math.sin(a)*ring;
        const col=COLORS[i%COLORS.length];
        add('path',{d:blob(px,py,18+rnd()*10),fill:col});
        for(let j=0;j<4;j++){const da=a+(rnd()-0.5)*1.2,dr=ring+(rnd()-0.4)*34;add('circle',{cx:(cx+Math.cos(da)*dr).toFixed(1),cy:(cy+Math.sin(da)*dr).toFixed(1),r:(2+rnd()*4).toFixed(1),fill:col});}
    }
    add('circle',{cx:cx,cy:cy,r:110,fill:'#ffffff'});
}

const PHOTO_VER=new Date().toISOString().slice(0,10); // changes once a day → photo caches across reloads
function setPhoto(photo,initial){
    const circ=document.getElementById('photoCircle');
    if(photo){circ.innerHTML='<img src="'+PHOTO_BASE+photo+'?v='+PHOTO_VER+'" style="width:100%;height:100%;object-fit:cover">';}
    else{circ.innerHTML='<img src="'+DEFAULT_AVATAR+'" style="width:100%;height:100%;object-fit:contain;padding:6px" alt="">';}
}

/* paint the screen for a known user */
function applyUser(u){
    if(!u) return;
    setPhoto(u.photo,u.initial);
    currentQR=u.customer_code||'SEVEN-DIGITAL';
    renderQR(currentQR);
    const li=document.getElementById('loginInput');
    if(li && !li.value && u.login) li.value=u.login;
    watchNotif(u.login);          // start watching this account for new payments
}

function saveLastUser(u){try{localStorage.setItem(LAST_USER_KEY,JSON.stringify(u));}catch(e){}}
function getLastUser(){try{return JSON.parse(localStorage.getItem(LAST_USER_KEY)||'null');}catch(e){return null;}}

/* initial paint */
buildSplatter();
renderQR(currentQR);

/* ═══ LOGIN-SCREEN PAYMENT NOTIFICATION ═══
   Mirrors the dashboard "New Money Received" box. Shows ONLY a count (no amounts,
   senders or balances) for the identified account, and refreshes every 5s so a
   payment that lands while this screen is open pops up right away. */
let notifLogin = null;       // the account login we're currently watching
let notifTimer = null;
let lastNotifSig = '';       // fingerprint of the cards on screen; skip re-render if unchanged
const dismissedIds = new Set(); // payments hidden this session (survives the 5s poll race)

function escapeHtml(s){return String(s==null?'':s).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));}
/* "2026-06-26 17:10:05" -> "26 Jun 17:10" (localised month) */
function fmtNotifTime(iso){
    try{
        const d=new Date(String(iso).replace(' ','T'));
        if(isNaN(d.getTime())) return iso||'';
        const lang=localStorage.getItem(LANG_KEY)||'en';
        const mo=(lang==='lo'?MO_LO:MO_EN)[d.getMonth()];
        const hh=String(d.getHours()).padStart(2,'0');
        const mm=String(d.getMinutes()).padStart(2,'0');
        return d.getDate()+' '+mo+' '+d.getFullYear()+' · '+hh+':'+mm;
    }catch(e){return iso||'';}
}
/* Render one green card per unread payment, newest first, stacked under the tagline. */
function renderLoginNotifs(items){
    const list=document.getElementById('loginNotifList');
    if(!list) return;
    const lang=localStorage.getItem(LANG_KEY)||'en';
    /* drop anything the user just dismissed, even if a racing poll still returns it */
    items=(items||[]).filter(it=>!dismissedIds.has(String(parseInt(it.id,10)||0)));

    /* fast path: same data + language already on screen → do nothing (no flicker) */
    const sig=lang+'::'+items.map(it=>(it.id!=null?it.id:'')+'|'+(it.created_at||'')+'|'+(it.amount!=null?it.amount:'')+'|'+(it.sender||'')).join('~');
    if(sig===lastNotifSig) return;
    lastNotifSig=sig;

    const qsec=document.querySelector('.qr-section');
    if(!items.length){ list.innerHTML=''; list.style.display='none'; if(qsec) qsec.classList.remove('has-notif'); try{localStorage.setItem('seven_had_notif','0');}catch(e){} return; }
    if(qsec) qsec.classList.add('has-notif');   // notifications present → QR moves up
    try{localStorage.setItem('seven_had_notif','1');}catch(e){}
    const fromWord=lang==='lo'?'ໄດ້ຮັບເງິນຈາກ ':'Received from ';

    /* language switched → drop existing cards so labels rebuild in the new language */
    if(list.getAttribute('data-lang')!==lang){ list.innerHTML=''; list.setAttribute('data-lang',lang); }

    /* remove cards that are no longer in the list */
    const wantIds=items.map(it=>String(parseInt(it.id,10)||0));
    Array.from(list.children).forEach(ch=>{ if(wantIds.indexOf(ch.getAttribute('data-id'))===-1) ch.remove(); });

    /* add / position cards in newest-first order. Existing cards are NEVER recreated,
       so they don't move or re-animate — only a genuinely new card animates in. */
    let prev=null;
    items.forEach(it=>{
        const id=String(parseInt(it.id,10)||0);
        let card=list.querySelector('.login-notif[data-id="'+id+'"]');
        if(!card){
            const sender=escapeHtml(it.sender||(lang==='lo'?'ບໍ່ຮູ້ຊື່':'Unknown'));
            const amt='₭ '+escapeHtml(it.amount_fmt!=null?it.amount_fmt:(it.amount!=null?it.amount:''));
            const tm=escapeHtml(fmtNotifTime(it.created_at||''));
            card=document.createElement('div');
            card.className='login-notif';
            card.setAttribute('data-id',id);
            card.setAttribute('onclick','showForm()');
            card.innerHTML='<div class="ln-emoji">💰</div>'
                + '<div class="ln-main"><div class="ln-title">'+escapeHtml(fromWord)+sender+'</div>'
                + '<div class="ln-sub">'+tm+'</div></div>'
                + '<div class="ln-amount">'+amt+'</div>'
                + '<button type="button" class="ln-close" aria-label="Dismiss" onclick="dismissNotif(event,'+(parseInt(it.id,10)||0)+',this)">✕</button>';
            if(prev) list.insertBefore(card, prev.nextSibling);
            else     list.insertBefore(card, list.firstChild);
        }
        prev=card;
    });
    list.style.display='flex';
}
/* Tap ✕ on a card: mark that payment read everywhere, then slide the card away. */
function dismissNotif(ev, id, btn){
    if(ev){ ev.stopPropagation(); ev.preventDefault(); }
    dismissedIds.add(String(parseInt(id,10)||0));   // never let a racing refresh re-add it
    const card=btn && btn.closest ? btn.closest('.login-notif') : null;
    /* tell the server to mark this one read (fire-and-forget) */
    if(notifLogin && id){
        try{
            fetch('dismiss_login_notification.php',{
                method:'POST',
                headers:{'Content-Type':'application/json'},
                body:JSON.stringify({u:notifLogin,id:id})
            });
        }catch(e){}
    }
    /* animate it out, then remove and let the next refresh reconcile */
    if(card){
        card.style.transition='opacity .2s ease, transform .2s ease';
        card.style.opacity='0';
        card.style.transform='translateX(24px)';
        setTimeout(()=>{
            card.remove();
            const list=document.getElementById('loginNotifList');
            if(list && !list.children.length){
                list.style.display='none';
                const qs=document.querySelector('.qr-section');
                if(qs) qs.classList.remove('has-notif');   // back to centered when empty
                try{localStorage.setItem('seven_had_notif','0');}catch(e){}
            }
            lastNotifSig='';   // force a clean re-render on the next poll
        },200);
    }
}
async function checkLoginNotif(login){
    if(!login || login.trim().length<2){ renderLoginNotifs([]); return; }
    try{
        const r=await fetch('check_login_notification.php?u='+encodeURIComponent(login.trim()));
        const d=await r.json();
        renderLoginNotifs((d && d.success && d.found && d.items) ? d.items : []);
    }catch(e){ /* silent — never block the login screen on a notif error */ }
}
function watchNotif(login){
    const next=login||null;
    /* if we're already watching this same account, keep the existing poller */
    if(next===notifLogin && notifTimer) return;
    notifLogin=next;
    lastNotifSig='';                 // account changed → force a fresh render
    clearInterval(notifTimer); notifTimer=null;
    if(notifLogin){
        checkLoginNotif(notifLogin);                                   // immediate
        notifTimer=setInterval(()=>checkLoginNotif(notifLogin),5000);  // then live
    }else{
        renderLoginNotifs([]);
    }
}

/* ═══ LIVE USER LOOKUP ═══ */
let ltimer=null;
function debounce(v){
    clearTimeout(ltimer);
    const val=v.trim();
    if(val.length<2){ watchNotif(null); return; }
    ltimer=setTimeout(()=>{
        lookupUser(val);    // resolve photo / QR / name (matches username or email)
        watchNotif(val);    // AND check notifications directly (also matches customer code)
    },500);
}
async function lookupUser(login){
    try{
        const res=await fetch('lookup_user.php?u='+encodeURIComponent(login));
        const d=await res.json();if(!d.found)return;
        const u={login:login,name:d.name,photo:d.photo||null,initial:d.initial||'',customer_code:d.customer_code||null};
        applyUser(u);
        saveLastUser(u);            // remember for next app open
        checkBioForUser(login);
    }catch(e){}
}
function clearUser(){
    document.getElementById('loginInput').value='';
    setPhoto(null,'7');
    currentQR='SEVEN-DIGITAL';renderQR(currentQR);
    watchNotif(null);                                        // stop watching + hide banner
    try{localStorage.removeItem(LAST_USER_KEY);}catch(e){}   // forget remembered user
}

/* ═══ CALENDAR ═══ */
const MO_LO=['ມັງກອນ','ກຸມພາ','ມີນາ','ເມສາ','ພຶດສະພາ','ມີຖຸນາ','ກໍລະກົດ','ສິງຫາ','ກັນຍາ','ຕຸລາ','ພະຈິກ','ທັນວາ'];
const MO_EN=['January','February','March','April','May','June','July','August','September','October','November','December'];
const DW_LO=['ອາ','ຈ','ອ','ພ','ພຫ','ສ','ເສ'];
const DW_EN=['Su','Mo','Tu','We','Th','Fr','Sa'];
const DW_FULL_LO=['ອາທິດ','ຈັນ','ອັງຄານ','ພຸດ','ພະຫັດ','ສຸກ','ເສົາ'];
const DW_FULL_EN=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
const ZODIAC_LO=['ຊວດ','ສະຫງົວ','ຂານ','ເຖາະ','ມະໂລງ','ມະເສັງ','ມະເມຍ','ມະແມ','ວອກ','ລະກາ','ຈໍ','ກຸນ'];
const HOLIDAYS={'1-1':{lo:'ວັນປີໃໝ່ສາກົນ',en:"New Year's Day"},'3-8':{lo:'ວັນແມ່ຍິງສາກົນ',en:"Women's Day"},'4-14':{lo:'ວັນປີໃໝ່ລາວ',en:'Lao New Year'},'4-15':{lo:'ວັນປີໃໝ່ລາວ',en:'Lao New Year'},'4-16':{lo:'ວັນປີໃໝ່ລາວ',en:'Lao New Year'},'5-1':{lo:'ວັນກຳມະກອນ',en:'Labour Day'},'12-2':{lo:'ວັນຊາດ',en:'National Day'}};
const BUDDHIST=543;
let calYear,calMonth,selectedDay;

/* ── Accurate Lao lunar calendar (Chula Sakarat / ປະຕິທິນລາວ) ── */
function laoLunar(input){
    const D=new Date(input.getFullYear(),input.getMonth(),input.getDate()); // calendar date, no time
    const aharakoune=y=>(y*292207+373)%800;
    const harakoune=y=>Math.floor((y*292207+373)/800)+1;
    const avomane=y=>(11*harakoune(y)+650)%692;
    const regularLeap=y=>(800-aharakoune(y))<=207;
    const bodethey=y=>{const ha=harakoune(y);return (ha+Math.floor((11*ha+650)/692))%30;};
    function jaisLeap(y){const b0=bodethey(y),b1=bodethey(y+1);return b0>24||b0<6||(b0===24&&b1===6)||(b0===25&&b1===5);}
    function isProtetinLeap(y){const a0=avomane(y),a1=avomane(y+1),normal=regularLeap(y);let v=normal&&a0<127;if(!normal){if(a0===137&&a1===0)v=false;else if(a0<138)v=true;}if(!v)v=isProtetinLeap(y-1)&&jaisLeap(y-1);return v;}
    function greatLeap(y){let v=isProtetinLeap(y);if(jaisLeap(y)&&v)v=false;return v;}
    function daysInYear(y){return jaisLeap(y)?384:greatLeap(y)?355:354;}
    function lunarDiffDays(end){let c=0,x=1970-638+1,yy=end.getFullYear()-638;if(x>yy){const t=x;x=yy;yy=t;}while(x<yy)c+=daysInYear(x++);return c;}
    function diffDays(end){return Math.abs(Math.round((end-286596e5)/86400000));}
    function monthsOfYear(y){const ath=jaisLeap(y),great=greatLeap(y),it=[];for(let i=0;i<12+(ath|0);i++){let j=i;if(ath&&j>=8)j--;it.push(29+((j%2!=0)|0)+((j==6&&great)|0));}return it;}
    const dt=new Date(D-7*3600*1000);
    let y=dt.getFullYear()-638;
    let day=Math.abs(diffDays(dt)-lunarDiffDays(dt))+1;
    const len=daysInYear(y);
    if(day>len){day-=len;y++;}
    let m=0;for(const mm of monthsOfYear(y)){if(day<=mm)break;day-=mm;m++;}
    const leap=jaisLeap(y);
    const phase=day<=15?'ຂຶ້ນ':'ແຮມ';          // waxing / waning
    const kham=day<=15?day:day-15;
    let mo=leap?(m<7?(m+1)+'':m===7?'8.1':m===8?'8.2':''+m):(m+1)+'';
    return {phase,kham,month:mo};
}
function zodiac(y){return ZODIAC_LO[(((y-4)%12)+12)%12]||'';}

function updateHero(date){
    const lang=localStorage.getItem(LANG_KEY)||'en';
    const dw=lang==='lo'?DW_FULL_LO:DW_FULL_EN;
    const mo=lang==='lo'?MO_LO:MO_EN;
    const by=date.getFullYear()+BUDDHIST;
    document.getElementById('calNum').textContent=date.getDate();
    document.getElementById('calDayName').textContent=(lang==='lo'?'ວັນ':'')+dw[date.getDay()];
    document.getElementById('calBudYear').textContent=mo[date.getMonth()]+' '+date.getFullYear()+' | '+by;
    const ll=laoLunar(date);
    const zd=zodiac(date.getFullYear());
    document.getElementById('calLunar').innerHTML=ll.phase+' '+ll.kham+' ຄ່ຳ, ເດືອນ '+ll.month+' ລາວ, ປີ'+zd;
}

function renderCalendar(){
    const lang=localStorage.getItem(LANG_KEY)||'en';
    const mo=lang==='lo'?MO_LO:MO_EN;
    const dw=lang==='lo'?DW_LO:DW_EN;
    const by=calYear+BUDDHIST;
    document.getElementById('calMonthLabel').textContent=mo[calMonth]+' '+calYear+' | '+by;
    const today=new Date();
    const first=new Date(calYear,calMonth,1).getDay();
    const dim=new Date(calYear,calMonth+1,0).getDate();
    const prev=new Date(calYear,calMonth,0).getDate();
    let h=dw.map((d,i)=>`<div class="cal-dow ${i===0?'s':i===6?'sa':''}">${d}</div>`).join('');
    for(let i=first-1;i>=0;i--)h+=`<div class="cal-cell other">${prev-i}</div>`;
    const hdays=[];
    for(let d=1;d<=dim;d++){
        const date=new Date(calYear,calMonth,d);
        const dow=date.getDay();
        const hk=`${calMonth+1}-${d}`;
        const hol=HOLIDAYS[hk];
        const isTod=d===today.getDate()&&calMonth===today.getMonth()&&calYear===today.getFullYear();
        const isSel=d===selectedDay&&calMonth===calMonth&&calYear===calYear;
        let cls='cal-cell';
        if(isTod)cls+=' today';
        if(dow===0&&!isTod)cls+=' sun';
        if(dow===6&&!isTod)cls+=' sat';
        if(hol)hdays.push({d,hol});
        const dot=hol&&!isTod?'<div class="cal-dot"></div>':'';
        h+=`<div class="${cls}" onclick="clickDay(${d})">${d}${dot}</div>`;
    }
    const rem=(first+dim)%7===0?0:7-(first+dim)%7;
    for(let d=1;d<=rem;d++)h+=`<div class="cal-cell other">${d}</div>`;
    document.getElementById('calGrid').innerHTML=h;
    const hl=document.getElementById('calHList');
    if(hdays.length){
        hl.innerHTML=hdays.map(({d,hol})=>`<div class="cal-hitem"><div class="cal-hdot"></div><span><b>${d}</b> — ${lang==='lo'?hol.lo:hol.en}</span></div>`).join('');
        hl.style.display='block';
    } else { hl.style.display='none'; }
}

function changeMonth(dir){
    calMonth+=dir;
    if(calMonth<0){calMonth=11;calYear--;}
    if(calMonth>11){calMonth=0;calYear++;}
    selectedDay=null;
    renderCalendar();
}
function clickDay(d){
    selectedDay=d;
    updateHero(new Date(calYear,calMonth,d));
    renderCalendar();
}

/* ═══ BIOMETRIC ═══ */
const CRED_KEY='seven_webauthn_cred';
function checkBioAvailable(){
    const isIOS=/iphone|ipad|ipod/i.test(navigator.userAgent);
    document.getElementById('bioIcon').textContent=isIOS?'🔒':'👆';
}
function checkBioForUser(login){
    try{const s=JSON.parse(localStorage.getItem(CRED_KEY)||'null');
    if(s&&s.username===login){const isIOS=/iphone|ipad|ipod/i.test(navigator.userAgent);document.getElementById('bioIcon').textContent=isIOS?'🔒':'👆';}}catch(e){}
}
function b64ToBuffer(b64){const bin=atob(b64.replace(/-/g,'+').replace(/_/g,'/'));return Uint8Array.from(bin,c=>c.charCodeAt(0));}
function bufToB64(buf){return btoa(String.fromCharCode(...new Uint8Array(buf))).replace(/\+/g,'-').replace(/\//g,'_').replace(/=/g,'');}
async function loginWithFaceID(){
    if(!window.PublicKeyCredential){alert('Biometrics not supported on this device.');return;}
    const lang=localStorage.getItem(LANG_KEY)||'en';

    /* figure out a username in case there's no local credential pointer */
    const stored=JSON.parse(localStorage.getItem(CRED_KEY)||'null');
    const typed=(document.getElementById('loginInput').value||'').trim();
    const last=getLastUser();
    const username=typed||(stored&&stored.username)||(last&&last.login)||'';

    const btn=document.getElementById('bioBtn');btn.style.opacity='.5';
    try{
        let challenge,credId;
        if(stored&&stored.credentialId){
            /* fast path — credential pointer saved in this browser */
            const ch=await (await fetch('../auth/webauthn_challenge.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'get_challenge',credential_id:stored.credentialId})})).json();
            if(!ch.success)throw new Error(ch.error);
            challenge=ch.challenge;credId=stored.credentialId;
        }else if(username){
            /* server path — look up the credential by username (survives data-clear; works across Safari & the home-screen app) */
            const ch=await (await fetch('../auth/webauthn_challenge.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'get_challenge_by_user',username})})).json();
            if(!ch.success)throw new Error(ch.error);
            challenge=ch.challenge;credId=ch.credential_id;
        }else{
            /* nothing to identify the account — ask for the username */
            btn.style.opacity='1';
            alert(lang==='lo'?'ກະລຸນາໃສ່ຊື່ຜູ້ໃຊ້ ຫຼື ອີເມວ ກ່ອນ ແລ້ວກົດ ສະແກນໜ້າ ອີກຄັ້ງ':'Enter your username or email first, then tap Face Scan again.');
            showForm();
            return;
        }

        const a=await navigator.credentials.get({publicKey:{challenge:b64ToBuffer(challenge),allowCredentials:[{id:b64ToBuffer(credId),type:'public-key',transports:['internal']}],timeout:60000,userVerification:'required'}});
        const v=await (await fetch('../auth/webauthn_challenge.php',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'verify',credential_id:bufToB64(a.rawId),authenticator_data:bufToB64(a.response.authenticatorData),client_data_json:bufToB64(a.response.clientDataJSON),signature:bufToB64(a.response.signature),challenge:challenge})})).json();
        if(v.success){
            /* save the pointer so next time uses the fast path */
            try{localStorage.setItem(CRED_KEY,JSON.stringify({credentialId:credId,username:username||(stored&&stored.username)||''}));}catch(e){}
            /* client-side navigation keeps us inside the standalone web app */
            window.location.href=v.redirect;
        }else throw new Error(v.error);
    }catch(e){btn.style.opacity='1';if(e.name!=='NotAllowedError')alert('Failed: '+(e.message||e));}
}

/* ═══ CALENDAR TOGGLE (expands upward, buttons stay put) ═══ */
let calOpen = false;
function toggleCal(){
    calOpen = !calOpen;
    const body = document.getElementById('calBody');
    const c1   = document.getElementById('chev1');
    const c2   = document.getElementById('chev2');
    body.classList.toggle('open', calOpen);
    c1.classList.toggle('up', calOpen);
    c2.classList.toggle('up', calOpen);
    /* lift the red panel above the notification cards while open */
    const rp=document.querySelector('.red-panel');
    if(rp) rp.classList.toggle('cal-on', calOpen);
}

/* ═══ AUTO-UPDATE ═══
   Poll the server's version token; when a newer login.php is live, reload to pick
   it up automatically (no need to close & reopen the app). We avoid reloading
   while the user is typing or has the password form open. */
const APP_VERSION = <?= json_encode($appVersion ?? '') ?>;
function safeToReload(){
    const ae=document.activeElement;
    if(ae && (ae.tagName==='INPUT' || ae.tagName==='TEXTAREA')) return false;
    const pw=document.getElementById('pwForm');
    if(pw && pw.style.display==='block') return false;
    return true;
}
async function checkAppVersion(){
    try{
        const r=await fetch('app_version.php?_='+Date.now(),{cache:'no-store'});
        if(!r.ok) return;
        const v=(await r.text()).trim();
        if(v && APP_VERSION && v!==APP_VERSION && safeToReload()){ location.reload(); }
    }catch(e){}
}
setInterval(checkAppVersion,30000);

/* ═══ INIT ═══ */
/* Identify the remembered/typed user and start the notification watcher. */
function bootIdentify(){
    const v=document.getElementById('loginInput').value;
    if(v.trim().length>=2){
        lookupUser(v.trim());
    }else{
        const last=getLastUser();
        if(last){
            applyUser(last);                       // show last user instantly (starts watchNotif)
            if(last.login)lookupUser(last.login);  // refresh photo/name/QR from server
        }
    }
}

/* Immediately re-check for new payments. Used when the app is reopened or brought
   back to the foreground on iOS, where background timers are paused and the normal
   'load' event may not fire again on a standalone home-screen app. */
function refreshNotifNow(){
    if(notifLogin){
        checkLoginNotif(notifLogin);   // we already know the account → just re-check now
    }else{
        bootIdentify();                // no account yet → try to identify the remembered user
    }
}

window.addEventListener('load',()=>{
    /* default to English unless the user explicitly chose a language via the globe.
       Persist it so the calendar + notifications (which read the stored value) match. */
    const saved=localStorage.getItem('seven_lang_set') ? (localStorage.getItem(LANG_KEY)||'en') : 'en';
    const now=new Date();
    calYear=now.getFullYear();calMonth=now.getMonth();selectedDay=now.getDate();
    setLang(saved);
    checkBioAvailable();
    bootIdentify();
});

/* iOS standalone PWA: re-check the instant the app becomes visible / is reopened,
   so a payment that arrived while the app was closed shows up right away. */
document.addEventListener('visibilitychange',()=>{ if(!document.hidden){ refreshNotifNow(); checkAppVersion(); } });
window.addEventListener('pageshow',()=>{ refreshNotifNow(); checkAppVersion(); });
window.addEventListener('focus',()=>{ refreshNotifNow(); checkAppVersion(); });

if('serviceWorker' in navigator){navigator.serviceWorker.register('/seven-credit-system/service-worker.js',{scope:'/seven-credit-system/'});}
</script>
</body>
</html>