<?php
// Variables: $contentView, $data, $user, $appName, $siteKey, $logoUrl
$isPartial = is_ajax();
if ($isPartial) {
  require __DIR__ . '/' . $contentView . '.php';
  return;
}

$cfgBasePath = rtrim(($config['app']['base_path'] ?? ''), '/');

// Fallback: infer base path from the running script
$autoBase = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '')), '/');
if ($autoBase === '/') $autoBase = '';

// Use one effective base path everywhere
$basePath = $cfgBasePath !== '' ? $cfgBasePath : $autoBase;
$assetBase = $basePath;

$isLoginPage = ($contentView === 'pages/login');
?>
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title><?= h($appName) ?></title>

  <!-- Select2 -->
  <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
  <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>

  <script src="https://cdn.tailwindcss.com"></script>
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: {
            wsc: { 500: '#ff7a00', 600: '#e96f00', 700: '#cc6100' },
            ink: {
              50:'#f8fafc', 100:'#f1f5f9', 200:'#e2e8f0', 300:'#cbd5e1', 400:'#94a3b8',
              500:'#64748b', 600:'#475569', 700:'#334155', 800:'#1f2937', 900:'#0f172a'
            }
          }
        }
      }
    }
  </script>

  <script src="https://www.google.com/recaptcha/api.js?render=explicit&onload=recaptchaLoaded" async defer></script>

  <style>
    body{
      background:
        radial-gradient(circle at top right, rgba(255,122,0,0.10), transparent 28%),
        radial-gradient(circle at bottom left, rgba(255,122,0,0.06), transparent 24%),
        #f8fafc !important;
    }

    .glass{
      background: rgba(255,255,255,0.96);
      border: 1px solid rgba(233,111,0,0.14);
      box-shadow:
        0 10px 30px rgba(15,23,42,0.06),
        0 1px 0 rgba(255,255,255,0.8) inset;
    }

    .glass-strong{
      background: rgba(255,255,255,0.98);
      border: 1px solid rgba(233,111,0,0.18);
      box-shadow:
        0 18px 45px rgba(15,23,42,0.08),
        0 1px 0 rgba(255,255,255,0.85) inset;
    }

    /* =========================
       PREMIUM COMMAND TOOLBAR
       ========================= */

    .command-toolbar{
      position:relative;
      overflow:visible;
      background:
        radial-gradient(circle at top right, rgba(255,255,255,.12), transparent 24%),
        radial-gradient(circle at left bottom, rgba(255,255,255,.06), transparent 30%),
        linear-gradient(135deg, #0b1220 0%, #172033 42%, #7c2d12 100%);
      border-bottom:1px solid rgba(255,255,255,.08);
      box-shadow:
        0 22px 48px rgba(15,23,42,.20),
        inset 0 1px 0 rgba(255,255,255,.08);
    }

    .command-toolbar::after{
      content:"";
      position:absolute;
      right:-60px;
      top:-70px;
      width:220px;
      height:220px;
      border-radius:999px;
      background:radial-gradient(circle, rgba(255,255,255,.12), transparent 68%);
      pointer-events:none;
    }

    .brand-shell{
      display:flex;
      align-items:center;
      gap:.9rem;
      min-width:0;
    }

    .brand-logo-wrap{
      position:relative;
      height:54px;
      width:54px;
      flex:0 0 auto;
      display:grid;
      place-items:center;
      border-radius:18px;
      background:linear-gradient(180deg, rgba(255,255,255,.14), rgba(255,255,255,.06));
      border:1px solid rgba(255,255,255,.10);
      box-shadow:
        0 16px 28px rgba(0,0,0,.18),
        inset 0 1px 0 rgba(255,255,255,.10);
      overflow:hidden;
    }

    .brand-title{
      color:#fff;
      font-size:1.2rem;
      line-height:1.1;
      font-weight:900;
      letter-spacing:.01em;
      text-shadow:0 1px 0 rgba(0,0,0,.18);
    }

    .brand-title .brand-accent{
      color:#fdba74;
      font-weight:800;
    }

    .brand-sub{
      margin-top:.18rem;
      color:rgba(255,255,255,.66);
      font-size:.75rem;
      letter-spacing:.12em;
      text-transform:uppercase;
      font-weight:700;
    }

    .command-nav{
      display:flex;
      align-items:center;
      gap:.45rem;
    }

    .command-link{
      position:relative;
      display:inline-flex;
      align-items:center;
      justify-content:center;
      min-height:44px;
      padding:.7rem 1rem;
      border-radius:16px;
      color:rgba(255,255,255,.92);
      font-size:.92rem;
      font-weight:800;
      letter-spacing:.01em;
      background:rgba(255,255,255,.06);
      border:1px solid rgba(255,255,255,.08);
      box-shadow:inset 0 1px 0 rgba(255,255,255,.06);
      transition:
        transform .18s ease,
        background .18s ease,
        border-color .18s ease,
        box-shadow .18s ease,
        color .18s ease;
    }

    .command-link:hover{
      color:#fff;
      background:rgba(255,255,255,.12);
      border-color:rgba(255,255,255,.14);
      box-shadow:
        0 10px 24px rgba(0,0,0,.14),
        inset 0 1px 0 rgba(255,255,255,.10);
      transform:translateY(-1px);
    }

    .command-link.active{
      color:#fff;
      background:linear-gradient(180deg, rgba(255,255,255,.16), rgba(255,255,255,.10));
      border-color:rgba(255,255,255,.18);
      box-shadow:
        0 14px 28px rgba(0,0,0,.16),
        0 8px 20px rgba(249,115,22,.14);
    }

    .command-link.library-trigger{
      padding-right:1.15rem;
    }

    .library-menu{
      background:
        linear-gradient(180deg, rgba(10,15,25,.98), rgba(23,32,51,.98));
      border:1px solid rgba(255,255,255,.10);
      box-shadow:
        0 22px 50px rgba(0,0,0,.36),
        inset 0 1px 0 rgba(255,255,255,.06);
      z-index:99999;
      backdrop-filter:blur(10px);
      -webkit-backdrop-filter:blur(10px);
    }

    .library-menu-head{
      padding:.9rem 1rem;
      border-bottom:1px solid rgba(255,255,255,.08);
      background:rgba(255,255,255,.04);
    }

    .library-menu-kicker{
      color:#fdba74;
      font-size:.67rem;
      text-transform:uppercase;
      letter-spacing:.14em;
      font-weight:800;
    }

    .library-menu-sub{
      color:rgba(255,255,255,.62);
      font-size:.72rem;
      margin-top:.2rem;
    }

    .library-item{
      display:block;
      padding:.9rem 1rem;
      color:rgba(255,255,255,.9);
      font-size:.9rem;
      font-weight:700;
      transition:background .18s ease, color .18s ease, padding-left .18s ease;
    }

    .library-item:hover{
      background:rgba(255,255,255,.08);
      color:#fff;
      padding-left:1.15rem;
    }

    .user-trigger{
      display:inline-flex;
      align-items:center;
      gap:.65rem;
      min-height:46px;
      padding:.65rem 1rem;
      border-radius:18px;
      color:#fff;
      background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.06));
      border:1px solid rgba(255,255,255,.10);
      box-shadow:
        0 14px 28px rgba(0,0,0,.18),
        inset 0 1px 0 rgba(255,255,255,.08);
      transition:all .18s ease;
    }

    .user-trigger:hover{
      background:linear-gradient(180deg, rgba(255,255,255,.15), rgba(255,255,255,.08));
      transform:translateY(-1px);
    }

    .user-avatar-badge{
      height:30px;
      width:30px;
      border-radius:999px;
      display:grid;
      place-items:center;
      background:linear-gradient(180deg, #fdba74, #f97316);
      color:#7c2d12;
      font-size:.78rem;
      font-weight:900;
      box-shadow:0 8px 18px rgba(249,115,22,.28);
    }

    .user-name-wrap{
      text-align:left;
      line-height:1.05;
    }

    .user-name-main{
      font-size:.84rem;
      font-weight:800;
      color:#fff;
    }

    .user-name-sub{
      margin-top:.14rem;
      font-size:.65rem;
      letter-spacing:.12em;
      text-transform:uppercase;
      color:rgba(255,255,255,.58);
      font-weight:700;
    }

    .user-menu-solid{
      background:linear-gradient(180deg, rgba(10,15,25,.985), rgba(23,32,51,.985));
      color:rgba(255,255,255,.94);
      border:1px solid rgba(255,255,255,.10);
      box-shadow:0 24px 55px rgba(0,0,0,.40);
      z-index:99999;
      backdrop-filter:none !important;
      -webkit-backdrop-filter:none !important;
    }
    .user-menu-solid .menu-head{
      background:rgba(255,255,255,.04);
      border-bottom:1px solid rgba(255,255,255,.08);
    }
    .user-menu-solid a:hover{
      background:rgba(255,255,255,.08);
    }
    .user-menu-solid .menu-muted{
      color:rgba(255,255,255,.70);
    }

    .mobile-command-btn{
      display:inline-flex;
      align-items:center;
      justify-content:center;
      height:46px;
      width:46px;
      border-radius:16px;
      color:#fff;
      background:linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,.06));
      border:1px solid rgba(255,255,255,.10);
      box-shadow:
        0 14px 28px rgba(0,0,0,.18),
        inset 0 1px 0 rgba(255,255,255,.08);
      transition:all .18s ease;
    }

    .mobile-command-btn:hover{
      background:linear-gradient(180deg, rgba(255,255,255,.15), rgba(255,255,255,.08));
      transform:translateY(-1px);
    }

    .mobile-command-sheet{
      background:
        linear-gradient(180deg, rgba(10,15,25,.985), rgba(23,32,51,.985));
      border-top:1px solid rgba(255,255,255,.08);
      box-shadow:0 -20px 48px rgba(0,0,0,.32);
    }

    .mobile-command-item{
      display:flex;
      align-items:center;
      justify-content:space-between;
      width:100%;
      padding:1rem 1rem;
      border-radius:18px;
      color:#fff;
      font-size:.95rem;
      font-weight:800;
      background:rgba(255,255,255,.05);
      border:1px solid rgba(255,255,255,.08);
      transition:all .18s ease;
    }

    .mobile-command-item:hover{
      background:rgba(255,255,255,.10);
      transform:translateY(-1px);
    }

    .mobile-command-kicker{
      color:#fdba74;
      font-size:.68rem;
      letter-spacing:.14em;
      text-transform:uppercase;
      font-weight:800;
    }

    .mobile-command-sub{
      color:rgba(255,255,255,.62);
      font-size:.72rem;
      margin-top:.15rem;
    }

    .command-mobile-note{
      color:rgba(255,255,255,.60);
      font-size:.72rem;
      font-weight:700;
      letter-spacing:.10em;
      text-transform:uppercase;
    }

    .form-label,
    label.form-label{
      color:#334155 !important;
      font-weight:600;
    }

    input[type="text"],
    input[type="email"],
    input[type="password"],
    input[type="number"],
    input[type="date"],
    input[type="search"],
    textarea,
    select{
      background:#fff !important;
      color:#0f172a !important;
      border:1px solid rgba(15,23,42,0.12) !important;
      border-radius:16px;
      transition:border-color .18s ease, box-shadow .18s ease, background .18s ease;
    }

    input::placeholder,
    textarea::placeholder{
      color:#94a3b8 !important;
    }

    input:focus,
    textarea:focus,
    select:focus{
      outline:none !important;
      border-color: rgba(233,111,0,0.55) !important;
      box-shadow: 0 0 0 4px rgba(255,122,0,0.12) !important;
      background:#fffdfb !important;
    }

    .btn-primary,
    button[type="submit"],
    a.btn-primary{
      background: linear-gradient(135deg, #ff8a1f 0%, #f97316 55%, #e96f00 100%) !important;
      color: #ffffff !important;
      border: 1px solid rgba(204,97,0,0.75) !important;
      box-shadow:
        0 10px 22px rgba(249,115,22,0.22),
        0 1px 0 rgba(255,255,255,0.18) inset;
      font-weight: 700;
    }

    .btn-primary:hover,
    button[type="submit"]:hover,
    a.btn-primary:hover{
      background: linear-gradient(135deg, #ff981f 0%, #fb7c1b 55%, #d95f00 100%) !important;
      box-shadow:
        0 14px 28px rgba(249,115,22,0.28),
        0 1px 0 rgba(255,255,255,0.20) inset;
      transform: translateY(-1px);
    }

    .select2-container { width: 100% !important; }
    .select2-container .select2-selection--single{
      height:46px;
      border-radius:16px;
      border:1px solid rgba(15,23,42,0.12) !important;
      background:#fff !important;
      color:#0f172a !important;
      display:flex;
      align-items:center;
      outline:none !important;
    }
    .select2-container--default .select2-selection--single .select2-selection__rendered{
      color:#0f172a !important;
      line-height:44px;
      padding-left:14px;
      padding-right:36px;
    }
    .select2-container--default .select2-selection--single .select2-selection__placeholder{
      color: rgba(15,23,42,0.55) !important;
    }
    .select2-dropdown {
      border-radius: 16px;
      border: 1px solid rgba(0,0,0,0.10);
      overflow: hidden;
      z-index: 9999;
    }

    .sheen-wrap{ position: relative; }
    .sheen{
      position: absolute;
      inset: 0;
      pointer-events: none;
      background: linear-gradient(
        120deg,
        transparent 0%,
        rgba(255, 122, 0, 0.22) 50%,
        transparent 100%
      );
      transform: translateX(-120%) skewX(-20deg);
      animation: logoSheenLoop 3.0s ease-in-out infinite !important;
    }
    @keyframes logoSheenLoop{
      0%   { transform: translateX(-120%) skewX(-20deg); opacity: 0; }
      20%  { opacity: 1; }
      45%  { transform: translateX(140%) skewX(-20deg); opacity: 0; }
      100% { transform: translateX(140%) skewX(-20deg); opacity: 0; }
    }

    .drawer-backdrop{
      background: rgba(15,23,42,0.45);
      backdrop-filter: blur(2px);
      -webkit-backdrop-filter: blur(2px);
    }
    .drawer-panel{
      width:min(560px, 96vw);
      height:100%;
      background:linear-gradient(180deg, rgba(255,255,255,0.98), rgba(255,255,255,0.96));
      border-left:1px solid rgba(233,111,0,0.16);
      box-shadow:-14px 0 40px rgba(15,23,42,0.14);
      overflow:auto;
    }
    .drawer-card{
      background:#fff;
      border:1px solid rgba(15,23,42,0.08);
      border-radius:18px;
    }
    .drawer-chip{
      display:inline-flex;
      align-items:center;
      gap:.4rem;
      padding:.35rem .7rem;
      border-radius:999px;
      font-size:.75rem;
      font-weight:700;
      background:#fff7ed;
      color:#c2410c;
      border:1px solid rgba(249,115,22,.18);
    }

    .shipment-row{
      cursor:pointer;
      transition:background .16s ease, transform .16s ease, box-shadow .16s ease;
    }
    .shipment-row:hover{
      background:#fff7ed;
    }

    .child-row{
      cursor:pointer;
      transition:background .16s ease;
    }
    .child-row:hover{
      background:#fff7ed;
    }

    .tab-pill{
      display:inline-flex;
      align-items:center;
      justify-content:center;
      padding:.65rem 1rem;
      border-radius:999px;
      font-weight:700;
      border:1px solid rgba(15,23,42,.08);
      background:#fff;
      color:#475569;
      transition:all .16s ease;
    }
    .tab-pill.active{
      background:linear-gradient(135deg, #ffedd5 0%, #fff7ed 100%);
      color:#c2410c;
      border-color:rgba(249,115,22,.18);
      box-shadow:0 10px 24px rgba(249,115,22,.10);
    }

    <?php if ($isLoginPage): ?>
    html, body{ min-height:100%; }
    body.login-screen{ overflow:hidden !important; }
    .login-shell{
      min-height:100vh;
      display:flex;
      align-items:center;
      justify-content:center;
      padding:24px;
      position:relative;
      z-index:10;
    }
    <?php endif; ?>
  </style>
  <meta name="wsc-base-path" content="<?= htmlspecialchars($basePath ?? '', ENT_QUOTES, 'UTF-8') ?>">
</head>

<body class="<?= $isLoginPage ? 'login-screen min-h-screen text-ink-900' : 'min-h-screen bg-ink-50 text-ink-900' ?>">

<?php if ($isLoginPage): ?>
  <div class="login-shell">
    <?php require __DIR__ . '/' . $contentView . '.php'; ?>
  </div>
<?php else: ?>
  <div class="max-w-7xl mx-auto px-4 py-6">
    <div class="glass-strong rounded-3xl overflow-hidden">
      <div class="command-toolbar px-4 md:px-6 py-5">
        <div class="flex items-center justify-between gap-4">
          <div class="brand-shell min-w-0">
            <div class="brand-logo-wrap">
              <img
                src="<?= h($assetBase) ?>/assets/logo.png?v=<?= file_exists($_SERVER['DOCUMENT_ROOT'] . $assetBase . '/assets/logo.png') ? filemtime($_SERVER['DOCUMENT_ROOT'] . $assetBase . '/assets/logo.png') : time() ?>"
                alt="WSC Logistics"
                class="h-10 w-10 object-contain relative z-[2]"
              />
              <div class="sheen"></div>
            </div>

            <div class="min-w-0">
              <div class="brand-title truncate">
                WSC Logistics <span class="brand-accent"></span>
              </div>
              <div class="brand-sub truncate">
                Freight Tracking · Billing · Release Control
              </div>
            </div>
          </div>

          <div class="flex items-center gap-3">
            <nav class="command-nav hidden md:flex">
              <a
                class="command-link"
                href="<?= h($basePath) ?>/shipments"
                data-nav
                data-nav-key="shipments">
                Manifests
              </a>

              <div class="relative">
                <button
                  id="libraryMenuBtn"
                  type="button"
                  class="command-link library-trigger"
                  data-nav-key="library">
                  Library ▾
                </button>

                <div id="libraryMenu" class="hidden absolute left-0 mt-3 w-64 library-menu rounded-2xl overflow-hidden">
                  <div class="library-menu-head">
                    <div class="library-menu-kicker">Reference Data</div>
                    <div class="library-menu-sub">Master records and administration tools</div>
                  </div>

                  <a class="library-item"
                     href="<?= h($basePath) ?>/customers"
                     data-nav>
                    Customers
                  </a>

                  <a class="library-item"
                     href="<?= h($basePath) ?>/shipping-lines"
                     data-nav>
                    SHP/Lines
                  </a>

                  <a class="library-item"
                     href="<?= h($basePath) ?>/users"
                     data-nav>
                    Users
                  </a>
                </div>
              </div>
            </nav>

            <div class="hidden lg:block command-mobile-note">
              &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
            </div>

            <button id="mobileCommandBtn" type="button" class="mobile-command-btn md:hidden" aria-label="Open menu">
              ☰
            </button>

            <?php if ($user): ?>
            <div class="relative z-[10000] hidden md:block">
              <button id="userMenuBtn" class="user-trigger">
                <span class="user-avatar-badge">
                  <?= h(strtoupper(substr(trim((string)$user['full_name']), 0, 1) ?: 'U')) ?>
                </span>
                <span class="user-name-wrap">
                  <span class="block user-name-main"><?= h($user['full_name']) ?></span>
                  <span class="block user-name-sub">Account</span>
                </span>
                <span class="text-white/80 font-black">▾</span>
              </button>

              <div id="userMenu" class="hidden absolute right-0 mt-3 w-64 user-menu-solid rounded-2xl overflow-hidden">
                <div class="px-4 py-3 text-sm menu-head">
                  <div class="font-semibold"><?= h($user['full_name']) ?></div>
                  <div class="text-xs menu-muted"><?= h($user['email']) ?></div>
                </div>

                <a class="block px-4 py-3 text-sm transition"
                   href="<?= h($basePath) ?>/change-password" data-nav>
                  Change Password
                </a>

                <a class="block px-4 py-3 text-sm transition"
                   href="<?= h($basePath) ?>/logout" data-nav>
                  Logout
                </a>
              </div>
            </div>
            <?php else: ?>
              <a class="command-link hidden md:inline-flex"
                 href="<?= h($basePath) ?>/login"
                 data-nav>
                Login
              </a>
            <?php endif; ?>
          </div>
        </div>

        <!-- MOBILE COMMAND MENU -->
        <div id="mobileCommandMenu" class="hidden md:hidden mt-5">
          <div class="mobile-command-sheet rounded-3xl p-4 space-y-3">
            <div class="pb-1">
              <div class="mobile-command-kicker">Command Menu</div>
              <div class="mobile-command-sub">Navigate the platform with the same premium control surface.</div>
            </div>

            <a class="mobile-command-item"
               href="<?= h($basePath) ?>/shipments"
               data-nav>
              <span>Manifests</span>
              <span class="text-white/60">→</span>
            </a>

            <a class="mobile-command-item"
               href="<?= h($basePath) ?>/customers"
               data-nav>
              <span>
                <span class="block">Customers</span>
                <span class="block text-[11px] text-white/55 font-semibold uppercase tracking-[0.14em] mt-1">Library</span>
              </span>
              <span class="text-white/60">→</span>
            </a>

            <a class="mobile-command-item"
               href="<?= h($basePath) ?>/shipping-lines"
               data-nav>
              <span>
                <span class="block">SHP/Lines</span>
                <span class="block text-[11px] text-white/55 font-semibold uppercase tracking-[0.14em] mt-1">Library</span>
              </span>
              <span class="text-white/60">→</span>
            </a>

            <a class="mobile-command-item"
               href="<?= h($basePath) ?>/users"
               data-nav>
              <span>
                <span class="block">Users</span>
                <span class="block text-[11px] text-white/55 font-semibold uppercase tracking-[0.14em] mt-1">Library</span>
              </span>
              <span class="text-white/60">→</span>
            </a>

            <?php if ($user): ?>
              <div class="pt-2 border-t border-white/10"></div>

              <a class="mobile-command-item"
                 href="<?= h($basePath) ?>/change-password"
                 data-nav>
                <span>Change Password</span>
                <span class="text-white/60">→</span>
              </a>

              <a class="mobile-command-item"
                 href="<?= h($basePath) ?>/logout"
                 data-nav>
                <span>Logout</span>
                <span class="text-white/60">→</span>
              </a>
            <?php else: ?>
              <a class="mobile-command-item"
                 href="<?= h($basePath) ?>/login"
                 data-nav>
                <span>Login</span>
                <span class="text-white/60">→</span>
              </a>
            <?php endif; ?>
          </div>
        </div>
      </div>

      <div class="px-6 py-6">
        <div id="app-content">
          <?php require __DIR__ . '/' . $contentView . '.php'; ?>
        </div>
      </div>
    </div>
  </div>

  <!-- GLOBAL HBL DETAIL DRAWER -->
  <div id="hblDrawer" class="hidden fixed inset-0 z-[120000]">
    <div class="absolute inset-0 drawer-backdrop" data-close-hbl-drawer></div>
    <div class="absolute inset-y-0 right-0 drawer-panel">
      <div class="sticky top-0 z-10 bg-white/95 backdrop-blur border-b border-ink-200 px-5 py-4 flex items-center justify-between">
        <div>
          <div class="text-xs uppercase tracking-[0.2em] text-ink-500 font-bold">House BL Details</div>
          <div id="hblDrawerTitle" class="text-lg font-extrabold text-ink-900">Loading...</div>
        </div>
        <button type="button" class="h-10 w-10 rounded-2xl border border-ink-200 hover:bg-orange-50" data-close-hbl-drawer>✕</button>
      </div>

      <div id="hblDrawerBody" class="p-5">
        <div class="text-sm text-ink-500">Loading...</div>
      </div>
    </div>
  </div>
  
    <!-- GLOBAL PAYMENT MODAL -->
  <div id="payModal" class="hidden fixed inset-0 z-[130000] items-center justify-center p-4">
    <div class="absolute inset-0 drawer-backdrop" data-close-pay-modal></div>

    <div class="relative w-full max-w-2xl rounded-3xl bg-white border border-ink-200 shadow-2xl overflow-hidden">
      <div class="px-5 py-4 border-b border-ink-200 flex items-center justify-between">
        <div>
          <div class="text-xs uppercase tracking-[0.2em] text-ink-500 font-bold">Payment Entry</div>
          <div class="text-lg font-extrabold text-ink-900">Create Payment &amp; Open Receipt</div>
        </div>
        <button type="button"
                class="h-10 w-10 rounded-2xl border border-ink-200 hover:bg-orange-50"
                data-close-pay-modal>✕</button>
      </div>

      <form id="payForm" class="p-5 space-y-4">
        <input type="hidden" name="house_bl_id" id="pay_house_bl_id">
        <input type="hidden" name="invoice_id" id="pay_invoice_id">

        <div id="pay_err" class="hidden rounded-2xl border border-red-200 bg-red-50 text-red-700 px-4 py-3 text-sm"></div>
        <div id="pay_hint" class="text-sm text-ink-500"></div>

        <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
          <div>
            <label class="form-label">Payment Date</label>
            <input id="pay_date" name="pay_date" class="mt-1 w-full px-4 py-3 rounded-2xl" placeholder="DD/MM/YYYY">
          </div>

          <div>
            <label class="form-label">Method</label>
            <select id="pay_method" name="method" class="mt-1 w-full px-4 py-3 rounded-2xl">
              <option value="">Select method</option>
              <option value="CASH">Cash</option>
              <option value="BANK">Bank</option>
              <option value="M-PESA">M-PESA</option>
              <option value="CHEQUE">Cheque</option>
            </select>
          </div>

          <div>
            <label class="form-label">Reference No</label>
            <input id="pay_ref_no" name="ref_no" class="mt-1 w-full px-4 py-3 rounded-2xl" placeholder="Transaction / bank / cheque reference">
          </div>

          <div>
            <label class="form-label">Currency</label>
            <select id="pay_currency" name="currency" class="mt-1 w-full px-4 py-3 rounded-2xl">
              <option value="USD">USD</option>
              <option value="KES">KES</option>
            </select>
          </div>

          <div>
            <label class="form-label">Amount</label>
            <input id="pay_amount" name="amount" type="number" step="0.01" min="0" class="mt-1 w-full px-4 py-3 rounded-2xl" placeholder="0.00">
          </div>

          <div id="fx_wrap" class="hidden">
            <label class="form-label">USD / KES Rate</label>
            <input id="pay_fx_rate" name="fx_rate" type="number" step="0.000001" min="0" class="mt-1 w-full px-4 py-3 rounded-2xl" placeholder="Exchange rate">
          </div>
        </div>

        <div class="pt-2 flex items-center justify-end gap-3">
          <button type="button"
                  class="px-4 py-3 rounded-2xl border border-ink-200 bg-white hover:bg-orange-50 font-semibold"
                  data-close-pay-modal>
            Cancel
          </button>

          <button type="submit" class="px-5 py-3 rounded-2xl btn-primary">
            Save Payment &amp; Open Receipt
          </button>
        </div>
      </form>
    </div>
  </div>
  
<?php endif; ?>

<script>
  const BASE = <?= json_encode($basePath, JSON_UNESCAPED_SLASHES) ?>;
  const IS_LOGIN_PAGE = <?= json_encode($isLoginPage) ?>;

  (function(){

    // ---------------------------------------
    // GLOBAL RECEIPT HANDLER (NON-LOGIN ONLY)
    // ---------------------------------------
    if (!IS_LOGIN_PAGE && !window.__wscReceiptGlobalInit) {
      window.__wscReceiptGlobalInit = true;

      function getBasePath(){
        if (typeof window.WSC_BASE_PATH === 'string') return window.WSC_BASE_PATH;
        const meta = document.querySelector('meta[name="wsc-base-path"]');
        if (meta) return meta.getAttribute('content') || '';
        return BASE || '';
      }

      function $(id){ return document.getElementById(id); }

      function ensureState(){
        if (!window.__wscReceiptState) {
          window.__wscReceiptState = { lastBalanceUsd: 0 };
        }
        return window.__wscReceiptState;
      }

      function setErr(msg){
        const err = $('pay_err');
        if (!err) return;
        err.textContent = msg || '';
        err.classList.toggle('hidden', !msg);
      }

      function setHint(msg){
        const hint = $('pay_hint');
        if (!hint) return;
        hint.textContent = msg || '';
      }

      function formatDMY(dateObj){
        const dd = String(dateObj.getDate()).padStart(2, '0');
        const mm = String(dateObj.getMonth() + 1).padStart(2, '0');
        const yyyy = dateObj.getFullYear();
        return `${dd}/${mm}/${yyyy}`;
      }

      function normalizeDateForApi(v){
        v = String(v || '').trim();

        let m = v.match(/^(\d{2})\/(\d{2})\/(\d{4})$/);
        if (m) return `${m[3]}-${m[2]}-${m[1]}`;

        m = v.match(/^(\d{4})-(\d{2})-(\d{2})$/);
        if (m) return v;

        return v;
      }

      function formatDateTyping(input){
        if (!input) return;
        let v = input.value.replace(/[^\d]/g, '').slice(0, 8);
        if (v.length > 4) {
          v = v.slice(0,2) + '/' + v.slice(2,4) + '/' + v.slice(4);
        } else if (v.length > 2) {
          v = v.slice(0,2) + '/' + v.slice(2);
        }
        input.value = v;
      }

      function payModalShow(){
        const m = $('payModal');
        if (!m) {
          alert('Payment modal markup not found on this page.');
          return false;
        }
        m.classList.remove('hidden');
        m.classList.add('flex');
        document.documentElement.style.overflow = 'hidden';
        return true;
      }

      function payModalHide(){
        const m = $('payModal');
        if (!m) return;
        m.classList.add('hidden');
        m.classList.remove('flex');
        document.documentElement.style.overflow = '';
      }

      async function fetchFxRate(){
        const fxInput = $('pay_fx_rate');
        if (!fxInput) return 0;

        const existing = Number(fxInput.value || 0);
        if (existing > 0) return existing;

        try{
          const res = await fetch('https://open.er-api.com/v6/latest/USD', { cache: 'no-store' });
          const data = await res.json().catch(() => ({}));
          const rate = Number(data?.rates?.KES || 0);

          if (!rate) throw new Error('KES rate unavailable');

          fxInput.value = rate.toFixed(6);
          return rate;
        }catch(e){
          setErr('Could not auto-load USD/KES rate. Enter it manually.');
          return 0;
        }
      }

      function toggleFxVisibility(){
        const state = ensureState();
        const ccy = $('pay_currency') ? $('pay_currency').value : 'USD';
        const wrap = $('fx_wrap');

        if (wrap) wrap.classList.toggle('hidden', ccy !== 'KES');

        if (ccy === 'USD') {
          setHint(state.lastBalanceUsd > 0 ? `Invoice balance: USD ${state.lastBalanceUsd.toFixed(2)}` : '');
        } else {
          setHint(state.lastBalanceUsd > 0 ? `Invoice balance: USD ${state.lastBalanceUsd.toFixed(2)}. Enter USD/KES rate to auto-fill KES amount.` : '');
        }
      }

      function autoFillAmountFromBalance(){
        const state = ensureState();
        const amountInput = $('pay_amount');
        const ccyInput = $('pay_currency');
        const fxInput = $('pay_fx_rate');

        if (!amountInput || !ccyInput) return;
        if (state.lastBalanceUsd <= 0) return;

        const ccy = ccyInput.value || 'USD';

        if (ccy === 'USD') {
          amountInput.value = state.lastBalanceUsd.toFixed(2);
          return;
        }

        const fx = Number(fxInput ? fxInput.value : 0);
        if (fx > 0) {
          amountInput.value = (state.lastBalanceUsd * fx).toFixed(2);
        }
      }

      async function getBalanceInfo(houseBlId){
        const base = getBasePath();
        const url = (base ? base : '') + '/api/invoices/balance?house_bl_id=' + encodeURIComponent(houseBlId);
        const res = await fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
        const data = await res.json().catch(() => ({}));

        if (!res.ok || !data.success){
          throw new Error(data.message || ('HTTP ' + res.status));
        }

        return data;
      }

      async function getLatestPaymentInfo(houseBlId){
        const base = getBasePath();
        const url = (base ? base : '') + '/api/payments/latest?house_bl_id=' + encodeURIComponent(houseBlId);
        const res = await fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
        const data = await res.json().catch(() => ({}));

        if (!res.ok || !data.success){
          throw new Error(data.message || ('HTTP ' + res.status));
        }

        return data;
      }

      async function fillBalance(houseBlId){
        const state = ensureState();
        try{
          const data = await getBalanceInfo(houseBlId);
          state.lastBalanceUsd = Number(data.balance || 0);

          toggleFxVisibility();
          autoFillAmountFromBalance();
          setErr('');
        }catch(e){
          state.lastBalanceUsd = 0;
          setErr('Could not auto-load invoice balance. Enter amount manually. (' + (e.message || 'Error') + ')');
          setHint('');
        }
      }

      function openReceiptByPayment(houseBlId, paymentId){
        const base = getBasePath();
        const url = (base ? base : '') + '/doc/receipt?payment_id='
                  + encodeURIComponent(paymentId)
                  + '&house_bl_id=' + encodeURIComponent(houseBlId);
        window.open(url, '_blank', 'noopener');
      }

      async function payModalOpen(houseBlId, invoiceId, knownBalanceUsd){
        const state = ensureState();
        const inv = Number(invoiceId || 0);

        if (!houseBlId) {
          alert('Missing HBL id');
          return false;
        }

        if (!inv || inv <= 0){
          alert('No invoice found for this HBL. Generate an invoice first.');
          return false;
        }

        if (!$('pay_house_bl_id') || !$('pay_invoice_id')) {
          alert('Payment modal fields not found on this page.');
          return false;
        }

        $('pay_house_bl_id').value = String(houseBlId);
        $('pay_invoice_id').value  = String(inv);

        if ($('pay_date')) $('pay_date').value = formatDMY(new Date());
        if ($('pay_method')) $('pay_method').value = '';
        if ($('pay_ref_no')) $('pay_ref_no').value = '';
        if ($('pay_amount')) $('pay_amount').value = '';
        if ($('pay_currency')) $('pay_currency').value = 'USD';
        if ($('pay_fx_rate')) $('pay_fx_rate').value = '';

        setErr('');
        setHint('');

        if (typeof knownBalanceUsd === 'number') {
          state.lastBalanceUsd = knownBalanceUsd;
          toggleFxVisibility();
          autoFillAmountFromBalance();
        } else {
          state.lastBalanceUsd = 0;
          toggleFxVisibility();
          await fillBalance(houseBlId);
        }

        payModalShow();
        return false;
      }

      async function onReceiptClick(houseBlId, invoiceId){
        const inv = Number(invoiceId || 0);

        if (!houseBlId) {
          alert('Missing HBL id');
          return false;
        }

        if (!inv || inv <= 0){
          alert('No invoice found for this HBL. Generate an invoice first.');
          return false;
        }

        try{
          const bal = await getBalanceInfo(houseBlId);
          const balance = Number(bal.balance || 0);

          if (balance > 0){
            await payModalOpen(houseBlId, invoiceId, balance);
            return false;
          }

          const latest = await getLatestPaymentInfo(houseBlId);

          if (Number(latest.found || 0) === 1 && Number(latest.payment_id || 0) > 0){
            openReceiptByPayment(houseBlId, latest.payment_id);
            return false;
          }

          await payModalOpen(houseBlId, invoiceId, balance);
          return false;
        }catch(e){
          alert(e.message || 'Could not determine receipt flow.');
          return false;
        }
      }

      async function onCurrencyChange(){
        toggleFxVisibility();

        if (($('pay_currency')?.value || 'USD') === 'KES') {
          await fetchFxRate();
        }

        autoFillAmountFromBalance();
      }

      async function submitPaymentAndOpenReceipt(){
        setErr('');

        const base = getBasePath();
        const form = $('payForm');
        if (!form) {
          setErr('Payment form not found.');
          return;
        }

        const payload = new FormData(form);
        payload.set('pay_date', normalizeDateForApi($('pay_date') ? $('pay_date').value : ''));

        try{
          const res = await fetch((base ? base : '') + '/api/payments/create', {
            method: 'POST',
            body: payload,
            headers: { 'X-Requested-With': 'XMLHttpRequest' }
          });

          const data = await res.json().catch(() => ({}));

          if (!res.ok || !data.success){
            throw new Error(data.message || ('HTTP ' + res.status));
          }

          payModalHide();
          openReceiptByPayment(data.house_bl_id, data.payment_id);

        }catch(e){
          setErr(e.message || 'Failed to save payment.');
        }
      }

      document.addEventListener('click', function(e){
        if (e.target.closest('[data-close-pay-modal]')){
          e.preventDefault();
          payModalHide();
          return;
        }

        const modal = $('payModal');
        if (modal && !modal.classList.contains('hidden') && e.target === modal){
          payModalHide();
        }
      }, true);

      document.addEventListener('keydown', function(e){
        if (e.key === 'Escape'){
          const modal = $('payModal');
          if (modal && !modal.classList.contains('hidden')) payModalHide();
        }
      });

      document.addEventListener('submit', function(e){
        const f = e.target;
        if (f && f.id === 'payForm'){
          e.preventDefault();
          submitPaymentAndOpenReceipt();
        }
      });

      document.addEventListener('input', function(e){
        if (e.target && e.target.id === 'pay_date'){
          formatDateTyping(e.target);
        }
      });

      document.addEventListener('change', function(e){
        if (e.target && e.target.id === 'pay_currency'){
          onCurrencyChange();
          return;
        }

        if (e.target && e.target.id === 'pay_fx_rate'){
          if (($('pay_currency')?.value || 'USD') === 'KES') {
            autoFillAmountFromBalance();
          }
        }
      });

      window.payModalClose = payModalHide;
      window.handleReceiptAction = function(btn){
        const houseId = btn.getAttribute('data-id') || '';
        const invoiceId = btn.getAttribute('data-invoice-id') || '0';
        return onReceiptClick(houseId, invoiceId);
      };
    }

    // ---------------------------------------
    // GLOBAL HBL DRAWER (NON-LOGIN ONLY)
    // ---------------------------------------
    if (!IS_LOGIN_PAGE && !window.__wscHblDrawerGlobalInit) {
      window.__wscHblDrawerGlobalInit = true;

      function drawerEl(id){ return document.getElementById(id); }

      function hblDrawerShow(){
        const root = drawerEl('hblDrawer');
        if (!root) return;
        root.classList.remove('hidden');
        document.documentElement.style.overflow = 'hidden';
      }

      function hblDrawerHide(){
        const root = drawerEl('hblDrawer');
        if (!root) return;
        root.classList.add('hidden');
        document.documentElement.style.overflow = '';
      }

      async function openHblDrawer(hblId){
        const root = drawerEl('hblDrawer');
        const body = drawerEl('hblDrawerBody');
        const title = drawerEl('hblDrawerTitle');

        if (!root || !body || !title) return;

        title.textContent = 'Loading...';
        body.innerHTML = '<div class="text-sm text-ink-500">Loading...</div>';
        hblDrawerShow();

        try{
          const res = await fetch(BASE + '/hbl/drawer?id=' + encodeURIComponent(hblId), {
            headers: { 'X-Requested-With':'XMLHttpRequest' }
          });

          if (!res.ok) {
            throw new Error('Failed to load HBL details');
          }

            const html = await res.text();
            body.innerHTML = html;
            
            // Bind any newly injected drawer actions
            if (typeof bindNav === 'function') bindNav();
            if (typeof initSelect2 === 'function') initSelect2(body);
            
            const hblNoEl = body.querySelector('[data-hbl-drawer-title]');
            title.textContent = hblNoEl
              ? (hblNoEl.getAttribute('data-hbl-drawer-title') || 'House BL Details')
              : 'House BL Details';
  
        }catch(e){
          body.innerHTML = '<div class="rounded-2xl border border-red-200 bg-red-50 text-red-700 px-4 py-3 text-sm">' + (e.message || 'Failed to load drawer.') + '</div>';
          title.textContent = 'House BL Details';
        }
      }

      document.addEventListener('click', function(e){
        if (e.target.closest('[data-close-hbl-drawer]')){
          e.preventDefault();
          hblDrawerHide();
          return;
        }

        const openBtn = e.target.closest('[data-open-hbl-drawer]');
        if (openBtn){
          e.preventDefault();
          const id = openBtn.getAttribute('data-id') || '';
          if (id) openHblDrawer(id);
          return;
        }

        const row = e.target.closest('[data-hbl-row]');
        if (row){
          if (e.target.closest('a, button, input, select, textarea, label, [data-action], [data-nav]')) return;
          const id = row.getAttribute('data-id') || '';
          if (id) openHblDrawer(id);
          return;
        }
      }, true);

      document.addEventListener('keydown', function(e){
        if (e.key === 'Escape'){
          const root = drawerEl('hblDrawer');
          if (root && !root.classList.contains('hidden')) {
            hblDrawerHide();
          }
        }
      });

      window.openHblDrawer = openHblDrawer;
      window.closeHblDrawer = hblDrawerHide;
    }

    window.copyConsigneeToField = function(btn, targetName){
      const form = btn.closest('form');
      if (!form) {
        alert('Form not found');
        return;
      }
    
      const cons = form.querySelector('select[name="consignee_customer_id"]');
      const target = form.querySelector(`select[name="${targetName}"]`);
    
      if (!cons || !target) {
        alert('Fields not found');
        return;
      }
    
      const consVal = cons.value || '';
      if (!consVal) {
        alert('Select Consignee first');
        return;
      }
    
      const consOpt = cons.options[cons.selectedIndex];
      const consText = (consOpt && consOpt.text) ? consOpt.text.trim() : '';
    
      let opt = Array.from(target.options).find(o => o.value === consVal);
      if (!opt) {
        opt = new Option(consText || ('Customer #' + consVal), consVal, true, true);
        target.add(opt);
      } else {
        opt.selected = true;
      }
    
      if (window.jQuery && jQuery.fn && jQuery.fn.select2) {
        jQuery(target).val(consVal).trigger('change');
      } else {
        target.value = consVal;
      }
    };
    
    window.copyConsigneeToBilling = function(btn){
      window.copyConsigneeToField(btn, 'billing_party_customer_id');
    };
    
    window.copyConsigneeToNotify = function(btn){
      window.copyConsigneeToField(btn, 'notify_party_customer_id');
    };

    // ---------------------------
    // EXISTING APP SHELL / SPA JS
    // ---------------------------

    let currentRecaptchaWidgetId = null;
    
    window.recaptchaLoaded = function(){
      const holder = document.getElementById('recaptchaBox');
      if (!holder || typeof grecaptcha === 'undefined') return;
      if (holder.dataset.rendered === '1') return;

      holder.innerHTML = '';
      currentRecaptchaWidgetId = grecaptcha.render(holder, {
        sitekey: <?= json_encode($siteKey ?? '') ?>
      });
      holder.dataset.rendered = '1';
    };

    function resetRecaptchaIfAny(){
      if (typeof grecaptcha === 'undefined') return;
      if (currentRecaptchaWidgetId !== null) {
        try { grecaptcha.reset(currentRecaptchaWidgetId); } catch(e){}
      }
    }

    function initSelect2(scope){
      if (!window.jQuery || !jQuery.fn || !jQuery.fn.select2) return;
      scope = scope || document;
    
      jQuery(scope).find('.js-customer').each(function(){
        const $el = jQuery(this);
        if ($el.hasClass('select2-hidden-accessible')) return;
    
        $el.select2({
          placeholder: 'Search customer...',
          allowClear: true,
          width: '100%',
          minimumInputLength: 0,
          ajax: {
            url: BASE + '/api/search/customers',
            dataType: 'json',
            delay: 250,
            data: params => ({ q: params.term || '' }),
            processResults: data => ({
              results: (data.results || []).map(x => ({
                id: x.id,
                text: x.text
              }))
            })
          }
        });
      });
    
      jQuery(scope).find('.js-mbl').each(function(){
        const $el = jQuery(this);
        if ($el.hasClass('select2-hidden-accessible')) return;
    
        $el.select2({
          placeholder: 'Search shipment / master BL...',
          allowClear: true,
          width: '100%',
          minimumInputLength: 0,
          ajax: {
            url: BASE + '/api/search/mbls',
            dataType: 'json',
            delay: 250,
            data: params => ({ q: params.term || '' }),
            processResults: data => ({
              results: (data.results || []).map(x => ({
                id: x.id,
                text: x.text
              }))
            })
          }
        });
      });
    
      jQuery(scope).find('.js-shipping-line').each(function(){
        const $el = jQuery(this);
        if ($el.hasClass('select2-hidden-accessible')) return;
    
        $el.select2({
          placeholder: 'Search shipping line...',
          allowClear: true,
          width: '100%',
          minimumInputLength: 0,
          ajax: {
            url: BASE + '/api/search/shipping-lines',
            dataType: 'json',
            delay: 250,
            data: params => ({ q: params.term || '' }),
            processResults: data => ({
              results: (data.results || []).map(x => ({
                id: x.id,
                text: x.text
              }))
            })
          }
        });
      });
    
      jQuery(scope).find('.js-port').each(function(){
        const $el = jQuery(this);
        if ($el.hasClass('select2-hidden-accessible')) return;
    
        $el.select2({
          placeholder: 'Search port...',
          allowClear: true,
          width: '100%',
          minimumInputLength: 0,
          ajax: {
            url: BASE + '/api/search/ports',
            dataType: 'json',
            delay: 250,
            data: params => ({ q: params.term || '' }),
            processResults: data => ({
              results: (data.results || []).map(x => ({
                id: x.id,
                text: x.text
              }))
            })
          }
        });
      });
    }

    function setActiveCommandNav(){
      const current = (location.pathname || '').replace(/\/+$/, '') || '/';
      const navs = document.querySelectorAll('[data-nav-key]');
      navs.forEach(el => el.classList.remove('active'));

      const dashboardPaths = [BASE || '', (BASE || '') + '/', '/'];
      const shipmentsPath = (BASE || '') + '/shipments';
      const customersPath = (BASE || '') + '/customers';
      const shippingLinesPath = (BASE || '') + '/shipping-lines';
      const usersPath = (BASE || '') + '/users';

      if (dashboardPaths.includes(current)) {
        document.querySelector('[data-nav-key="dashboard"]')?.classList.add('active');
        return;
      }

      if (current.startsWith(shipmentsPath)) {
        document.querySelector('[data-nav-key="shipments"]')?.classList.add('active');
        return;
      }

      if (
        current.startsWith(customersPath) ||
        current.startsWith(shippingLinesPath) ||
        current.startsWith(usersPath)
      ) {
        document.querySelector('[data-nav-key="library"]')?.classList.add('active');
      }
    }

    async function loadPartial(url, push=true){
      try{
        const res = await fetch(url, { headers: { 'X-Requested-With':'XMLHttpRequest' } });
        const html = await res.text();
        const app = document.getElementById('app-content');
        if (!app) {
          location.href = url;
          return;
        }
        app.innerHTML = html;
        initSelect2(app);
        bindNav();
        setActiveCommandNav();
        if (push) history.pushState({}, '', url);
      } catch(e){
        alert('Failed to load page.');
      }
    }

    window.loadPartial = loadPartial;

    function bindNav(){
      document.querySelectorAll('[data-nav]').forEach(a => {
        if (a.dataset.bound === '1') return;
        a.dataset.bound = '1';

        a.addEventListener('click', e => {
          const href = a.getAttribute('href') || '';

          if (
            href.includes('/login') ||
            href === (BASE + '/login') ||
            href.includes('/logout') ||
            href === (BASE + '/logout')
          ) {
            return;
          }

          e.preventDefault();

          const libraryMenu = document.getElementById('libraryMenu');
          if (libraryMenu) libraryMenu.classList.add('hidden');

          const mobileCommandMenu = document.getElementById('mobileCommandMenu');
          if (mobileCommandMenu) mobileCommandMenu.classList.add('hidden');

          loadPartial(href, true);
        });
      });

      document.querySelectorAll('[data-action]').forEach(el => {
        if (el.dataset.boundAction === '1') return;
        el.dataset.boundAction = '1';

        el.addEventListener('click', async e => {
          e.preventDefault();

          const action = el.dataset.action;
          const id = el.dataset.id;

          if (action === 'receipt') {
            return window.handleReceiptAction(el);
          }

          if (action === 'shipment-open') {
              loadPartial(BASE + '/shipments/view?id=' + encodeURIComponent(id), true);
              return;
            }

          if (action === 'open-hbl-drawer') {
            if (window.openHblDrawer) window.openHblDrawer(id);
            return;
          }

          if (action === 'arrival') {
            window.open(BASE + '/doc/arrival-notice?house_bl_id=' + id, '_blank');
            return;
          }

          if (action === 'do') {
            window.open(BASE + '/doc/delivery-order?house_bl_id=' + id, '_blank');
            return;
          }

          if (action === 'gen-invoice') {
            window.open(BASE + '/invoice/select-charges?house_bl_id=' + encodeURIComponent(id), '_blank', 'noopener');
            return;
          }

          if (action === 'edit-hbl') {
            loadPartial(BASE + '/hbl/edit?id=' + encodeURIComponent(id), true);
            return;
          }

          if (action === 'hbl-status') {
            const status = el.dataset.status || '';
            const fd = new FormData();
            fd.append('id', id);
            fd.append('status', status);

            const res = await fetch(BASE + '/api/hbl/status', {
              method:'POST',
              body: fd,
              headers:{ 'X-Requested-With':'XMLHttpRequest' }
            });
            const data = await res.json().catch(() => ({}));
            if (!res.ok || !data.ok) {
              alert(data.msg || 'Failed to update status');
              return;
            }
            loadPartial(location.pathname + location.search, false);
          }
        });
      });

      document.querySelectorAll('form[data-ajax]').forEach(form => {
        if (form.dataset.boundSubmit === '1') return;
        form.dataset.boundSubmit = '1';

        form.addEventListener('submit', async e => {
          e.preventDefault();

          if (form.id === 'loginForm') {
            if (typeof grecaptcha === 'undefined') {
              alert('reCAPTCHA not ready. Please wait a moment and try again.');
              return;
            }
            if (currentRecaptchaWidgetId === null) {
              alert('reCAPTCHA not ready. Refresh the page and try again.');
              return;
            }
            const token = grecaptcha.getResponse(currentRecaptchaWidgetId);
            if (!token) {
              alert('Please complete the reCAPTCHA challenge.');
              return;
            }
          }

          const fd = new FormData(form);
          const res = await fetch(form.action, {
            method: (form.method || 'POST').toUpperCase(),
            body: fd,
            headers: { 'X-Requested-With':'XMLHttpRequest' }
          });

          const data = await res.json().catch(() => ({}));

          if (!res.ok || (data.ok === false)) {
            alert(data.msg || data.error || 'Request failed');
            if (form.id === 'loginForm') resetRecaptchaIfAny();
            return;
          }

          if (data.redirect) {
            if (
              data.redirect.includes('/login') ||
              data.redirect === (BASE + '/login') ||
              data.redirect.includes('/logout') ||
              data.redirect === (BASE + '/logout')
            ) {
              location.href = data.redirect;
              return;
            }

            loadPartial(data.redirect, true);
          } else if (form.dataset.refresh === '1') {
            loadPartial(location.pathname + location.search, false);
          } else {
            alert(data.msg || 'Saved');
          }

          if (form.id === 'loginForm') resetRecaptchaIfAny();
        });
      });

      const userMenuBtn = document.getElementById('userMenuBtn');
      const userMenu = document.getElementById('userMenu');
      if (userMenuBtn && userMenu && !userMenuBtn.dataset.boundMenu) {
        userMenuBtn.dataset.boundMenu = '1';

        userMenuBtn.addEventListener('click', (e) => {
          e.stopPropagation();
          userMenu.classList.toggle('hidden');

          const libraryMenu = document.getElementById('libraryMenu');
          if (libraryMenu) libraryMenu.classList.add('hidden');

          const mobileCommandMenu = document.getElementById('mobileCommandMenu');
          if (mobileCommandMenu) mobileCommandMenu.classList.add('hidden');
        });

        document.addEventListener('click', (e) => {
          if (!userMenu.contains(e.target) && !userMenuBtn.contains(e.target)) {
            userMenu.classList.add('hidden');
          }
        });
      }

      const libraryMenuBtn = document.getElementById('libraryMenuBtn');
      const libraryMenu = document.getElementById('libraryMenu');
      if (libraryMenuBtn && libraryMenu && !libraryMenuBtn.dataset.boundMenu) {
        libraryMenuBtn.dataset.boundMenu = '1';

        libraryMenuBtn.addEventListener('click', (e) => {
          e.preventDefault();
          e.stopPropagation();
          libraryMenu.classList.toggle('hidden');

          const userMenu = document.getElementById('userMenu');
          if (userMenu) userMenu.classList.add('hidden');

          const mobileCommandMenu = document.getElementById('mobileCommandMenu');
          if (mobileCommandMenu) mobileCommandMenu.classList.add('hidden');
        });

        document.addEventListener('click', (e) => {
          if (!libraryMenu.contains(e.target) && !libraryMenuBtn.contains(e.target)) {
            libraryMenu.classList.add('hidden');
          }
        });
      }

      const mobileCommandBtn = document.getElementById('mobileCommandBtn');
      const mobileCommandMenu = document.getElementById('mobileCommandMenu');
      if (mobileCommandBtn && mobileCommandMenu && !mobileCommandBtn.dataset.boundMenu) {
        mobileCommandBtn.dataset.boundMenu = '1';

        mobileCommandBtn.addEventListener('click', (e) => {
          e.preventDefault();
          e.stopPropagation();
          mobileCommandMenu.classList.toggle('hidden');

          const libraryMenu = document.getElementById('libraryMenu');
          if (libraryMenu) libraryMenu.classList.add('hidden');

          const userMenu = document.getElementById('userMenu');
          if (userMenu) userMenu.classList.add('hidden');
        });

        document.addEventListener('click', (e) => {
          if (!mobileCommandMenu.contains(e.target) && !mobileCommandBtn.contains(e.target)) {
            mobileCommandMenu.classList.add('hidden');
          }
        });
      }

      if (typeof window.recaptchaLoaded === 'function') {
        window.recaptchaLoaded();
      }
    }

    window.addEventListener('popstate', () => {
      loadPartial(location.pathname + location.search, false);
    });

    initSelect2(document);
    bindNav();
    setActiveCommandNav();
  })();
</script>

</body>
</html>