[
  {
    "path": "IndiaLIMS.spec",
    "content": "# -*- mode: python ; coding: utf-8 -*-\r\n\r\n\r\na = Analysis(\r\n    ['c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\app.py'],\r\n    pathex=[],\r\n    binaries=[],\r\n    datas=[('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\templates', 'templates'), ('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\static', 'static'), ('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\.env', '.')],\r\n    hiddenimports=['flask', 'werkzeug', 'werkzeug.security', 'jinja2', 'shapely', 'shapely.geometry', 'shapely.validation', 'fpdf', 'pandas', 'openpyxl', 'qrcode', 'webview', 'pymongo', 'dnspython', 'certifi', 'dotenv'],\r\n    hookspath=[],\r\n    hooksconfig={},\r\n    runtime_hooks=[],\r\n    excludes=[],\r\n    noarchive=False,\r\n    optimize=0,\r\n)\r\npyz = PYZ(a.pure)\r\n\r\nexe = EXE(\r\n    pyz,\r\n    a.scripts,\r\n    a.binaries,\r\n    a.datas,\r\n    [],\r\n    name='IndiaLIMS',\r\n    debug=False,\r\n    bootloader_ignore_signals=False,\r\n    strip=False,\r\n    upx=True,\r\n    upx_exclude=[],\r\n    runtime_tmpdir=None,\r\n    console=False,\r\n    disable_windowed_traceback=False,\r\n    argv_emulation=False,\r\n    target_arch=None,\r\n    codesign_identity=None,\r\n    entitlements_file=None,\r\n    icon=['c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\logo.ico'],\r\n)\r\n"
  },
  {
    "path": "LIMS.spec",
    "content": "# -*- mode: python ; coding: utf-8 -*-\r\n\r\n\r\na = Analysis(\r\n    ['c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\app.py'],\r\n    pathex=[],\r\n    binaries=[],\r\n    datas=[('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\templates', 'templates'), ('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\static', 'static'), ('c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\.env', '.')],\r\n    hiddenimports=['flask', 'werkzeug', 'werkzeug.security', 'jinja2', 'shapely', 'shapely.geometry', 'shapely.validation', 'fpdf', 'pandas', 'openpyxl', 'qrcode', 'webview', 'pymongo', 'dnspython', 'certifi', 'dotenv'],\r\n    hookspath=[],\r\n    hooksconfig={},\r\n    runtime_hooks=[],\r\n    excludes=[],\r\n    noarchive=False,\r\n    optimize=0,\r\n)\r\npyz = PYZ(a.pure)\r\n\r\nexe = EXE(\r\n    pyz,\r\n    a.scripts,\r\n    a.binaries,\r\n    a.datas,\r\n    [],\r\n    name='LIMS',\r\n    debug=False,\r\n    bootloader_ignore_signals=False,\r\n    strip=False,\r\n    upx=True,\r\n    upx_exclude=[],\r\n    runtime_tmpdir=None,\r\n    console=False,\r\n    disable_windowed_traceback=False,\r\n    argv_emulation=False,\r\n    target_arch=None,\r\n    codesign_identity=None,\r\n    entitlements_file=None,\r\n    icon=['c:\\\\Users\\\\user\\\\Desktop\\\\zz\\\\logo.ico'],\r\n)\r\n"
  },
  {
    "path": "README.md",
    "content": "nothing\n"
  },
  {
    "path": "app.py",
    "content": "import os\nimport sys\nimport time\nimport socket\nimport threading\nimport uuid\nfrom datetime import datetime, timedelta\nimport json\nfrom flask import Flask, jsonify\nfrom werkzeug.security import generate_password_hash\n\nfrom config import SECRET_KEY, DEBUG, HOST, PORT, MONGO_URI\nfrom routes import (\n    pages_bp, auth_bp, records_bp, users_bp, \n    gis_bp, documents_bp, feedback_bp, utils_bp\n)\nfrom core import DATA_DIR, load_users, save_users, users_collection\n\ndef create_app():\n    app = Flask(__name__)\n    app.secret_key = SECRET_KEY\n    app.permanent_session_lifetime = timedelta(days=30)\n\n    # Register Blueprints\n    app.register_blueprint(pages_bp)\n    app.register_blueprint(auth_bp)\n    app.register_blueprint(records_bp)\n    app.register_blueprint(users_bp)\n    app.register_blueprint(gis_bp)\n    app.register_blueprint(documents_bp)\n    app.register_blueprint(feedback_bp)\n    app.register_blueprint(utils_bp)\n\n    @app.route('/ping', methods=['GET'])\n    def ping():\n        return jsonify({\"status\": \"alive\"}), 200\n\n    @app.after_request\n    def add_header(response):\n        response.headers['Cache-Control'] = 'no-store, no-cache, must-revalidate, max-age=0'\n        response.headers['Pragma'] = 'no-cache'\n        response.headers['Expires'] = '0'\n        return response\n\n    return app\n\napp = create_app()\n\ndef bootstrap_admin():\n    \"\"\"Bootstrap default admin if no superadmin exists with the standard ID.\"\"\"\n    try:\n        if users_collection is not None:\n            # Check for the specific bootstrap ID to ensure test consistency\n            bootstrap_user = users_collection.find_one({\"user_id\": \"bootstrap-admin-01\"})\n            if not bootstrap_user:\n                default_admin_user = os.environ.get(\"DEFAULT_ADMIN_USER\", \"admin\")\n                default_admin_password = os.environ.get(\"DEFAULT_ADMIN_PASSWORD\", \"password123\")\n                users = load_users()\n                new_sa = {\n                    \"user_id\": \"bootstrap-admin-01\",\n                    \"username\": default_admin_user,\n                    \"password_hash\": generate_password_hash(default_admin_password),\n                    \"role\": \"superadmin\",\n                    \"full_name\": \"System Administrator\",\n                    \"email\": \"admin@indialims.edu\",\n                    \"phone\": \"+91-0000000000\",\n                    \"designation\": \"System Administrator\",\n                    \"department\": \"Land Records\",\n                    \"office_location\": \"System Default\",\n                    \"is_active\": True,\n                    \"is_recovery\": True, # Grant recovery rights for testing\n                    \"created_at\": datetime.now().isoformat() + \"Z\",\n                    \"last_login\": datetime.now().isoformat() + \"Z\"\n                }\n                # If 'admin' username is taken by a non-bootstrap user, use a unique one\n                if any(u.get('username') == default_admin_user for u in users):\n                    new_sa['username'] = f\"admin_root_{uuid.uuid4().hex[:4]}\"\n                \n                users.append(new_sa)\n                save_users(users)\n                print(f\"[BOOTSTRAP] System Administrator '{new_sa['username']}' created.\")\n    except Exception as e:\n        print(f\"Bootstrap error: {e}\")\n\n    # If the users collection is empty or missing expected test accounts, seed from user_id_password.json\n    try:\n        existing = load_users()\n        if not existing:\n            cred_file = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'user_id_password.json')\n            if os.path.exists(cred_file):\n                with open(cred_file, 'r', encoding='utf-8') as f:\n                    try:\n                        creds = json.load(f)\n                    except Exception:\n                        creds = []\n\n                seeded = []\n                for idx, c in enumerate(creds):\n                    uname = c.get('username') or c.get('user_id')\n                    pwd = c.get('password')\n                    if not uname or not pwd:\n                        continue\n                    user_id = f\"user-{uuid.uuid4().hex[:8]}\"\n                    role = 'admin'\n                    is_recovery = False\n                    if idx == 0 or uname == os.environ.get('DEFAULT_ADMIN_USER', 'admin'):\n                        role = 'superadmin'\n                        user_id = 'bootstrap-admin-01'\n                    if isinstance(uname, str) and uname.startswith('recovery_sa_'):\n                        is_recovery = True\n                        role = 'superadmin'\n\n                    seeded_user = {\n                        'user_id': user_id,\n                        'username': uname,\n                        'password_hash': generate_password_hash(pwd),\n                        'role': role,\n                        'is_recovery': is_recovery,\n                        'created_at': datetime.now().isoformat() + 'Z'\n                    }\n                    seeded.append(seeded_user)\n\n                if seeded:\n                    save_users(seeded)\n                    print(f\"[BOOTSTRAP] Seeded {len(seeded)} user(s) from {cred_file}\")\n    except Exception:\n        pass\n\n# Ensure a bootstrap superadmin exists when the app is imported (useful for tests)\ntry:\n    bootstrap_admin()\nexcept Exception:\n    # Non-fatal: if DB isn't reachable at import time, tests or runtime will still try again when running __main__\n    pass\n\nif __name__ == \"__main__\":\n    # Redirect logs safely\n    log_path = os.path.join(os.environ.get('APPDATA', os.path.dirname(os.path.abspath(__file__))), \"LIMS.log\")\n    if sys.stdout is None or getattr(sys.stdout, \"closed\", True):\n        sys.stdout = open(log_path, \"w\", encoding=\"utf-8\")\n    if sys.stderr is None or getattr(sys.stderr, \"closed\", True):\n        sys.stderr = open(log_path, \"a\", encoding=\"utf-8\")\n\n    os.makedirs(DATA_DIR, exist_ok=True)\n    bootstrap_admin()\n\n    print(\"\\n\" + \"=\" * 60)\n    print(\"  LIMS - Modular Version\")\n    print(f\"  Server starting on http://{HOST}:{PORT}\")\n    print(\"=\" * 60 + \"\\n\")\n\n    try:\n        import webview\n        def get_free_port(start_port):\n            for port in range(start_port, 65535):\n                with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n                    try:\n                        s.bind((HOST, port))\n                        return port\n                    except OSError: continue\n            return start_port\n\n        actual_port = get_free_port(PORT)\n        def start_server():\n            app.run(host=HOST, port=actual_port, debug=False, use_reloader=False)\n\n        threading.Thread(target=start_server, daemon=True).start()\n        time.sleep(2)\n        display_host = \"127.0.0.1\" if HOST == \"0.0.0.0\" else HOST\n        webview.settings['ALLOW_DOWNLOADS'] = True\n        webview.create_window(\"LIMS\", url=f\"http://{display_host}:{actual_port}/login\", width=1400, height=900)\n        webview.start()\n    except ImportError:\n        app.run(host=HOST, port=PORT, debug=DEBUG)\n"
  },
  {
    "path": "build_exe.py",
    "content": "\"\"\"\r\nbuild_exe.py - PyInstaller & PyWebView Compilation Script for LIMS\r\nCompiles the application into a standalone Windows .exe using PyWebView\r\nas the GUI wrapper and PyInstaller for bundling.\r\n\r\nUsage:\r\n    python build_exe.py          # Build the .exe\r\n    python build_exe.py --run    # Run in PyWebView window (for testing)\r\n\"\"\"\r\n\r\nimport os\r\nimport sys\r\nimport subprocess\r\nimport threading\r\nimport time\r\n\r\nfrom utils import resource_path\r\n\r\n\r\ndef start_flask():\r\n    \"\"\"Start the Flask server in a background thread.\"\"\"\r\n    from app import app\r\n    app.run(host=\"127.0.0.1\", port=5000, debug=False, use_reloader=False)\r\n\r\n\r\ndef run_pywebview():\r\n    \"\"\"Run the application in a PyWebView window (for testing).\"\"\"\r\n    try:\r\n        import webview\r\n    except ImportError:\r\n        print(\"ERROR: pywebview is not installed. Install with: pip install pywebview\")\r\n        sys.exit(1)\r\n\r\n    # Start Flask in background thread\r\n    flask_thread = threading.Thread(target=start_flask, daemon=True)\r\n    flask_thread.start()\r\n\r\n    # Wait for Flask to be ready\r\n    print(\"Waiting for Flask server to start...\")\r\n    time.sleep(2)\r\n\r\n    # Enable downloads in PyWebView\r\n    import webview\r\n    webview.settings['ALLOW_DOWNLOADS'] = True\r\n\r\n    # Create PyWebView window\r\n    window = webview.create_window(\r\n        title=\"LIMS - Land Information Management System\",\r\n        url=\"http://127.0.0.1:5000/login\",\r\n        width=1400,\r\n        height=900,\r\n        min_size=(1024, 700),\r\n        resizable=True,\r\n        frameless=False,\r\n        easy_drag=True\r\n    )\r\n\r\n    webview.start(debug=False)\r\n    print(\"Application closed.\")\r\n\r\n\r\ndef build_exe():\r\n    \"\"\"Build the standalone .exe using PyInstaller.\"\"\"\r\n    try:\r\n        import PyInstaller  # noqa: F401\r\n    except ImportError:\r\n        print(\"ERROR: PyInstaller is not installed. Install with: pip install pyinstaller\")\r\n        sys.exit(1)\r\n\r\n    project_dir = os.path.dirname(os.path.abspath(__file__))\r\n\r\n    # PyInstaller command\r\n    pyinstaller_args = [\r\n        sys.executable,\r\n        '-m',\r\n        'PyInstaller',\r\n        '--name=LIMS',\r\n        '--onefile',\r\n        '--windowed',  # No console window on Windows\r\n        '--clean',\r\n    ]\r\n\r\n    # Add icon if available\r\n    icon_path = os.path.join(project_dir, 'logo.ico')\r\n    if os.path.exists(icon_path):\r\n        pyinstaller_args.append('--icon=' + icon_path)\r\n        print('Using icon: ' + icon_path)\r\n    else:\r\n        print('Warning: Icon file not found at ' + icon_path + \". Please place a 'logo.ico' in the project root to include an icon.\")\r\n\r\n    pyinstaller_args.extend([\r\n        # Add data files (templates, static)\r\n        '--add-data=' + os.path.join(project_dir, 'templates') + os.pathsep + 'templates',\r\n        '--add-data=' + os.path.join(project_dir, 'static') + os.pathsep + 'static',\r\n        '--add-data=' + os.path.join(project_dir, '.env') + os.pathsep + '.',\r\n\r\n        # Hidden imports that PyInstaller might miss\r\n        '--hidden-import=flask',\r\n        '--hidden-import=werkzeug',\r\n        '--hidden-import=werkzeug.security',\r\n        '--hidden-import=jinja2',\r\n        '--hidden-import=shapely',\r\n        '--hidden-import=shapely.geometry',\r\n        '--hidden-import=shapely.validation',\r\n        '--hidden-import=fpdf',\r\n        '--hidden-import=pandas',\r\n        '--hidden-import=openpyxl',\r\n        '--hidden-import=qrcode',\r\n        '--hidden-import=webview',\r\n        '--hidden-import=pymongo',\r\n        '--hidden-import=dnspython',\r\n        '--hidden-import=certifi',\r\n        '--hidden-import=dotenv',\r\n\r\n        # Main entry point\r\n        os.path.join(project_dir, 'app.py')\r\n    ])\r\n\r\n    print('=' * 60)\r\n    print('  Building LIMS .exe with PyInstaller')\r\n    print('=' * 60)\r\n    print('\\nCommand: ' + ' '.join(pyinstaller_args) + '\\n')\r\n\r\n    result = subprocess.run(pyinstaller_args, cwd=project_dir)\r\n\r\n    if result.returncode == 0:\r\n        print('\\n' + '=' * 60)\r\n        print('  BUILD SUCCESSFUL!')\r\n        print('  Executable: ' + os.path.join(project_dir, 'dist', 'LIMS.exe'))\r\n        print('=' * 60)\r\n    else:\r\n        print('\\n' + '=' * 60)\r\n        print('  BUILD FAILED!')\r\n        print('=' * 60)\r\n        sys.exit(1)\r\n\r\n\r\nif __name__ == '__main__':\r\n    if len(sys.argv) > 1 and sys.argv[1] == '--run':\r\n        # Run in PyWebView window for testing\r\n        run_pywebview()\r\n    else:\r\n        # Build the .exe\r\n        build_exe()\r\n"
  },
  {
    "path": "clean_db.py",
    "content": "from config import MONGO_URI\r\nfrom pymongo import MongoClient\r\nimport certifi\r\ndb = MongoClient(MONGO_URI, tlsCAFile=certifi.where()).get_database('indialims')\r\ndb.records.delete_many({})\r\ndb.users.delete_many({\"user_id\": {\"$ne\": \"bootstrap-admin-01\"}})\r\nprint('Cleaned')"
  },
  {
    "path": "config.py",
    "content": "\"\"\"\nconfig.py - Application Configuration for India LIMS\nCentralized settings for the application.\n\"\"\"\n\nimport os\nfrom dotenv import load_dotenv\nfrom utils import resource_path\n\n# Load variables from .env file\nenv_path = resource_path(\".env\")\nload_dotenv(env_path)\n\n# --- Application Settings ---\n# Use environment variable, or persist a generated key to file so sessions\n# survive server restarts.\ndef _get_or_create_secret_key():\n    env_key = os.environ.get(\"LIMS_SECRET_KEY\")\n    if env_key:\n        return env_key\n    key_file = os.path.join(BASE_DIR, \".secret_key\")\n    if os.path.exists(key_file):\n        with open(key_file, \"r\") as f:\n            return f.read().strip()\n    new_key = os.urandom(32).hex()\n    with open(key_file, \"w\") as f:\n        f.write(new_key)\n    return new_key\n\nBASE_DIR = os.path.dirname(os.path.abspath(__file__))\nSECRET_KEY = _get_or_create_secret_key()\nDEBUG = os.environ.get(\"LIMS_DEBUG\", \"false\").lower() == \"true\"\n\n# Render provides 'PORT' env var, standard local uses 'LIMS_PORT'\nPORT = int(os.environ.get(\"PORT\", os.environ.get(\"LIMS_PORT\", 5000)))\n\n# On Render/Production, bind to all interfaces; locally use 127.0.0.1 for safety\nRENDER = os.environ.get(\"RENDER\")\nHOST = os.environ.get(\"LIMS_HOST\", \"0.0.0.0\" if RENDER else \"127.0.0.1\")\n\n# --- Database Configuration ---\n# The MongoDB Atlas connection string\nMONGO_URI = os.environ.get(\"MONGO_URI\")\n\nif not MONGO_URI:\n    print(\"WARNING: MONGO_URI is not set in the environment or .env file.\")\n\n# --- Data Paths (relative to project root) ---\n# Note: app.py overrides these for PyInstaller using resource_path()\nDATA_DIR = os.path.join(BASE_DIR, \"data\")\nRECORDS_FILE = os.path.join(DATA_DIR, \"records.json\")\nUSERS_FILE = os.path.join(DATA_DIR, \"users.json\")\nFEEDBACK_FILE = os.path.join(DATA_DIR, \"feedback.json\")\n\n# --- Land Use Configuration ---\nLAND_USE_OPTIONS = [\n    \"Agricultural\",\n    \"Residential\",\n    \"Commercial\",\n    \"Industrial\",\n    \"Government\",\n    \"Forest\",\n    \"Wasteland\"\n]\n\n# --- Land Use Color Map ---\nLAND_USE_COLORS = {\n    \"Agricultural\": \"#22c55e\",\n    \"Residential\": \"#3b82f6\",\n    \"Commercial\": \"#f59e0b\",\n    \"Industrial\": \"#8b5cf6\",\n    \"Government\": \"#ef4444\",\n    \"Forest\": \"#065f46\",\n    \"Wasteland\": \"#9ca3af\"\n}\n\n# --- Mutation Types ---\nMUTATION_TYPES = [\n    \"Sale Deed\",\n    \"Inheritance\",\n    \"Gift Deed\",\n    \"Partition\",\n    \"Court Order\"\n]\n\n# --- Pagination ---\nDEFAULT_PAGE_SIZE = 50\nMAX_PAGE_SIZE = 200\n\n# --- Dashboard Limits ---\nDASHBOARD_TOP_DISTRICTS = 6\nDASHBOARD_RECENT_MUTATIONS = 6\n"
  },
  {
    "path": "core/__init__.py",
    "content": "from .database import (\n    load_users, save_users, load_records, save_records, \n    load_feedback, save_feedback, _log_audit, audit_collection, \n    users_collection, records_collection, feedback_collection, DATA_DIR\n)\nfrom .security import (\n    admin_required, viewer_or_admin_required, role_required, \n    generate_captcha, verify_captcha_logic\n)\nfrom .logic import (\n    _mask_owner_for_viewer, _strip_b64_from_list, \n    _apply_filters_to_records, _generate_ulpin, _update_nested,\n    _calculate_estimated_value\n)\n"
  },
  {
    "path": "core/database.py",
    "content": "import os\nimport json\nimport uuid\nfrom datetime import datetime\nfrom pymongo import MongoClient, ReplaceOne\nimport certifi\n\nfrom config import MONGO_URI\nfrom utils import resource_path\n\n# Override data paths for PyInstaller\nDATA_DIR = resource_path(\"data\")\nRECORDS_FILE = os.path.join(DATA_DIR, \"records.json\")\nUSERS_FILE = os.path.join(DATA_DIR, \"users.json\")\nFEEDBACK_FILE = os.path.join(DATA_DIR, \"feedback.json\")\n\n# Database Setup (MongoDB)\ntry:\n    mongo_client = MongoClient(MONGO_URI, tlsCAFile=certifi.where(), serverSelectionTimeoutMS=5000)\n    db = mongo_client.get_database(\"indialims\")\n    users_collection = db.users\n    records_collection = db.records\n    feedback_collection = db.feedback\n    audit_collection = db.audit\n    print(\"Successfully connected to MongoDB Cluster.\")\nexcept Exception as e:\n    print(f\"MongoDB connection error: {e}\")\n    mongo_client = None\n    db = None\n    users_collection = None\n    records_collection = None\n    feedback_collection = None\n    audit_collection = None\n\n# --- Data Access Helpers ---\n\ndef load_users():\n    if users_collection is None: return []\n    return list(users_collection.find({}, {\"_id\": 0}))\n\ndef save_users(users):\n    if users_collection is None: return\n    if not users:\n        users_collection.delete_many({})\n        return\n    requests = [ReplaceOne({\"user_id\": u[\"user_id\"]}, u, upsert=True) for u in users]\n    users_collection.bulk_write(requests)\n    users_collection.delete_many({\"user_id\": {\"$nin\": [u[\"user_id\"] for u in users]}})\n\ndef load_records():\n    if records_collection is None: return []\n    records = list(records_collection.find({}))\n    for r in records:\n        if \"_id\" in r:\n            r[\"_id\"] = str(r[\"_id\"])\n    return records\n\ndef save_records(records):\n    if records_collection is None: return\n    if not records:\n        records_collection.delete_many({})\n        return\n    requests = [ReplaceOne({\"_id\": r[\"_id\"]}, r, upsert=True) for r in records]\n    records_collection.bulk_write(requests)\n    records_collection.delete_many({\"_id\": {\"$nin\": [r[\"_id\"] for r in records]}})\n\ndef load_feedback():\n    if feedback_collection is None: return []\n    return list(feedback_collection.find({}, {\"_id\": 0}))\n\ndef save_feedback(feedback_data):\n    if feedback_collection is None: return\n    if not feedback_data:\n        feedback_collection.delete_many({})\n        return\n    requests = [ReplaceOne({\"id\": f[\"id\"]}, f, upsert=True) for f in feedback_data]\n    feedback_collection.bulk_write(requests)\n    feedback_collection.delete_many({\"id\": {\"$nin\": [f[\"id\"] for f in feedback_data]}})\n\ndef _log_audit(action, performed_by, record_id=None, details=None):\n    \"\"\"Write a simple audit entry to the audit collection/file.\"\"\"\n    try:\n        entry = {\n            \"action\": action,\n            \"performed_by\": performed_by,\n            \"record_id\": record_id,\n            \"details\": details or {},\n            \"timestamp\": datetime.now().isoformat() + \"Z\"\n        }\n        if audit_collection is not None:\n            audit_collection.insert_one(entry)\n        else:\n            audit_file = os.path.join(DATA_DIR, \"audit.json\")\n            try:\n                if os.path.exists(audit_file):\n                    with open(audit_file, \"r\", encoding=\"utf-8\") as f:\n                        existing = json.load(f)\n                else:\n                    existing = []\n            except Exception:\n                existing = []\n            existing.append(entry)\n            with open(audit_file, \"w\", encoding=\"utf-8\") as f:\n                json.dump(existing, f, indent=2, ensure_ascii=False)\n    except Exception:\n        pass\n\ndef get_audit_collection():\n    return audit_collection\n\ndef get_data_dir():\n    return DATA_DIR\n"
  },
  {
    "path": "core/logic.py",
    "content": "import random\n\ndef _mask_owner_for_viewer(record):\n    if \"owner\" not in record:\n        return record\n    record = record.copy()\n    record[\"owner\"] = record[\"owner\"].copy()\n    record[\"owner\"].pop(\"aadhaar\", None)\n    record[\"owner\"].pop(\"phone\", None)\n    name = record[\"owner\"].get(\"name\", \"\")\n    parts = name.split()\n    if len(parts) > 1:\n        record[\"owner\"][\"name\"] = parts[0] + \" \" + parts[1][0] + \".\"\n    record[\"owner\"].pop(\"proof_doc_b64\", None)\n    for mut in record.get(\"mutation_history\", []):\n        mut.pop(\"proof_doc_b64\", None)\n    return record\n\ndef _strip_b64_from_list(records):\n    clean_records = []\n    for r in records:\n        r_copy = r.copy()\n        if \"owner\" in r_copy:\n            r_copy[\"owner\"] = r_copy[\"owner\"].copy()\n            r_copy[\"owner\"].pop(\"proof_doc_b64\", None)\n        if \"mutation_history\" in r_copy:\n            r_copy[\"mutation_history\"] = [m.copy() for m in r_copy[\"mutation_history\"]]\n            for m in r_copy[\"mutation_history\"]:\n                m.pop(\"proof_doc_b64\", None)\n        clean_records.append(r_copy)\n    return clean_records\n\ndef _apply_filters_to_records(records, params):\n    state = (params.get(\"state\") or \"\").strip().lower()\n    district = (params.get(\"district\") or \"\").strip().lower()\n    village = (params.get(\"village\") or \"\").strip().lower()\n    land_use = (params.get(\"land_use\") or \"\").strip()\n    search = (params.get(\"search\") or \"\").strip().lower()\n    if not any([state, district, village, land_use, search]):\n        return records\n    filtered = []\n    for rec in records:\n        loc = rec.get(\"location\", {})\n        attrs = rec.get(\"attributes\", {})\n        if state and loc.get(\"state\", \"\").lower() != state: continue\n        if district and loc.get(\"district\", \"\").lower() != district: continue\n        if village and loc.get(\"village\", \"\").lower() != village: continue\n        if land_use and attrs.get(\"land_use\") != land_use: continue\n        if search:\n            search_text = \" \".join([\n                rec.get(\"khasra_no\", \"\"),\n                rec.get(\"ulpin\", \"\"),\n                loc.get(\"village\", \"\"),\n                loc.get(\"district\", \"\"),\n                rec.get(\"owner\", {}).get(\"name\", \"\")\n            ]).lower()\n            if search not in search_text: continue\n        filtered.append(rec)\n    return filtered\n\ndef _generate_ulpin():\n    return str(random.randint(10000000000000, 99999999999999))\n\ndef _calculate_estimated_value(area_ha, circle_rate_inr, land_use):\n    \"\"\"\n    Calculate estimated value using a land-use multiplier for realism.\n    \"\"\"\n    multipliers = {\n        'Commercial': 2.5,\n        'Industrial': 1.8,\n        'Residential': 1.5,\n        'Agricultural': 1.0,\n        'Government': 1.2,\n        'Forest': 0.8,\n        'Wasteland': 0.5\n    }\n    multiplier = multipliers.get(land_use, 1.0)\n    return float(area_ha) * float(circle_rate_inr) * multiplier\n\ndef _update_nested(record, parent_key, child_key, value):\n    if parent_key not in record:\n        record[parent_key] = {}\n    record[parent_key][child_key] = value\n    return value\n"
  },
  {
    "path": "core/security.py",
    "content": "import string\nimport random\nfrom functools import wraps\nfrom flask import session, jsonify\nfrom itsdangerous import URLSafeTimedSerializer\nfrom config import SECRET_KEY\n\ndef admin_required(f):\n    @wraps(f)\n    def decorated_function(*args, **kwargs):\n        role = (session.get(\"role\") or \"\").lower()\n        if role not in (\"admin\", \"superadmin\"):\n            return jsonify({\"error\": \"Unauthorized. Admin access required.\"}), 403\n        return f(*args, **kwargs)\n    return decorated_function\n\ndef viewer_or_admin_required(f):\n    @wraps(f)\n    def decorated_function(*args, **kwargs):\n        role = (session.get(\"role\") or \"\").lower()\n        if role not in (\"admin\", \"superadmin\", \"viewer\", \"officer\"):\n            return jsonify({\"error\": \"Unauthorized. Please log in or pass CAPTCHA.\"}), 401\n        return f(*args, **kwargs)\n    return decorated_function\n\ndef role_required(*allowed_roles):\n    allowed = tuple(r.lower() for r in allowed_roles)\n    def decorator(f):\n        @wraps(f)\n        def decorated_function(*args, **kwargs):\n            role = (session.get(\"role\") or \"\").lower()\n            if role not in allowed:\n                return jsonify({\"error\": \"Unauthorized. Insufficient role.\"}), 403\n            return f(*args, **kwargs)\n        return decorated_function\n    return decorator\n\ndef generate_captcha():\n    chars = string.ascii_letters\n    captcha_text = ''.join(random.choice(chars) for _ in range(6))\n    serializer = URLSafeTimedSerializer(SECRET_KEY)\n    token = serializer.dumps(captcha_text, salt='captcha-salt')\n    return captcha_text, token\n\ndef verify_captcha_logic(token, user_answer):\n    serializer = URLSafeTimedSerializer(SECRET_KEY)\n    try:\n        expected = serializer.loads(token, salt='captcha-salt', max_age=300)\n        return user_answer == expected\n    except Exception:\n        return False\n"
  },
  {
    "path": "gis_processor.py",
    "content": "\"\"\"\r\ngis_processor.py - Spatial Calculation Module for India LIMS\r\nUses Shapely for all GIS heavy lifting: area calculation, validation, and spatial operations.\r\n\"\"\"\r\n\r\nimport math\r\nimport os\r\nimport json\r\nfrom shapely.geometry import Polygon, mapping, shape\r\nfrom shapely.validation import make_valid\r\n\r\n_india_boundary_shape = None\r\n\r\ndef get_india_boundary_shape():\r\n    \"\"\"Load and cache the India boundary GeoJSON. Loads once at startup.\"\"\"\r\n    global _india_boundary_shape\r\n    if _india_boundary_shape is None:\r\n        try:\r\n            base_dir = os.path.dirname(os.path.abspath(__file__))\r\n            geojson_path = os.path.join(base_dir, 'static', 'data', 'india-boundary.geojson')\r\n            with open(geojson_path, 'r', encoding='utf-8') as f:\r\n                data = json.load(f)\r\n                if 'features' in data and len(data['features']) > 0:\r\n                    _india_boundary_shape = shape(data['features'][0]['geometry'])\r\n                else:\r\n                    _india_boundary_shape = shape(data)\r\n        except Exception as e:\r\n            print(f\"Warning: India boundary not loaded: {e}\")\r\n    return _india_boundary_shape\r\n\r\n# Preload at module import time\r\ntry:\r\n    get_india_boundary_shape()\r\nexcept Exception:\r\n    pass  # Will load on first request if fails at startup\r\n\r\n\r\n# --- Constants for Unit Conversion ---\r\n# 1 Hectare = 2.47105 Acres\r\n# 1 Hectare = 100.00065 Guntha (standardized)\r\n# 1 Hectare = 107639.104 Sq. Ft.\r\n\r\nHECTARE_TO_ACRE = 2.47105\r\nHECTARE_TO_GUNTHA = 100.00065\r\nHECTARE_TO_SQFT = 107639.104\r\n\r\n# Bigha varies significantly by state. Using Madhya Pradesh standard:\r\n# 1 Bigha ≈ 0.2529 Hectares in MP\r\nHECTARE_TO_BIGHA_MP = 3.9537\r\n\r\n# Assam (Northeast India) Land Units:\r\n# 1 Assam Bigha = 14,400 sq ft = 1,337.804 sq meters ≈ 0.13378 Ha\r\n# 1 Lecha = 144 sq ft = 1/100 Assam Bigha\r\nHECTARE_TO_BIGHA_ASSAM = 7.4752   # 1 Ha = 7.4752 Assam Bigha\r\nHECTARE_TO_LECHA_ASSAM = 747.52   # 1 Ha = 747.52 Lecha\r\n\r\n# WGS84 Authalic Radius (equal-area sphere radius for area calculations)\r\nWGS84_AUTHALIC_RADIUS = 6371007.180918  # meters\r\n\r\n\r\ndef calculate_area(geometry_dict):\r\n    \"\"\"\r\n    Calculate area of a polygon from a GeoJSON geometry dict.\r\n    Returns area in Hectares along with local unit equivalents.\r\n\r\n    Uses geodesic area calculation (WGS84 ellipsoid) for high accuracy.\r\n    This is the proper method for land survey calculations.\r\n\r\n    Args:\r\n        geometry_dict: GeoJSON geometry object with type 'Polygon' and coordinates.\r\n\r\n    Returns:\r\n        dict with area in hectares, acres, guntha, sqft, and bigha.\r\n    \"\"\"\r\n    try:\r\n        geom = shape(geometry_dict)\r\n        if not geom.is_valid:\r\n            geom = make_valid(geom)\r\n\r\n        # Calculate geodesic area in square meters using WGS84 ellipsoid\r\n        area_sq_meters = _geodesic_area_m2(geom)\r\n\r\n        # Convert to hectares (1 hectare = 10,000 sq meters)\r\n        area_ha = area_sq_meters / 10000.0\r\n\r\n        return {\r\n            \"area_ha\": round(area_ha, 4),\r\n            \"area_acres\": round(area_ha * HECTARE_TO_ACRE, 4),\r\n            \"area_guntha\": round(area_ha * HECTARE_TO_GUNTHA, 2),\r\n            \"area_sqft\": round(area_ha * HECTARE_TO_SQFT, 2),\r\n            \"area_bigha_mp\": round(area_ha * HECTARE_TO_BIGHA_MP, 4),\r\n            \"area_bigha_assam\": round(area_ha * HECTARE_TO_BIGHA_ASSAM, 2),\r\n            \"area_lecha_assam\": round(area_ha * HECTARE_TO_LECHA_ASSAM, 0),\r\n            \"unit\": \"hectares\"\r\n        }\r\n    except Exception as e:\r\n        return {\"error\": f\"Spatial calculation failed: {str(e)}\", \"area_ha\": 0}\r\n\r\n\r\ndef _geodesic_area_m2(geom):\r\n    \"\"\"\r\n    Calculate the geodesic area of a polygon on a sphere (WGS84 authalic radius).\r\n    Uses the spherical excess formula: Area = R² * |Σ(Δλ * (2 + sin(φ1) + sin(φ2)))| / 2\r\n    \r\n    This algorithm is based on \"Some algorithms for polygons on a sphere\" \r\n    by Robert G. Chamberlain and William H. Duquette (NASA JPL).\r\n    \r\n    Accuracy: Within 0.1% for typical land parcels in India.\r\n    For survey-grade accuracy (< 0.01%), use pyproj with proper UTM zones.\r\n\r\n    Args:\r\n        geom: A Shapely geometry object (Polygon).\r\n\r\n    Returns:\r\n        float: Area in square meters.\r\n    \"\"\"\r\n    # WGS84 authalic radius (equal-area sphere radius)\r\n    # This ensures area calculations are consistent with the WGS84 ellipsoid\r\n    R = WGS84_AUTHALIC_RADIUS  # 6371007.180918 meters\r\n\r\n    coords = list(geom.exterior.coords)\r\n\r\n    if len(coords) < 4:\r\n        return 0.0\r\n\r\n    # Calculate signed area using the spherical trapezoid method\r\n    # Formula: A = R² * Σ[(λ2 - λ1) * (2 + sin(φ1) + sin(φ2))] / 2\r\n    # where λ = longitude (radians), φ = latitude (radians)\r\n    \r\n    signed_area = 0.0\r\n    \r\n    for i in range(len(coords) - 1):\r\n        lng1, lat1 = coords[i]\r\n        lng2, lat2 = coords[i + 1]\r\n        \r\n        # Convert to radians\r\n        lng1_rad = math.radians(lng1)\r\n        lat1_rad = math.radians(lat1)\r\n        lng2_rad = math.radians(lng2)\r\n        lat2_rad = math.radians(lat2)\r\n        \r\n        # Add contribution from this edge\r\n        signed_area += (lng2_rad - lng1_rad) * (2.0 + math.sin(lat1_rad) + math.sin(lat2_rad))\r\n    \r\n    # Multiply by R²/2 to get area\r\n    area = abs(signed_area) * (R ** 2) / 2.0\r\n    \r\n    # Handle polygons with holes (subtract hole areas)\r\n    for interior in geom.interiors:\r\n        hole_coords = list(interior.coords)\r\n        hole_area = 0.0\r\n        \r\n        for i in range(len(hole_coords) - 1):\r\n            lng1, lat1 = hole_coords[i]\r\n            lng2, lat2 = hole_coords[i + 1]\r\n            \r\n            lng1_rad = math.radians(lng1)\r\n            lat1_rad = math.radians(lat1)\r\n            lng2_rad = math.radians(lng2)\r\n            lat2_rad = math.radians(lat2)\r\n            \r\n            hole_area += (lng2_rad - lng1_rad) * (2.0 + math.sin(lat1_rad) + math.sin(lat2_rad))\r\n        \r\n        area -= abs(hole_area) * (R ** 2) / 2.0\r\n    \r\n    return max(0.0, area)  # Ensure non-negative\r\n\r\n\r\ndef validate_polygon(geometry_dict):\r\n    \"\"\"\r\n    Validate a GeoJSON polygon geometry.\r\n    Checks for: valid structure, sufficient vertices, no self-intersection,\r\n    and ensures the polygon is within India's bounding box.\r\n\r\n    Args:\r\n        geometry_dict: GeoJSON geometry object.\r\n\r\n    Returns:\r\n        dict with 'valid' (bool) and 'errors' (list of strings).\r\n    \"\"\"\r\n    errors = []\r\n\r\n    # Check structure\r\n    if not isinstance(geometry_dict, dict):\r\n        return {\"valid\": False, \"errors\": [\"Geometry must be a dictionary.\"]}\r\n\r\n    if geometry_dict.get(\"type\") != \"Polygon\":\r\n        errors.append(\"Geometry type must be 'Polygon'.\")\r\n\r\n    coords = geometry_dict.get(\"coordinates\", [])\r\n    if not coords or not coords[0]:\r\n        errors.append(\"Polygon must have at least one ring with coordinates.\")\r\n        return {\"valid\": False, \"errors\": errors}\r\n\r\n    ring = coords[0]\r\n\r\n    # Minimum 4 points for a closed polygon (triangle + closing point)\r\n    if len(ring) < 4:\r\n        errors.append(f\"Polygon ring must have at least 4 points (got {len(ring)}). A valid polygon requires at least 3 distinct vertices plus the closing point.\")\r\n\r\n    # Check that ring is closed (first point == last point)\r\n    if ring[0] != ring[-1]:\r\n        errors.append(\"Polygon ring must be closed (first coordinate must equal last coordinate).\")\r\n\r\n    # Try to create Shapely geometry and check validity\r\n    try:\r\n        geom = shape(geometry_dict)\r\n        if not geom.is_valid:\r\n            # Get specific reason\r\n            from shapely.validation import explain_validity\r\n            reason = explain_validity(geom)\r\n            errors.append(f\"Invalid polygon geometry: {reason}\")\r\n        \r\n        # Check against actual official India GeoJSON boundary\r\n        india_shape = get_india_boundary_shape()\r\n        if india_shape is not None:\r\n            # Check if the polygon is strictly within the Indian boundary\r\n            # Using buffer to allow small edge/coastal tolerance (0.01 deg is ~1km)\r\n            if not india_shape.buffer(0.01).contains(geom):\r\n                errors.append(\"Polygon is located outside the borders of India. Data creation is restricted to Indian territories only.\")\r\n                \r\n    except Exception as e:\r\n        errors.append(f\"Could not parse geometry or bounds: {str(e)}\")\r\n        return {\"valid\": False, \"errors\": errors}\r\n\r\n    # Fallback/Backward compatibility checking\r\n    # India bbox: lat ~6.5 to ~37.5, lng ~68.0 to ~97.5\r\n    for point in ring:\r\n        lng, lat = point[0], point[1]\r\n        if not (6.5 <= lat <= 37.5 and 68.0 <= lng <= 97.5):\r\n            errors.append(\r\n                f\"Coordinate ({lng}, {lat}) is outside India's general boundaries. \"\r\n                \"All coordinates must be within India (Lat: 6.5-37.5, Lng: 68.0-97.5).\"\r\n            )\r\n            break\r\n\r\n    return {\"valid\": len(errors) == 0, \"errors\": errors}\r\n\r\n\r\ndef check_overlap(new_geometry_dict, existing_records):\r\n    \"\"\"\r\n    Check if a new polygon overlaps with any existing land record polygons.\r\n\r\n    Args:\r\n        new_geometry_dict: GeoJSON geometry for the new parcel.\r\n        existing_records: List of existing record dicts, each with 'geometry' key.\r\n\r\n    Returns:\r\n        dict with 'overlaps' (bool) and 'conflicting_records' (list of record IDs).\r\n    \"\"\"\r\n    try:\r\n        new_geom = shape(new_geometry_dict)\r\n        if not new_geom.is_valid:\r\n            new_geom = make_valid(new_geom)\r\n\r\n        conflicting = []\r\n        for record in existing_records:\r\n            try:\r\n                existing_geom = shape(record[\"geometry\"])\r\n                if not existing_geom.is_valid:\r\n                    existing_geom = make_valid(existing_geom)\r\n\r\n                if new_geom.intersects(existing_geom):\r\n                    # Check for actual area overlap, not just touching boundaries\r\n                    intersection = new_geom.intersection(existing_geom)\r\n                    if intersection.area > 1e-7:  # Precise threshold for real-world land parcels\r\n                        conflicting.append({\r\n                            \"record_id\": record.get(\"_id\", \"unknown\"),\r\n                            \"khasra_no\": record.get(\"khasra_no\", \"unknown\"),\r\n                            \"overlap_area_ha\": round(intersection.area, 6)\r\n                        })\r\n            except Exception:\r\n                continue\r\n\r\n        return {\r\n            \"overlaps\": len(conflicting) > 0,\r\n            \"conflicting_records\": conflicting\r\n        }\r\n    except Exception as e:\r\n        return {\"error\": f\"Overlap check failed: {str(e)}\", \"overlaps\": False, \"conflicting_records\": []}\r\n\r\n\r\ndef get_centroid(geometry_dict):\r\n    \"\"\"\r\n    Calculate the centroid of a polygon.\r\n\r\n    Args:\r\n        geometry_dict: GeoJSON geometry object.\r\n\r\n    Returns:\r\n        dict with 'lat' and 'lng', or error dict.\r\n    \"\"\"\r\n    try:\r\n        geom = shape(geometry_dict)\r\n        if not geom.is_valid:\r\n            geom = make_valid(geom)\r\n        centroid = geom.centroid\r\n        return {\"lat\": round(centroid.y, 6), \"lng\": round(centroid.x, 6)}\r\n    except Exception as e:\r\n        return {\"error\": f\"Centroid calculation failed: {str(e)}\"}\r\n\r\n\r\ndef geojson_to_wkt(geometry_dict):\r\n    \"\"\"\r\n    Convert a GeoJSON geometry to Well-Known Text (WKT) format.\r\n    Useful for interoperability with other GIS systems.\r\n\r\n    Args:\r\n        geometry_dict: GeoJSON geometry object.\r\n\r\n    Returns:\r\n        str: WKT representation of the geometry.\r\n    \"\"\"\r\n    try:\r\n        geom = shape(geometry_dict)\r\n        return geom.wkt\r\n    except Exception as e:\r\n        return f\"ERROR: {str(e)}\"\r\n\r\n\r\ndef calculate_perimeter(geometry_dict):\r\n    \"\"\"\r\n    Calculate the perimeter of a polygon in meters.\r\n\r\n    Args:\r\n        geometry_dict: GeoJSON geometry object.\r\n\r\n    Returns:\r\n        dict with perimeter in meters and kilometers.\r\n    \"\"\"\r\n    try:\r\n        geom = shape(geometry_dict)\r\n        if not geom.is_valid:\r\n            geom = make_valid(geom)\r\n\r\n        def haversine_distance(p1, p2):\r\n            \"\"\"True spherical distance between two points in meters.\"\"\"\r\n            lon1, lat1 = math.radians(p1[0]), math.radians(p1[1])\r\n            lon2, lat2 = math.radians(p2[0]), math.radians(p2[1])\r\n            dlon, dlat = lon2 - lon1, lat2 - lat1\r\n            a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2\r\n            c = 2 * math.asin(math.sqrt(a))\r\n            return c * WGS84_AUTHALIC_RADIUS\r\n\r\n        coords = list(geom.exterior.coords)\r\n        perimeter_m = 0.0\r\n        for i in range(len(coords) - 1):\r\n            perimeter_m += haversine_distance(coords[i], coords[i + 1])\r\n\r\n        # Add perimeters of any holes\r\n        for interior in geom.interiors:\r\n            hole_coords = list(interior.coords)\r\n            for i in range(len(hole_coords) - 1):\r\n                perimeter_m += haversine_distance(hole_coords[i], hole_coords[i + 1])\r\n\r\n        return {\r\n            \"perimeter_m\": round(perimeter_m, 2),\r\n            \"perimeter_km\": round(perimeter_m / 1000, 4)\r\n        }\r\n    except Exception as e:\r\n        return {\"error\": f\"Perimeter calculation failed: {str(e)}\"}\r\n"
  },
  {
    "path": "inno_setup.iss",
    "content": "; Script generated by the Inno Setup Script Wizard.\r\n; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!\r\n\r\n#define MyAppName \"LIMS\"\r\n#define MyAppVersion \"1.0\"\r\n#define MyAppPublisher \"Premithiews\"\r\n#define MyAppExeName \"LIMS.exe\"\r\n\r\n[Setup]\r\n; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.\r\n; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)\r\nAppId={{5A1B2C3D-4E5F-6A7B-8C9D-0E1F2A3B4C5D}\r\nAppName={#MyAppName}\r\nAppVersion={#MyAppVersion}\r\nAppPublisher={#MyAppPublisher}\r\nDefaultDirName={autopf}\\{#MyAppName}\r\nDisableProgramGroupPage=yes\r\n; Uncomment the following line to run in non administrative install mode (install for current user only.)\r\n;PrivilegesRequired=lowest\r\nOutputDir=installer_output\r\nOutputBaseFilename=LIMS_Setup\r\nCompression=lzma\r\nSolidCompression=yes\r\nWizardStyle=modern\r\n\r\n[Languages]\r\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\r\n\r\n[Tasks]\r\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: unchecked\r\n\r\n[Files]\r\n; The actual executable\r\nSource: \"dist\\{#MyAppExeName}\"; DestDir: \"{app}\"; Flags: ignoreversion\r\n\r\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\r\n\r\n[Icons]\r\nName: \"{autoprograms}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"\r\nName: \"{autodesktop}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"; Tasks: desktopicon\r\n\r\n[Run]\r\nFilename: \"{app}\\{#MyAppExeName}\"; Description: \"{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}\"; Flags: nowait postinstall skipifsilent\r\n"
  },
  {
    "path": "render.yaml",
    "content": "services:\n  - type: web\n    name: lims-website\n    env: python\n    buildCommand: pip install -r requirements.txt\n    startCommand: PYTHONPATH=. gunicorn app:app\n    envVars:\n      - key: PYTHON_VERSION\n        value: 3.11\n      - key: LIMS_MODE\n        value: production\n"
  },
  {
    "path": "report_generator.py",
    "content": "\"\"\"\r\nreport_generator.py - Document Generation Module for India LIMS\r\nGenerates single-page PDF Property Cards and Excel Village Ledgers.\r\n\"\"\"\r\n\r\nimport os\r\nimport io\r\nimport json\r\nimport uuid\r\nimport struct\r\nimport base64\r\nimport tempfile\r\nfrom datetime import datetime\r\n\r\nfrom utils import resource_path\r\n\r\ntry:\r\n    from fpdf import FPDF\r\nexcept ImportError:\r\n    FPDF = None\r\n\r\ntry:\r\n    import pandas as pd\r\nexcept ImportError:\r\n    pd = None\r\n\r\ntry:\r\n    import qrcode\r\nexcept ImportError:\r\n    qrcode = None\r\n\r\n# ── Unit Conversion Constants ─────────────────────────────────────────────────\r\nHA_TO_ACRE        = 2.47105\r\nHA_TO_BIGHA_ASSAM = 7.4752    # 1 Assam Bigha = 14,400 sq ft\r\nHA_TO_LECHA_ASSAM = 747.52    # 1 Lecha = 1/100 Assam Bigha\r\n\r\n\r\ndef _fmt_inr(value):\r\n    \"\"\"Format a number as Indian Rupees with commas.\"\"\"\r\n    try:\r\n        v = int(float(value))\r\n        s = str(v)\r\n        if len(s) > 3:\r\n            last3 = s[-3:]\r\n            rest = s[:-3]\r\n            parts = []\r\n            while len(rest) > 2:\r\n                parts.append(rest[-2:])\r\n                rest = rest[:-2]\r\n            if rest:\r\n                parts.append(rest)\r\n            parts.reverse()\r\n            return ','.join(parts) + ',' + last3\r\n        return s\r\n    except Exception:\r\n        return str(value)\r\n\r\n\r\ndef generate_property_card_pdf(record, map_image_base64=None):\r\n    \"\"\"\r\n    Generate a clean, single-page A4 PDF Property Card.\r\n\r\n    Layout (all on one page):\r\n      • Header with QR code\r\n      • 2-column property info table\r\n      • Prominent map section (90mm)\r\n      • Polygon coordinates table\r\n      • Footer\r\n    \"\"\"\r\n    if FPDF is None:\r\n        raise ImportError(\"fpdf2 is required. Install with: pip install fpdf2\")\r\n\r\n    pdf = PropertyCardPDF()\r\n    pdf.set_auto_page_break(auto=False)   # We handle layout manually\r\n    pdf.add_page()\r\n\r\n    # ── Extract Data ─────────────────────────────────────────────────────────\r\n    loc      = record.get(\"location\", {})\r\n    attrs    = record.get(\"attributes\", {})\r\n    owner    = record.get(\"owner\", {})\r\n    mutations = record.get(\"mutation_history\", [])\r\n    geometry = record.get(\"geometry\", {})\r\n\r\n    area_ha    = float(attrs.get(\"area_ha\", 0) or 0)\r\n    area_acres = round(area_ha * HA_TO_ACRE, 2)\r\n    area_bigha = round(area_ha * HA_TO_BIGHA_ASSAM, 2)\r\n    area_lecha = int(round(area_ha * HA_TO_LECHA_ASSAM))\r\n\r\n    try:\r\n        circle_rate = float(attrs.get(\"circle_rate_inr\", 0) or 0)\r\n        land_use = attrs.get(\"land_use\", \"Agricultural\")\r\n        \r\n        multipliers = {\r\n            'Commercial': 2.5, 'Industrial': 1.8, 'Residential': 1.5,\r\n            'Agricultural': 1.0, 'Government': 1.2, 'Forest': 0.8, 'Wasteland': 0.5\r\n        }\r\n        multiplier = multipliers.get(land_use, 1.0)\r\n        estimated_value = area_ha * circle_rate * multiplier\r\n    except Exception:\r\n        circle_rate = 0\r\n        estimated_value = 0\r\n\r\n    state    = loc.get(\"state\",    \"N/A\")\r\n    district = loc.get(\"district\", \"N/A\")\r\n    village  = loc.get(\"village\",  \"N/A\")\r\n\r\n    # ── QR Code (top-right) ──────────────────────────────────────────────────\r\n    qr_path = None\r\n    if qrcode is not None:\r\n        try:\r\n            qr = qrcode.QRCode(version=1, box_size=4, border=1)\r\n            qr.add_data(record.get(\"ulpin\", \"N/A\"))\r\n            qr.make(fit=True)\r\n            qr_img = qr.make_image(fill_color=\"black\", back_color=\"white\")\r\n            buf = io.BytesIO()\r\n            qr_img.save(buf, format=\"PNG\")\r\n            buf.seek(0)\r\n            qr_path = os.path.join(tempfile.gettempdir(), f\"qr_{uuid.uuid4().hex[:8]}.png\")\r\n            with open(qr_path, \"wb\") as f:\r\n                f.write(buf.getvalue())\r\n            pdf.image(qr_path, x=183, y=10, w=18, h=18)\r\n        except Exception:\r\n            pass\r\n\r\n    # ── Header ───────────────────────────────────────────────────────────────\r\n    pdf.set_y(10)\r\n    pdf.set_font(\"Helvetica\", \"B\", 14)\r\n    pdf.cell(170, 7, \"LIMS - Property Card (Khasra Patta)\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n    pdf.set_font(\"Helvetica\", \"\", 8)\r\n    pdf.cell(170, 4, \"Land Information Management System | Academic Prototype\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n    pdf.set_font(\"Helvetica\", \"B\", 9)\r\n    pdf.cell(170, 5, f\"{village}, {district}, {state}\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n    pdf.ln(1)\r\n\r\n    pdf.set_draw_color(30, 64, 150)\r\n    pdf.set_line_width(0.6)\r\n    y_div = pdf.get_y()\r\n    pdf.line(10, y_div, 200, y_div)\r\n    pdf.ln(3)\r\n\r\n    # ── Two-Column Info Table ────────────────────────────────────────────────\r\n    Y_TABLE = pdf.get_y()\r\n    RH = 6.5          # row height mm\r\n    LW = 38           # label cell width\r\n    VW = 54           # value cell width  (total col = 92mm)\r\n    GAP = 6           # gap between cols\r\n\r\n    def draw_row(x, y, label, value, fill=True):\r\n        pdf.set_xy(x, y)\r\n        pdf.set_font(\"Helvetica\", \"B\", 7.5)\r\n        pdf.set_fill_color(230, 237, 255)\r\n        pdf.cell(LW, RH, f\"  {label}\", border=1, fill=fill)\r\n        pdf.set_font(\"Helvetica\", \"\", 7.5)\r\n        pdf.set_fill_color(255, 255, 255)\r\n        val_str = str(value)[:42]\r\n        pdf.cell(VW, RH, f\"  {val_str}\", border=1, fill=True, new_x=\"RIGHT\", new_y=\"TOP\")\r\n\r\n    left = [\r\n        (\"ULPIN\",        record.get(\"ulpin\", \"N/A\")),\r\n        (\"Khasra No.\",   record.get(\"khasra_no\", \"N/A\")),\r\n        (\"Khata No.\",    record.get(\"khata_no\", \"N/A\")),\r\n        (\"Land Use\",     attrs.get(\"land_use\", \"N/A\")),\r\n        (\"State\",        state),\r\n        (\"District\",     district),\r\n        (\"Village/Ward\", village),\r\n        (\"Share (%)\",    f\"{owner.get('share_pct', 'N/A')}%\"),\r\n    ]\r\n    right = [\r\n        (\"Area (Ha)\",    f\"{area_ha} Ha\"),\r\n        (\"Area (Acres)\", f\"{area_acres} Ac\"),\r\n        (\"Area (Bigha)\", f\"{area_bigha} Bigha\"),\r\n        (\"Circle Rate\",  f\"Rs. {_fmt_inr(int(circle_rate))}/Ha\" if circle_rate else \"N/A\"),\r\n        (\"Est. Value\",   f\"Rs. {_fmt_inr(int(estimated_value))}\" if estimated_value else \"N/A\"),\r\n        (\"Centroid\",     f\"{attrs.get('centroid', {}).get('lat', 'N/A')}, {attrs.get('centroid', {}).get('lng', 'N/A')}\"),\r\n        (\"Perimeter\",    f\"{int(attrs.get('perimeter_m', 0))} meters\"),\r\n        (\"Owner\",        owner.get(\"name\", \"N/A\")),\r\n    ]\r\n\r\n    for i, (lbl, val) in enumerate(left):\r\n        draw_row(10, Y_TABLE + i * RH, lbl, val)\r\n    for i, (lbl, val) in enumerate(right):\r\n        draw_row(10 + LW + VW + GAP, Y_TABLE + i * RH, lbl, val)\r\n\r\n    # Extra row for Mutations (full width below the two columns)\r\n    y_mut = Y_TABLE + max(len(left), len(right)) * RH\r\n    draw_row(10, y_mut, \"Mutation History\", f\"{len(mutations)} transaction(s) recorded on this parcel\")\r\n    \r\n    pdf.ln(0)\r\n    y_after_table = y_mut + RH + 3\r\n\r\n    # ── Divider ───────────────────────────────────────────────────────────────\r\n    pdf.set_draw_color(30, 64, 150)\r\n    pdf.set_line_width(0.4)\r\n    pdf.line(10, y_after_table, 200, y_after_table)\r\n\r\n    # ── Map Section ───────────────────────────────────────────────────────────\r\n    MAP_LABEL_Y = y_after_table + 2\r\n    MAP_Y       = MAP_LABEL_Y + 6\r\n    MAP_H       = 118   # mm — generous height now that coords table is removed\r\n\r\n    pdf.set_font(\"Helvetica\", \"B\", 9)\r\n    pdf.set_xy(10, MAP_LABEL_Y)\r\n    pdf.cell(0, 5, \"PARCEL MAP\", new_x=\"LMARGIN\", new_y=\"NEXT\")\r\n\r\n    if map_image_base64:\r\n        try:\r\n            if \",\" in map_image_base64:\r\n                map_image_base64 = map_image_base64.split(\",\")[1]\r\n            img_data = base64.b64decode(map_image_base64)\r\n\r\n            tmp_map = os.path.join(tempfile.gettempdir(), f\"map_{record.get('ulpin','x')}_{uuid.uuid4().hex[:6]}.png\")\r\n            with open(tmp_map, \"wb\") as f:\r\n                f.write(img_data)\r\n\r\n            # Read actual PNG dimensions for proportional placement\r\n            img_w, img_h = 800, 450  # fallback defaults\r\n            try:\r\n                with open(tmp_map, \"rb\") as f:\r\n                    f.read(8)\r\n                    chunk = f.read(17)\r\n                    if len(chunk) == 17:\r\n                        img_w = struct.unpack(\">I\", chunk[8:12])[0]\r\n                        img_h = struct.unpack(\">I\", chunk[12:16])[0]\r\n            except Exception:\r\n                pass\r\n\r\n            # Scale to fill 190mm width, but cap at MAP_H height\r\n            max_w = 190.0\r\n            scale = min(max_w / img_w, MAP_H / (img_h * (210 / img_w)) if img_w else 1)\r\n            draw_w = min(max_w, img_w * (max_w / img_w))\r\n            draw_h = img_h * (draw_w / img_w)\r\n            if draw_h > MAP_H:\r\n                draw_h = MAP_H\r\n                draw_w = img_w * (draw_h / img_h)\r\n\r\n            x_pos = (210 - draw_w) / 2\r\n            pdf.image(tmp_map, x=x_pos, y=MAP_Y, w=draw_w, h=draw_h)\r\n\r\n            try:\r\n                os.remove(tmp_map)\r\n            except Exception:\r\n                pass\r\n\r\n        except Exception as e:\r\n            pdf.set_font(\"Helvetica\", \"I\", 9)\r\n            pdf.set_xy(10, MAP_Y + 5)\r\n            pdf.cell(0, 6, f\"Map not available: {str(e)[:60]}\", new_x=\"LMARGIN\", new_y=\"NEXT\")\r\n    else:\r\n        pdf.set_font(\"Helvetica\", \"I\", 9)\r\n        pdf.set_xy(10, MAP_Y + 5)\r\n        pdf.cell(0, 6, \"Map image not captured.\", new_x=\"LMARGIN\", new_y=\"NEXT\")\r\n\r\n    y_after_map = MAP_Y + MAP_H + 3\r\n\r\n    # ── Page 2: Mutation Ledger (History) ──────────────────────────────────\r\n    if mutations:\r\n        pdf.add_page()\r\n        pdf.set_y(15)\r\n        pdf.set_font(\"Helvetica\", \"B\", 12)\r\n        pdf.cell(0, 7, \"Ownership Mutation Ledger (History of Transactions)\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n        pdf.set_font(\"Helvetica\", \"I\", 8)\r\n        pdf.cell(0, 4, f\"Detailed record of ownership changes for ULPIN: {record.get('ulpin', 'N/A')}\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n        pdf.ln(5)\r\n\r\n        # Table Header\r\n        pdf.set_font(\"Helvetica\", \"B\", 8)\r\n        pdf.set_fill_color(240, 240, 240)\r\n        col_widths = [35, 60, 30, 30, 35]\r\n        headers = [\"Date\", \"Previous Owner\", \"Type\", \"Share\", \"Reference\"]\r\n        for i, h in enumerate(headers):\r\n            pdf.cell(col_widths[i], 8, h, border=1, fill=True, align=\"C\")\r\n        pdf.ln(8)\r\n\r\n        # Table Rows\r\n        pdf.set_font(\"Helvetica\", \"\", 7.5)\r\n        for mut in mutations:\r\n            pdf.cell(col_widths[0], 7, f\" {mut.get('mutation_date', 'N/A')}\", border=1)\r\n            pdf.cell(col_widths[1], 7, f\" {mut.get('previous_owner', 'N/A')[:38]}\", border=1)\r\n            pdf.cell(col_widths[2], 7, f\" {mut.get('mutation_type', 'Sale Deed')}\", border=1)\r\n            pdf.cell(col_widths[3], 7, f\" {mut.get('previous_share_pct', 100)}%\", border=1, align=\"C\")\r\n            pdf.cell(col_widths[4], 7, f\" {mut.get('mutation_ref', 'N/A')[:22]}\", border=1)\r\n            pdf.ln(7)\r\n            \r\n        pdf.ln(10)\r\n        pdf.set_font(\"Helvetica\", \"I\", 8)\r\n        pdf.multi_cell(0, 5, \"Note: This ledger is an archived record of past ownership. The first page of this document reflects the current state of the land record as per the digital database.\")\r\n\r\n\r\n    # ── Footer ───────────────────────────────────────────────────────────────\r\n    pdf.set_y(281)   # 297 - 16mm from bottom\r\n    pdf.set_draw_color(30, 64, 150)\r\n    pdf.set_line_width(0.3)\r\n    pdf.line(10, pdf.get_y(), 200, pdf.get_y())\r\n    pdf.ln(2)\r\n    gen_time = datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")\r\n    pdf.set_font(\"Helvetica\", \"I\", 7)\r\n    doc_id = f\"PC-{record.get('ulpin','N/A')}-{datetime.now().strftime('%Y%m%d%H%M')}\"\r\n    pdf.cell(0, 4, f\"Generated: {gen_time}  |  Document ID: {doc_id}  |  LIMS Academic Prototype\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n    pdf.set_font(\"Helvetica\", \"B\", 7)\r\n    pdf.cell(0, 4, \"Computer-generated document. Scan QR code for digital verification.\", new_x=\"LMARGIN\", new_y=\"NEXT\", align=\"C\")\r\n\r\n    # Cleanup QR\r\n    if qr_path:\r\n        try:\r\n            os.remove(qr_path)\r\n        except Exception:\r\n            pass\r\n\r\n    # ── Output ────────────────────────────────────────────────────────────────\r\n    try:\r\n        return bytes(pdf.output())\r\n    except Exception:\r\n        output = pdf.output()\r\n        if isinstance(output, (bytes, bytearray)):\r\n            return bytes(output)\r\n        return output.encode(\"latin-1\") if isinstance(output, str) else bytes(output)\r\n\r\n\r\nclass PropertyCardPDF(FPDF):\r\n    \"\"\"Custom FPDF with thin blue top border.\"\"\"\r\n\r\n    def header(self):\r\n        self.set_draw_color(30, 64, 150)\r\n        self.set_line_width(1.2)\r\n        self.line(5, 5, 205, 5)\r\n        self.set_line_width(0.3)\r\n        self.set_draw_color(0, 0, 0)\r\n\r\n    def footer(self):\r\n        pass   # Footer handled manually above\r\n\r\n\r\ndef generate_village_excel(records, village_name=\"All Villages\"):\r\n    \"\"\"\r\n    Generate a formatted Excel village ledger from land records.\r\n    Includes Bigha and Lecha columns.\r\n    \"\"\"\r\n    if pd is None:\r\n        raise ImportError(\"pandas and openpyxl are required.\")\r\n\r\n    flat_rows = []\r\n    for rec in records:\r\n        loc   = rec.get(\"location\", {})\r\n        attrs = rec.get(\"attributes\", {})\r\n        owner = rec.get(\"owner\", {})\r\n        muts  = rec.get(\"mutation_history\", [])\r\n        last_mut = muts[-1] if muts else {}\r\n\r\n        area_ha = float(attrs.get(\"area_ha\", 0) or 0)\r\n        circle_rate = float(attrs.get(\"circle_rate_inr\", 0) or 0)\r\n        land_use = attrs.get(\"land_use\", \"Agricultural\")\r\n        \r\n        multipliers = {\r\n            'Commercial': 2.5, 'Industrial': 1.8, 'Residential': 1.5,\r\n            'Agricultural': 1.0, 'Government': 1.2, 'Forest': 0.8, 'Wasteland': 0.5\r\n        }\r\n        multiplier = multipliers.get(land_use, 1.0)\r\n        estimated_value = area_ha * circle_rate * multiplier\r\n\r\n        flat_rows.append({\r\n            \"ULPIN\":                   rec.get(\"ulpin\", \"\"),\r\n            \"Khasra No.\":              rec.get(\"khasra_no\", \"\"),\r\n            \"Khata No.\":               rec.get(\"khata_no\", \"\"),\r\n            \"State\":                   loc.get(\"state\", \"\"),\r\n            \"District\":                loc.get(\"district\", \"\"),\r\n            \"Village\":                 loc.get(\"village\", \"\"),\r\n            \"Area (Ha)\":               area_ha,\r\n            \"Area (Bigha - Assam)\":    round(area_ha * HA_TO_BIGHA_ASSAM, 2),\r\n            \"Area (Lecha - Assam)\":    int(round(area_ha * HA_TO_LECHA_ASSAM)),\r\n            \"Area (Acres)\":            round(area_ha * HA_TO_ACRE, 2),\r\n            \"Land Use\":                attrs.get(\"land_use\", \"\"),\r\n            \"Circle Rate (INR/Ha)\":    circle_rate,\r\n            \"Multiplier\":              multiplier,\r\n            \"Estimated Value (INR)\":   estimated_value,\r\n            \"Owner Name\":              owner.get(\"name\", \"\"),\r\n            \"Share (%)\":               owner.get(\"share_pct\", 0),\r\n            \"Aadhaar (Masked)\":        owner.get(\"aadhaar_mask\", \"\"),\r\n            \"Total Mutations\":         len(muts),\r\n            \"Last Mutation Date\":      last_mut.get(\"mutation_date\", \"\"),\r\n            \"Last Mutation Type\":      last_mut.get(\"mutation_type\", \"\"),\r\n            \"Record ID\":               rec.get(\"_id\", \"\"),\r\n        })\r\n\r\n    df = pd.DataFrame(flat_rows)\r\n    output = io.BytesIO()\r\n    with pd.ExcelWriter(output, engine=\"openpyxl\") as writer:\r\n        df.to_excel(writer, index=False, sheet_name=\"Village Ledger\")\r\n        try:\r\n            ws = writer.sheets[\"Village Ledger\"]\r\n            for idx, col in enumerate(df.columns):\r\n                max_len = max(\r\n                    df[col].astype(str).map(len).max() if len(df) > 0 else 0,\r\n                    len(col)\r\n                )\r\n                col_letter = chr(65 + idx) if idx < 26 else chr(64 + idx // 26) + chr(65 + idx % 26)\r\n                ws.column_dimensions[col_letter].width = min(max_len + 3, 40)\r\n        except Exception:\r\n            pass\r\n\r\n    output.seek(0)\r\n    return output.getvalue()\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    sample = {\r\n        \"_id\": \"test-001\",\r\n        \"ulpin\": \"18011010001001\",\r\n        \"khasra_no\": \"42/B\",\r\n        \"khata_no\": \"KH-07\",\r\n        \"location\": {\"state\": \"Assam\", \"district\": \"Kamrup Metropolitan\", \"village\": \"Guwahati Ward 12\"},\r\n        \"attributes\": {\"area_ha\": 1.34, \"land_use\": \"Agricultural\", \"circle_rate_inr\": 85000},\r\n        \"owner\": {\"name\": \"Ramesh Kumar\", \"share_pct\": 100, \"aadhaar_mask\": \"XXXX-XXXX-7890\"},\r\n        \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[91.76, 26.12], [91.765, 26.12], [91.765, 26.125], [91.76, 26.125], [91.76, 26.12]]]},\r\n        \"mutation_history\": []\r\n    }\r\n    b = generate_property_card_pdf(sample)\r\n    print(f\"PDF generated: {len(b)} bytes\")\r\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "pytest>=7.0\r\nflask>=3.0\r\npymongo>=4.0\r\ncertifi\r\npython-dotenv\r\n"
  },
  {
    "path": "requirements-windows.txt",
    "content": "# Base Web Requirements\n-r requirements.txt\n\n# Windows Desktop Specific\npywebview==4.4.1\npythonnet==3.0.3\npyinstaller==6.3.0\npillow==10.1.0\n"
  },
  {
    "path": "requirements.txt",
    "content": "Flask==3.0.0\npymongo[srv]==4.6.1\npython-dotenv==1.0.0\nreportlab==4.0.8\npandas\nopenpyxl==3.1.2\ngunicorn==21.2.0\nwerkzeug==3.0.1\njinja2==3.1.2\nitsdangerous==2.1.2\nclick==8.1.7\nblinker==1.7.0\ncertifi==2023.11.17\nfpdf2==2.7.7\nqrcode==7.4.2\n"
  },
  {
    "path": "routes/__init__.py",
    "content": "from .pages import pages_bp\nfrom .auth import auth_bp\nfrom .records import records_bp\nfrom .users import users_bp\nfrom .gis import gis_bp\nfrom .documents import documents_bp\nfrom .feedback import feedback_bp\nfrom .utils import utils_bp\n"
  },
  {
    "path": "routes/auth.py",
    "content": "from datetime import datetime\nfrom flask import Blueprint, request, jsonify, session\nfrom werkzeug.security import check_password_hash\nfrom core import generate_captcha, verify_captcha_logic, load_users, save_users\n\nauth_bp = Blueprint('auth', __name__)\n\n@auth_bp.route(\"/api/captcha\", methods=[\"GET\"])\ndef get_captcha():\n    question, token = generate_captcha()\n    return jsonify({\"question\": question, \"token\": token})\n\n@auth_bp.route(\"/api/verify-captcha\", methods=[\"POST\"])\ndef verify_captcha():\n    data = request.get_json() or {}\n    user_answer = str(data.get(\"answer\", \"\")).strip()\n    token = str(data.get(\"token\", \"\")).strip()\n\n    if verify_captcha_logic(token, user_answer):\n        session.permanent = True\n        session[\"role\"] = \"viewer\"\n        session[\"username\"] = \"Viewer\"\n        return jsonify({\"success\": True, \"redirect\": \"/viewer\"})\n    else:\n        new_question, new_token = generate_captcha()\n        return jsonify({\"success\": False, \"message\": \"Incorrect answer or expired. Please try again.\", \"new_question\": new_question, \"new_token\": new_token}), 400\n\n@auth_bp.route(\"/api/login\", methods=[\"POST\"])\ndef admin_login():\n    data = request.get_json() or {}\n    username = data.get(\"username\", \"\").strip()\n    password = data.get(\"password\", \"\")\n\n    if not username or not password:\n        return jsonify({\"error\": \"Username and password are required.\"}), 400\n\n    users = load_users()\n    user = next((u for u in users if u[\"username\"] == username), None)\n\n    if user and check_password_hash(user[\"password_hash\"], password):\n        session.permanent = True\n        role = (user.get(\"role\") or \"Officer\").lower()\n        session[\"role\"] = role\n        session[\"username\"] = username\n        session[\"admin_id\"] = user.get(\"user_id\", \"\")\n\n        user[\"last_login\"] = datetime.now().isoformat() + \"Z\"\n        save_users(users)\n\n        redirect_url = \"/admin\" if role in (\"admin\", \"superadmin\") else \"/viewer\"\n        return jsonify({\"success\": True, \"redirect\": redirect_url})\n    else:\n        return jsonify({\"error\": \"Invalid username or password.\"}), 401\n\n@auth_bp.route(\"/api/logout\", methods=[\"POST\"])\ndef logout():\n    session.clear()\n    return jsonify({\"success\": True, \"redirect\": \"/login\"})\n\n@auth_bp.route(\"/api/session-info\", methods=[\"GET\"])\ndef session_info():\n    return jsonify({\n        \"role\": session.get(\"role\", None),\n        \"username\": session.get(\"username\", None),\n        \"is_authenticated\": session.get(\"role\") is not None\n    })\n\n@auth_bp.route(\"/api/forgot\", methods=[\"GET\", \"POST\"])\ndef forgot_password():\n    \"\"\"Handle password recovery instructions.\"\"\"\n    return jsonify({\n        \"success\": True, \n        \"instructions\": \"To recover your password, please contact the District Revenue Officer or System Administrator at support@india-lims.gov.in. Provide your Employee ID and Office Location for verification.\"\n    })\n"
  },
  {
    "path": "routes/documents.py",
    "content": "import io\nfrom datetime import datetime\nfrom flask import Blueprint, request, jsonify, send_file\nfrom core import load_records, viewer_or_admin_required, admin_required\n\ndocuments_bp = Blueprint('documents', __name__)\n\n@documents_bp.route(\"/api/print-card/<ulpin>\", methods=[\"GET\", \"POST\"])\n@viewer_or_admin_required\ndef print_property_card(ulpin):\n    records = load_records()\n    record = next((r for r in records if r.get(\"ulpin\") == ulpin), None)\n    if not record: return jsonify({\"error\": \"Not found.\"}), 404\n    map_image = None\n    if request.method == \"POST\":\n        map_image = (request.get_json() or {}).get(\"map_image\")\n    try:\n        from report_generator import generate_property_card_pdf\n        pdf_bytes = generate_property_card_pdf(record, map_image_base64=map_image)\n        return send_file(io.BytesIO(pdf_bytes), mimetype=\"application/pdf\", as_attachment=True, download_name=f\"Card_{ulpin}.pdf\")\n    except Exception as e:\n        return jsonify({\"error\": str(e)}), 500\n\n@documents_bp.route(\"/api/export-village\", methods=[\"GET\"])\n@admin_required\ndef export_village_ledger():\n    records = load_records()\n    village = request.args.get(\"village\", \"\").strip()\n    if village:\n        records = [r for r in records if r.get(\"location\", {}).get(\"village\", \"\").lower() == village.lower()]\n    if not records: return jsonify({\"error\": \"No records.\"}), 404\n    try:\n        from report_generator import generate_village_excel\n        excel_bytes = generate_village_excel(records, village_name=village or \"All\")\n        return send_file(io.BytesIO(excel_bytes), mimetype=\"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\", as_attachment=True, download_name=\"Ledger.xlsx\")\n    except Exception as e:\n        return jsonify({\"error\": str(e)}), 500\n"
  },
  {
    "path": "routes/feedback.py",
    "content": "import uuid\nimport os\nimport json\nfrom datetime import datetime\nfrom flask import Blueprint, request, jsonify, session\nfrom core import (\n    load_feedback, save_feedback, load_records, admin_required, \n    viewer_or_admin_required, _apply_filters_to_records, audit_collection, DATA_DIR,\n    _calculate_estimated_value\n)\n\nfeedback_bp = Blueprint('feedback', __name__)\n\n@feedback_bp.route(\"/api/feedback\", methods=[\"GET\"])\n@admin_required\ndef get_feedback():\n    \"\"\"Admin-only: Fetch all feedback submissions.\"\"\"\n    return jsonify(load_feedback())\n\n@feedback_bp.route(\"/api/feedback\", methods=[\"POST\"])\n@viewer_or_admin_required\ndef submit_feedback():\n    \"\"\"Submit feedback or issue reports.\"\"\"\n    data = request.get_json() or {}\n    email = data.get(\"email\", \"\").strip()\n    message = data.get(\"message\", \"\").strip()\n    if not email or not message:\n        return jsonify({\"error\": \"Required fields missing.\"}), 400\n        \n    entry = {\n        \"id\": str(uuid.uuid4()),\n        \"email\": email,\n        \"type\": data.get(\"type\", \"General\"),\n        \"message\": message,\n        \"timestamp\": datetime.now().isoformat(),\n        \"status\": \"New\"\n    }\n    fb = load_feedback()\n    fb.append(entry)\n    save_feedback(fb)\n    return jsonify({\"success\": True})\n\n@feedback_bp.route(\"/api/dashboard\", methods=[\"GET\"])\n@admin_required\ndef dashboard_analytics():\n    \"\"\"Compute heavy aggregation for the admin dashboard charts and KPIs.\"\"\"\n    records = load_records()\n    params = {k: request.args.get(k, \"\") for k in [\"state\", \"district\", \"village\", \"land_use\", \"search\"]}\n    filtered = _apply_filters_to_records(records, params)\n    \n    total_area = 0.0\n    total_value = 0.0\n    total_mutations = 0\n    land_use_stats = {}\n    district_stats = {}\n    top_parcel = None\n    top_parcel_value = -1\n    \n    for rec in filtered:\n        loc = rec.get(\"location\", {})\n        attrs = rec.get(\"attributes\", {})\n        area = float(attrs.get(\"area_ha\", 0) or 0)\n        rate = float(attrs.get(\"circle_rate_inr\", 0) or 0)\n        lu = attrs.get(\"land_use\", \"Agricultural\")\n        value = _calculate_estimated_value(area, rate, lu)\n        \n        total_area += area\n        total_value += value\n        muts = rec.get(\"mutation_history\", []) or []\n        total_mutations += len(muts)\n        \n        # Land use distribution\n        lu = attrs.get(\"land_use\", \"Unknown\")\n        if lu not in land_use_stats: land_use_stats[lu] = {\"count\": 0, \"area\": 0.0}\n        land_use_stats[lu][\"count\"] += 1\n        land_use_stats[lu][\"area\"] += area\n        \n        # District stats\n        d = loc.get(\"district\", \"Unknown\")\n        if d not in district_stats: district_stats[d] = {\"count\": 0, \"area\": 0.0, \"value\": 0.0}\n        district_stats[d][\"count\"] += 1\n        district_stats[d][\"area\"] += area\n        district_stats[d][\"value\"] += value\n        \n        if value > top_parcel_value:\n            top_parcel_value = value\n            top_parcel = {\n                \"khasra_no\": rec.get(\"khasra_no\"), \n                \"ulpin\": rec.get(\"ulpin\"),\n                \"village\": loc.get(\"village\"), \n                \"district\": d, \n                \"land_use\": attrs.get(\"land_use\", \"N/A\"),\n                \"area_ha\": round(area, 2),\n                \"estimated_value\": round(value, 0)\n            }\n            \n    sorted_districts = sorted(district_stats.items(), key=lambda x: x[1][\"value\"], reverse=True)[:6]\n    \n    all_mutations = []\n    for rec in filtered:\n        for m in rec.get(\"mutation_history\", []) or []:\n            all_mutations.append({\n                \"khasra_no\": rec.get(\"khasra_no\"), \n                \"district\": rec.get(\"location\", {}).get(\"district\"),\n                \"previous_owner\": m.get(\"previous_owner\"), \n                \"mutation_date\": m.get(\"mutation_date\"),\n                \"mutation_type\": m.get(\"mutation_type\", \"Mutation\")\n            })\n    all_mutations.sort(key=lambda x: str(x.get(\"mutation_date\", \"\")), reverse=True)\n    \n    return jsonify({\n        \"kpis\": {\n            \"total_parcels\": len(filtered),\n            \"total_area\": round(total_area, 2),\n            \"estimated_value\": round(total_value, 0),\n            \"total_mutations\": total_mutations\n        },\n        \"land_use_distribution\": land_use_stats,\n        \"district_overview\": [{\"name\": d, **s} for d, s in sorted_districts],\n        \"top_parcel\": top_parcel,\n        \"recent_mutations\": all_mutations[:6]\n    })\n\n@feedback_bp.route('/api/audit', methods=['GET'])\n@admin_required\ndef list_audit():\n    \"\"\"Fetch system audit logs.\"\"\"\n    limit = min(100, max(1, int(request.args.get('limit', 50))))\n    if audit_collection is not None:\n        entries = list(audit_collection.find({}, {'_id': 0}).sort('timestamp', -1).limit(limit))\n    else:\n        audit_file = os.path.join(DATA_DIR, 'audit.json')\n        if os.path.exists(audit_file):\n            with open(audit_file, 'r', encoding='utf-8') as f:\n                entries = json.load(f)\n                entries = sorted(entries, key=lambda x: x.get('timestamp', ''), reverse=True)[:limit]\n        else:\n            entries = []\n    return jsonify(entries)\n\n@feedback_bp.route(\"/api/feedback/<feedback_id>\", methods=[\"DELETE\"])\n@admin_required\ndef delete_feedback(feedback_id):\n    fb = load_feedback()\n    new_fb = [entry for entry in fb if entry.get(\"id\") != feedback_id]\n    if len(new_fb) == len(fb):\n        return jsonify({\"error\": \"Feedback not found.\"}), 404\n    save_feedback(new_fb)\n    return jsonify({\"success\": True})\n\n@feedback_bp.route(\"/api/feedback/<feedback_id>/status\", methods=[\"PUT\"])\n@admin_required\ndef update_feedback_status(feedback_id):\n    \"\"\"Mark feedback as reviewed or resolved.\"\"\"\n    data = request.get_json() or {}\n    new_status = data.get(\"status\", \"Reviewed\")\n    \n    fb = load_feedback()\n    entry = next((e for e in fb if e.get(\"id\") == feedback_id), None)\n    if not entry:\n        return jsonify({\"error\": \"Feedback not found.\"}), 404\n        \n    entry[\"status\"] = new_status\n    entry[\"reviewed_at\"] = datetime.now().isoformat()\n    entry[\"reviewed_by\"] = session.get(\"username\", \"admin\")\n    \n    save_feedback(fb)\n    return jsonify({\"success\": True, \"status\": new_status})\n"
  },
  {
    "path": "routes/gis.py",
    "content": "import os\nimport json\nfrom flask import Blueprint, request, jsonify, current_app\nfrom urllib.parse import urlencode\nfrom urllib.request import Request, urlopen\nfrom core import role_required\n\ngis_bp = Blueprint('gis', __name__)\n\n@gis_bp.route(\"/api/boundary\", methods=[\"GET\"])\ndef get_india_boundary():\n    geojson_path = os.path.join(current_app.root_path, \"static\", \"data\", \"india-boundary.geojson\")\n    try:\n        with open(geojson_path, \"r\", encoding=\"utf-8\") as f:\n            data = json.load(f)\n        return jsonify(data)\n    except FileNotFoundError:\n        return jsonify({\"error\": \"Boundary data not found.\"}), 404\n\n@gis_bp.route(\"/api/calculate-area\", methods=[\"POST\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef api_calculate_area():\n    data = request.get_json() or {}\n    geometry = data.get(\"geometry\")\n    if not geometry: return jsonify({\"error\": \"Geometry required.\"}), 400\n    from gis_processor import calculate_area, calculate_perimeter, get_centroid\n    return jsonify({\n        \"area\": calculate_area(geometry),\n        \"perimeter\": calculate_perimeter(geometry),\n        \"centroid\": get_centroid(geometry)\n    })\n\n@gis_bp.route(\"/api/validate-geometry\", methods=[\"POST\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef api_validate_geometry():\n    \"\"\"Validate a GeoJSON polygon geometry.\"\"\"\n    data = request.get_json() or {}\n    geometry = data.get(\"geometry\")\n    if not geometry: return jsonify({\"error\": \"Geometry is required.\"}), 400\n    from gis_processor import validate_polygon\n    return jsonify(validate_polygon(geometry))\n\n@gis_bp.route(\"/api/location-from-coords\", methods=[\"GET\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef location_from_coordinates():\n    lat = request.args.get(\"lat\", type=float)\n    lng = request.args.get(\"lng\", type=float)\n    if lat is None or lng is None: return jsonify({\"error\": \"lat and lng required.\"}), 400\n    query = urlencode({\"lat\": f\"{lat:.6f}\", \"lon\": f\"{lng:.6f}\", \"format\": \"jsonv2\", \"addressdetails\": 1})\n    url = f\"https://nominatim.openstreetmap.org/reverse?{query}\"\n    try:\n        req = Request(url, headers={\"User-Agent\": \"LIMS/1.0\"})\n        with urlopen(req, timeout=8) as response:\n            data = json.loads(response.read().decode(\"utf-8\"))\n        addr = data.get(\"address\", {})\n        \n        # Robust detection for Indian administrative levels\n        state = addr.get(\"state\", \"\")\n        \n        # District can be in several fields\n        district = addr.get(\"state_district\") or addr.get(\"district\") or addr.get(\"county\") or addr.get(\"city\") or \"\"\n        \n        # Clean up district names (e.g. \"Indore District\" -> \"Indore\")\n        district = district.replace(\" District\", \"\").replace(\" Zila\", \"\").replace(\" Dist.\", \"\").strip()\n        \n        # Village/Ward/Locality can be in many fields in India\n        village = (\n            addr.get(\"village\") or \n            addr.get(\"suburb\") or \n            addr.get(\"neighbourhood\") or \n            addr.get(\"hamlet\") or \n            addr.get(\"town\") or \n            addr.get(\"city_district\") or \n            addr.get(\"locality\") or \n            addr.get(\"residential\") or\n            \"\"\n        )\n        \n        return jsonify({\n            \"success\": True,\n            \"state\": state,\n            \"district\": district,\n            \"village\": village,\n            \"display_name\": data.get(\"display_name\", \"\")\n        })\n    except Exception as e:\n        return jsonify({\"error\": str(e)}), 502\n\n@gis_bp.route(\"/api/location-catalog\", methods=[\"GET\"])\ndef get_location_catalog():\n    \"\"\"Return the master hierarchy of States, Districts, and Villages.\"\"\"\n    # This would typically come from a DB, but we'll use a robust static catalog for India\n    catalog = {\n        \"Madhya Pradesh\": {\n            \"Indore\": [\"Bicholi Mardana\", \"Kanadia\", \"Hatod\", \"Rau\", \"Mhow\"],\n            \"Bhopal\": [\"Bairagarh\", \"Huzur\", \"Berasia\", \"Misrod\", \"Arera\"],\n            \"Jabalpur\": [\"Panagar\", \"Sihora\", \"Patan\", \"Shahpura\"],\n            \"Gwalior\": [\"Dabra\", \"Bhitarwar\", \"Chinore\"],\n            \"Ujjain\": [\"Nagda\", \"Mahidpur\", \"Tarana\", \"Khachrod\"]\n        },\n        \"Maharashtra\": {\n            \"Mumbai\": [\"Colaba\", \"Dadar\", \"Andheri\", \"Borivali\", \"Kurla\"],\n            \"Pune\": [\"Haveli\", \"Khed\", \"Shirur\", \"Baramati\", \"Indapur\"],\n            \"Nagpur\": [\"Kamptee\", \"Ramtek\", \"Katol\", \"Saoner\"],\n            \"Nashik\": [\"Malegaon\", \"Sinnar\", \"Yeola\", \"Igatpuri\"]\n        },\n        \"Uttar Pradesh\": {\n            \"Lucknow\": [\"Bakshi Ka Talab\", \"Malihabad\", \"Mohanlalganj\"],\n            \"Kanpur\": [\"Bilhaur\", \"Ghatampur\"],\n            \"Varanasi\": [\"Pindra\", \"Rajatalab\"],\n            \"Agra\": [\"Etmadpur\", \"Fatehabad\", \"Kheragarh\"]\n        },\n        \"Delhi\": {\n            \"New Delhi\": [\"Connaught Place\", \"Chanakyapuri\"],\n            \"South Delhi\": [\"Saket\", \"Hauz Khas\", \"Mehrauli\"],\n            \"North Delhi\": [\"Model Town\", \"Narela\"],\n            \"East Delhi\": [\"Preet Vihar\", \"Mayur Vihar\"]\n        }\n    }\n    return jsonify(catalog)\n"
  },
  {
    "path": "routes/pages.py",
    "content": "from flask import Blueprint, render_template, redirect, url_for, session, request, jsonify\nfrom core import generate_captcha\n\npages_bp = Blueprint('pages', __name__)\n\n@pages_bp.route(\"/\")\ndef index():\n    return redirect(url_for(\"pages.login_page\"))\n\n@pages_bp.route(\"/login\")\ndef login_page():\n    role = (session.get(\"role\") or \"\").lower()\n    if role in (\"admin\", \"superadmin\"):\n        return redirect(url_for(\"pages.admin_dashboard\"))\n    elif role == \"viewer\":\n        return redirect(url_for(\"pages.viewer_page\"))\n    captcha_question, captcha_token = generate_captcha()\n    return render_template(\"login.html\", captcha_question=captcha_question, captcha_token=captcha_token)\n\n@pages_bp.route(\"/admin\")\ndef admin_dashboard():\n    role = (session.get(\"role\") or \"\").lower()\n    if role not in (\"admin\", \"superadmin\"):\n        return redirect(url_for(\"pages.login_page\"))\n    return render_template(\"admin_dashboard.html\", username=session.get(\"username\", \"Admin\"))\n\n@pages_bp.route(\"/viewer\")\ndef viewer_page():\n    role = (session.get(\"role\") or \"\").lower()\n    if role not in (\"admin\", \"superadmin\", \"viewer\"):\n        return redirect(url_for(\"pages.login_page\"))\n    return render_template(\"public_viewer_v2.html\")\n"
  },
  {
    "path": "routes/records.py",
    "content": "import uuid\nimport random\nfrom datetime import datetime\nfrom flask import Blueprint, request, jsonify, session\nfrom core import (\n    load_records, save_records, viewer_or_admin_required, role_required,\n    _strip_b64_from_list, _mask_owner_for_viewer, _log_audit,\n    _generate_ulpin, _update_nested, _apply_filters_to_records\n)\n\nrecords_bp = Blueprint('records', __name__)\n\ndef generate_ulpin(state_name, district_name):\n    \"\"\"Generate a unique 14-digit ULPIN.\"\"\"\n    state_code = str(abs(hash(state_name)) % 90 + 10)\n    dist_code = str(abs(hash(district_name)) % 90 + 10)\n    parcel_code = ''.join([str(random.randint(0, 9)) for _ in range(10)])\n    return f\"{state_code}{dist_code}{parcel_code}\"\n\n@records_bp.route(\"/api/records\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef get_records():\n    \"\"\"Fetch all land records with role-based masking and filtering.\"\"\"\n    records = load_records()\n    records = _strip_b64_from_list(records)\n    role = (session.get(\"role\") or \"\").lower()\n    \n    # Exclude soft-deleted records for non-admins\n    if role not in (\"admin\", \"superadmin\"):\n        records = [r for r in records if not r.get(\"deleted\")]\n    \n    # Mask owner details for public viewers\n    if role == \"viewer\":\n        records = [_mask_owner_for_viewer(rec) for rec in records]\n        \n    return jsonify(records)\n\n@records_bp.route(\"/api/records/<record_id>\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef get_record(record_id):\n    \"\"\"Fetch a single land record by its ID.\"\"\"\n    records = load_records()\n    record = next((r for r in records if r[\"_id\"] == record_id), None)\n    \n    if not record:\n        return jsonify({\"error\": \"Record not found.\"}), 404\n        \n    role = (session.get(\"role\") or \"\").lower()\n    if record.get(\"deleted\") and role not in (\"admin\", \"superadmin\"):\n        return jsonify({\"error\": \"Record not found.\"}), 404\n        \n    if role == \"viewer\":\n        record = _mask_owner_for_viewer(record)\n        \n    return jsonify(record)\n\n@records_bp.route(\"/api/records/search\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef search_records():\n    \"\"\"Search records using a global text query.\"\"\"\n    query = request.args.get(\"q\", \"\").strip().lower()\n    if not query:\n        return jsonify({\"error\": \"Search query parameter 'q' is required.\"}), 400\n        \n    records = load_records()\n    results = _apply_filters_to_records(records, {\"search\": query})\n    \n    role = (session.get(\"role\") or \"\").lower()\n    if role not in (\"admin\", \"superadmin\"):\n        results = [r for r in results if not r.get(\"deleted\")]\n    if role == \"viewer\":\n        results = [_mask_owner_for_viewer(rec) for rec in results]\n        \n    return jsonify(results)\n\n@records_bp.route(\"/api/records/filter\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef filter_records():\n    \"\"\"Advanced filtering of records by location and attributes.\"\"\"\n    records = load_records()\n    params = {k: request.args.get(k, \"\") for k in [\"state\", \"district\", \"village\", \"land_use\", \"search\"]}\n    filtered = _apply_filters_to_records(records, params)\n    \n    role = (session.get(\"role\") or \"\").lower()\n    if role not in (\"admin\", \"superadmin\"):\n        filtered = [r for r in filtered if not r.get(\"deleted\")]\n    \n    filtered = _strip_b64_from_list(filtered)\n    return jsonify(filtered)\n\n@records_bp.route(\"/api/location-catalog\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef location_catalog():\n    \"\"\"Return the hierarchy of state > district > village for dropdowns.\"\"\"\n    records = load_records()\n    catalog = {}\n    for rec in records:\n        loc = rec.get(\"location\", {})\n        state, dist, vill = loc.get(\"state\"), loc.get(\"district\"), loc.get(\"village\")\n        if not all([state, dist, vill]): continue\n        \n        if state not in catalog: catalog[state] = {}\n        if dist not in catalog[state]: catalog[state][dist] = set()\n        catalog[state][dist].add(vill)\n    \n    # Format for JSON\n    result = {}\n    for s in sorted(catalog.keys()):\n        result[s] = {d: sorted(list(v)) for d, v in sorted(catalog[s].items())}\n    return jsonify(result)\n\n@records_bp.route(\"/api/records\", methods=[\"POST\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef create_record():\n    \"\"\"Create a new land record with geometry validation.\"\"\"\n    data = request.get_json() or {}\n    required_fields = [\"khasra_no\", \"khata_no\", \"location\", \"geometry\", \"land_use\", \"owner_name\"]\n    missing = [f for f in required_fields if not data.get(f)]\n    if missing:\n        return jsonify({\"error\": f\"Missing fields: {', '.join(missing)}\"}), 400\n    \n    username = session.get(\"username\", \"admin\")\n    \n    # 1. Geometry Validation & Metrics\n    geometry = data.get(\"geometry\")\n    if not geometry:\n        return jsonify({\"error\": \"Geometry is required.\"}), 400\n        \n    from gis_processor import validate_polygon, calculate_area, check_overlap, get_centroid, calculate_perimeter\n    validation = validate_polygon(geometry)\n    if not validation[\"valid\"]:\n        return jsonify({\"error\": \"Invalid Geometry\", \"details\": validation[\"errors\"]}), 400\n        \n    # 2. Overlap Check\n    records = load_records()\n    overlap = check_overlap(geometry, [r for r in records if not r.get(\"deleted\")])\n    if overlap[\"overlaps\"]:\n        return jsonify({\n            \"error\": \"Spatial Overlap Detected\", \n            \"conflicting_records\": overlap[\"conflicting_records\"]\n        }), 409\n\n    # 3. Auto-fill Data\n    area_data = calculate_area(geometry)\n    centroid = get_centroid(geometry)\n    \n    loc = data.get(\"location\", {})\n    state = loc.get(\"state\", \"Unknown\")\n    district = loc.get(\"district\", \"Unknown\")\n    \n    ulpin = data.get(\"ulpin\")\n    if not ulpin or len(str(ulpin)) < 10:\n        ulpin = generate_ulpin(state, district)\n        # Ensure uniqueness\n        while any(r.get(\"ulpin\") == ulpin for r in records):\n            ulpin = generate_ulpin(state, district)\n\n    new_record = {\n        \"_id\": str(uuid.uuid4()),\n        \"ulpin\": ulpin,\n        \"khasra_no\": data.get(\"khasra_no\", \"N/A\"),\n        \"khata_no\": data.get(\"khata_no\", \"N/A\"),\n        \"location\": {\n            \"state\": state,\n            \"district\": district,\n            \"village\": loc.get(\"village\", \"Unknown\")\n        },\n        \"owner\": {\n            \"name\": data.get(\"owner_name\", \"N/A\"),\n            \"share_pct\": data.get(\"share_pct\", 100),\n            \"aadhaar_mask\": data.get(\"aadhaar_mask\", \"XXXX-XXXX-XXXX\"),\n            \"proof_doc_b64\": data.get(\"owner_proof_doc_b64\")\n        },\n        \"attributes\": {\n            \"area_ha\": area_data.get(\"area_ha\", 0),\n            \"land_use\": data.get(\"land_use\", \"Other\"),\n            \"circle_rate_inr\": data.get(\"circle_rate_inr\", 0),\n            \"centroid\": centroid,\n            \"perimeter_m\": calculate_perimeter(geometry).get(\"perimeter_m\", 0)\n        },\n        \"geometry\": geometry,\n        \"mutation_history\": [],\n        \"deleted\": False\n    }\n    \n    records.append(new_record)\n    save_records(records)\n    \n    _log_audit('create', username, new_record[\"_id\"], {'ulpin': ulpin, 'khasra_no': new_record[\"khasra_no\"]})\n    return jsonify({\"success\": True, \"record\": new_record}), 201\n\n@records_bp.route(\"/api/records/<record_id>\", methods=[\"PUT\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef update_record(record_id):\n    \"\"\"Update a record or perform an ownership mutation.\"\"\"\n    data = request.get_json() or {}\n    records = load_records()\n    record = next((r for r in records if r[\"_id\"] == record_id), None)\n    \n    if not record:\n        return jsonify({\"error\": \"Record not found.\"}), 404\n\n    from gis_processor import validate_polygon, calculate_area, get_centroid, calculate_perimeter\n\n    # --- Scenario A: Ownership Mutation ---\n    if data.get(\"mutation\") and data.get(\"new_owner_name\"):\n        old_owner = record.get(\"owner\", {})\n        mutation_entry = {\n            \"previous_owner\": old_owner.get(\"name\", \"Unknown\"),\n            \"previous_share_pct\": old_owner.get(\"share_pct\", 0),\n            \"previous_aadhaar\": old_owner.get(\"aadhaar_mask\", \"XXXX-XXXX-XXXX\"),\n            \"previous_proof_doc\": old_owner.get(\"proof_doc_b64\"),\n            \"mutation_date\": data.get(\"mutation_date\", datetime.now().strftime(\"%Y-%m-%d\")),\n            \"mutation_type\": data.get(\"mutation_type\", \"Sale Deed\"),\n            \"mutation_ref\": data.get(\"mutation_ref\", f\"MUT-{datetime.now().strftime('%Y')}-{random.randint(10000, 99999)}\"),\n            \"proof_doc_b64\": data.get(\"mutation_proof_doc_b64\")\n        }\n        if \"mutation_history\" not in record: record[\"mutation_history\"] = []\n        record[\"mutation_history\"].append(mutation_entry)\n        record[\"owner\"] = {\n            \"name\": data[\"new_owner_name\"],\n            \"share_pct\": data.get(\"new_share_pct\", 100),\n            \"aadhaar_mask\": data.get(\"new_aadhaar_mask\", \"XXXX-XXXX-XXXX\")\n        }\n        \n        # Mutations can also update location/geometry if provided\n        if \"location\" in data: record[\"location\"] = data[\"location\"]\n        if \"geometry\" in data:\n            val = validate_polygon(data[\"geometry\"])\n            if val[\"valid\"]:\n                record[\"geometry\"] = data[\"geometry\"]\n                record[\"attributes\"][\"area_ha\"] = calculate_area(data[\"geometry\"]).get(\"area_ha\", 0)\n                record[\"attributes\"][\"centroid\"] = get_centroid(data[\"geometry\"])\n                record[\"attributes\"][\"perimeter_m\"] = calculate_perimeter(data[\"geometry\"]).get(\"perimeter_m\", 0)\n\n    # --- Scenario B: Regular Field Updates ---\n    else:\n        for field in [\"khasra_no\", \"khata_no\"]:\n            if field in data: record[field] = data[field]\n        \n        if \"land_use\" in data: _update_nested(record, \"attributes\", \"land_use\", data[\"land_use\"])\n        if \"circle_rate_inr\" in data: _update_nested(record, \"attributes\", \"circle_rate_inr\", data[\"circle_rate_inr\"])\n        if \"share_pct\" in data: _update_nested(record, \"owner\", \"share_pct\", data[\"share_pct\"])\n        if \"aadhaar_mask\" in data: _update_nested(record, \"owner\", \"aadhaar_mask\", data[\"aadhaar_mask\"])\n        if \"location\" in data: record[\"location\"] = data[\"location\"]\n        if \"owner_proof_doc_b64\" in data: _update_nested(record, \"owner\", \"proof_doc_b64\", data[\"owner_proof_doc_b64\"])\n        \n        if \"geometry\" in data:\n            val = validate_polygon(data[\"geometry\"])\n            if not val[\"valid\"]:\n                return jsonify({\"error\": \"Invalid geometry.\", \"details\": val[\"errors\"]}), 400\n            record[\"geometry\"] = data[\"geometry\"]\n            \n            # Check for overlaps with other records\n            from gis_processor import check_overlap\n            others = [r for r in records if r[\"_id\"] != record_id and not r.get(\"deleted\")]\n            overlap_result = check_overlap(data[\"geometry\"], others)\n            if overlap_result.get(\"overlaps\"):\n                return jsonify({\"error\": \"Parcel overlap detected.\", \"conflicting_records\": overlap_result[\"conflicting_records\"]}), 409\n                \n            record[\"attributes\"][\"area_ha\"] = calculate_area(data[\"geometry\"]).get(\"area_ha\", 0)\n            record[\"attributes\"][\"centroid\"] = get_centroid(data[\"geometry\"])\n            record[\"attributes\"][\"perimeter_m\"] = calculate_perimeter(data[\"geometry\"]).get(\"perimeter_m\", 0)\n\n    save_records(records)\n    \n    username = session.get(\"username\", \"admin\")\n    _log_audit('update', username, record_id, {'ulpin': record.get('ulpin'), 'khasra_no': record.get('khasra_no')})\n    \n    return jsonify({\"success\": True, \"record\": record})\n\n@records_bp.route(\"/api/records/<record_id>\", methods=[\"DELETE\"])\n@role_required(\"admin\", \"superadmin\", \"officer\")\ndef delete_record(record_id):\n    \"\"\"\n    Safely delete a record.\n    - If active: Soft-delete (move to trash).\n    - If already in trash: Hard-delete (permanent) if user is Admin/Superadmin.\n    \"\"\"\n    records = load_records()\n    record_index = next((i for i, r in enumerate(records) if r[\"_id\"] == record_id), None)\n    \n    if record_index is None:\n        return jsonify({\"error\": \"Record not found.\"}), 404\n        \n    record = records[record_index]\n    role = (session.get(\"role\") or \"\").lower()\n    username = session.get(\"username\", \"admin\")\n    \n    if record.get(\"deleted\"):\n        # Record is already in trash. Only Admins can permanently remove it.\n        if role in (\"admin\", \"superadmin\"):\n            records.pop(record_index)\n            save_records(records)\n            _log_audit('hard_delete', username, record_id, {'khasra_no': record.get('khasra_no')})\n            return jsonify({\"success\": True, \"message\": \"Record permanently deleted from database.\"})\n        else:\n            return jsonify({\"error\": \"Only administrators can permanently delete records.\"}), 403\n    else:\n        # Soft delete the record\n        record[\"deleted\"] = True\n        record[\"deleted_at\"] = datetime.now().isoformat() + \"Z\"\n        record[\"deleted_by\"] = username\n        save_records(records)\n        _log_audit('soft_delete', username, record_id, {'khasra_no': record.get('khasra_no')})\n        return jsonify({\"success\": True, \"message\": \"Record moved to trash.\"})\n\n@records_bp.route(\"/api/records/<record_id>/restore\", methods=[\"POST\"])\n@role_required(\"admin\", \"superadmin\")\ndef restore_record(record_id):\n    \"\"\"Restore a soft-deleted record.\"\"\"\n    records = load_records()\n    record = next((r for r in records if r.get(\"_id\") == record_id), None)\n    if not record: return jsonify({\"error\": \"Not found.\"}), 404\n    record[\"deleted\"] = False\n    record.pop(\"deleted_by\", None)\n    record.pop(\"deleted_at\", None)\n    save_records(records)\n    return jsonify({\"success\": True})\n\nimport io\nimport base64\nfrom fpdf import FPDF\nimport qrcode\nfrom flask import send_file\n\n@records_bp.route(\"/api/records/<ulpin>/card\", methods=[\"POST\"])\n@viewer_or_admin_required\ndef generate_property_card(ulpin):\n    \"\"\"Generate a PDF Property Card for a given ULPIN.\"\"\"\n    records = load_records()\n    record = next((r for r in records if r.get(\"ulpin\") == ulpin), None)\n    if not record:\n        return jsonify({\"error\": \"Record not found.\"}), 404\n        \n    data = request.get_json() or {}\n    map_image_b64 = data.get(\"map_image\")\n    \n    # PDF Configuration\n    pdf = FPDF()\n    pdf.add_page()\n    pdf.set_font(\"helvetica\", \"B\", 18)\n    \n    # Header\n    pdf.set_text_color(234, 88, 12) # Orange-600\n    pdf.cell(0, 10, \"GOVERNMENT OF INDIA\", ln=True, align=\"C\")\n    pdf.set_font(\"helvetica\", \"B\", 14)\n    pdf.set_text_color(31, 41, 55) # Gray-800\n    pdf.cell(0, 8, \"BHOOMI-LIMS PROPERTY CARD\", ln=True, align=\"C\")\n    pdf.ln(5)\n    \n    # Horizontal Line\n    pdf.set_draw_color(209, 213, 219)\n    pdf.line(10, pdf.get_y(), 200, pdf.get_y())\n    pdf.ln(10)\n    \n    # Main Info Grid\n    pdf.set_font(\"helvetica\", \"B\", 10)\n    pdf.set_fill_color(249, 250, 251)\n    \n    col_width = 45\n    def add_info_row(label, value):\n        pdf.set_font(\"helvetica\", \"B\", 9)\n        pdf.set_text_color(107, 114, 128)\n        pdf.cell(col_width, 8, f\"{label}:\", border=0)\n        pdf.set_font(\"helvetica\", \"\", 10)\n        pdf.set_text_color(0, 0, 0)\n        pdf.cell(0, 8, str(value), ln=True)\n\n    add_info_row(\"ULPIN\", record.get(\"ulpin\", \"N/A\"))\n    add_info_row(\"Khasra No\", record.get(\"khasra_no\", \"N/A\"))\n    add_info_row(\"Khata No\", record.get(\"khata_no\", \"N/A\"))\n    add_info_row(\"Area (Hectares)\", f\"{record.get('attributes', {}).get('area_ha', 0):.4f}\")\n    add_info_row(\"Village\", record.get(\"location\", {}).get(\"village\", \"N/A\"))\n    add_info_row(\"District\", record.get(\"location\", {}).get(\"district\", \"N/A\"))\n    add_info_row(\"State\", record.get(\"location\", {}).get(\"state\", \"N/A\"))\n    \n    # Owner Info\n    pdf.ln(5)\n    pdf.set_font(\"helvetica\", \"B\", 11)\n    pdf.cell(0, 10, \"OWNERSHIP DETAILS\", ln=True)\n    pdf.set_font(\"helvetica\", \"\", 10)\n    owner = record.get(\"owner\", {})\n    add_info_row(\"Primary Owner\", owner.get(\"name\", \"N/A\"))\n    add_info_row(\"Share Percentage\", f\"{owner.get('share_pct', 100)}%\")\n    \n    # Map Image\n    if map_image_b64:\n        try:\n            img_data = base64.b64decode(map_image_b64.split(\",\")[1])\n            img_io = io.BytesIO(img_data)\n            # Position at bottom right or below text\n            y_pos = pdf.get_y() + 10\n            if y_pos > 180: # Start new page if no space\n                pdf.add_page()\n                y_pos = 20\n            pdf.image(img_io, x=10, y=y_pos, w=120)\n            pdf.set_y(y_pos + 70)\n        except Exception as e:\n            print(f\"PDF Map Error: {e}\")\n\n    # QR Code for Verification\n    qr_data = f\"https://lims-india.gov.in/verify/{ulpin}\"\n    qr = qrcode.QRCode(version=1, box_size=10, border=1)\n    qr.add_data(qr_data)\n    qr.make(fit=True)\n    qr_img = qr.make_image(fill_color=\"black\", back_color=\"white\")\n    \n    qr_io = io.BytesIO()\n    qr_img.save(qr_io, format=\"PNG\")\n    qr_io.seek(0)\n    \n    # Place QR code in top right\n    pdf.image(qr_io, x=165, y=30, w=30)\n    pdf.set_font(\"helvetica\", \"I\", 7)\n    pdf.set_xy(165, 60)\n    pdf.cell(30, 5, \"Scan to Verify\", align=\"C\")\n\n    # Mutation History Table\n    pdf.set_xy(10, pdf.get_y() + 10)\n    pdf.set_font(\"helvetica\", \"B\", 11)\n    pdf.cell(0, 10, \"MUTATION HISTORY\", ln=True)\n    \n    mutations = record.get(\"mutation_history\", [])\n    if not mutations:\n        pdf.set_font(\"helvetica\", \"I\", 9)\n        pdf.cell(0, 8, \"No prior mutations recorded.\", ln=True)\n    else:\n        pdf.set_font(\"helvetica\", \"B\", 8)\n        pdf.set_fill_color(243, 244, 246)\n        pdf.cell(30, 8, \"Date\", 1, 0, \"C\", True)\n        pdf.cell(30, 8, \"Type\", 1, 0, \"C\", True)\n        pdf.cell(80, 8, \"Previous Owner\", 1, 0, \"C\", True)\n        pdf.cell(50, 8, \"Reference\", 1, 1, \"C\", True)\n        \n        pdf.set_font(\"helvetica\", \"\", 8)\n        for m in mutations:\n            pdf.cell(30, 7, str(m.get(\"mutation_date\", \"N/A\")), 1)\n            pdf.cell(30, 7, str(m.get(\"mutation_type\", \"N/A\")), 1)\n            pdf.cell(80, 7, str(m.get(\"previous_owner\", \"N/A\")), 1)\n            pdf.cell(50, 7, str(m.get(\"mutation_ref\", \"N/A\")), 1, 1)\n\n    # Footer\n    pdf.set_y(-25)\n    pdf.set_font(\"helvetica\", \"I\", 8)\n    pdf.set_text_color(156, 163, 175)\n    pdf.cell(0, 10, f\"Document Generated on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\", align=\"C\")\n    pdf.ln(5)\n    pdf.cell(0, 10, \"This is a computer-generated document and does not require a physical signature.\", align=\"C\")\n\n    # Return PDF\n    pdf_output = pdf.output()\n    return send_file(\n        io.BytesIO(pdf_output),\n        mimetype=\"application/pdf\",\n        as_attachment=True,\n        download_name=f\"Property_Card_{ulpin}.pdf\"\n    )\n"
  },
  {
    "path": "routes/users.py",
    "content": "import uuid\nfrom datetime import datetime\nfrom flask import Blueprint, request, jsonify, session\nfrom werkzeug.security import generate_password_hash, check_password_hash\nfrom core import load_users, save_users, admin_required, viewer_or_admin_required\n\nusers_bp = Blueprint('users', __name__)\n\ndef _get_current_user(users):\n    current_username = session.get(\"username\", \"\")\n    return next((u for u in users if u.get(\"username\") == current_username), None)\n\n@users_bp.route(\"/api/profile\", methods=[\"GET\"])\n@viewer_or_admin_required\ndef get_profile():\n    users = load_users()\n    user = _get_current_user(users)\n    if not user:\n        return jsonify({\"error\": \"Profile not found.\"}), 404\n    profile = {k: v for k, v in user.items() if k != \"password_hash\"}\n    return jsonify(profile)\n\n@users_bp.route(\"/api/profile\", methods=[\"PUT\"])\n@viewer_or_admin_required\ndef update_profile():\n    data = request.get_json() or {}\n    users = load_users()\n    user = _get_current_user(users)\n    if not user:\n        return jsonify({\"error\": \"Profile not found.\"}), 404\n    \n    allowed_fields = [\"full_name\", \"email\", \"phone\", \"designation\", \"department\", \"office_location\"]\n    for field in allowed_fields:\n        if field in data:\n            user[field] = data[field]\n    \n    if data.get(\"current_password\") and data.get(\"new_password\"):\n        if not check_password_hash(user.get(\"password_hash\", \"\"), data[\"current_password\"]):\n            return jsonify({\"error\": \"Current password is incorrect.\"}), 403\n        user[\"password_hash\"] = generate_password_hash(data[\"new_password\"])\n    \n    save_users(users)\n    profile = {k: v for k, v in user.items() if k != \"password_hash\"}\n    return jsonify({\"success\": True, \"profile\": profile})\n\n@users_bp.route(\"/api/users\", methods=[\"GET\"])\n@admin_required\ndef list_users():\n    \"\"\"List all users (admin only). Recovery accounts are hidden.\"\"\"\n    users = load_users()\n    # Ghost Mode: Recovery accounts are hidden from standard admins\n    current_user = _get_current_user(users)\n    is_rec = current_user.get(\"is_recovery\") if current_user else False\n    \n    result = [{k: v for k, v in u.items() if k != \"password_hash\"} \n              for u in users if is_rec or not u.get(\"is_recovery\")]\n    return jsonify(result)\n\n@users_bp.route(\"/api/users\", methods=[\"POST\"])\n@admin_required\ndef create_user():\n    data = request.get_json() or {}\n    role = (data.get(\"role\") or \"officer\").strip().lower()\n    current_role = (session.get(\"role\") or \"\").lower()\n    \n    users = load_users()\n    current_user = _get_current_user(users)\n\n    # 1. SECURITY CHECK FIRST\n    if role == \"superadmin\" and current_role != \"superadmin\":\n        return jsonify({\"error\": \"Unauthorized. Only SuperAdmins can create other SuperAdmins.\"}), 403\n    \n    # Define role hierarchy\n    if current_role == \"superadmin\":\n        valid_roles = [\"superadmin\", \"admin\", \"officer\", \"viewer\"]\n    elif current_role == \"admin\":\n        valid_roles = [\"admin\", \"officer\", \"viewer\"]\n    else:\n        valid_roles = []\n\n    if role not in valid_roles:\n        return jsonify({\n            \"error\": f\"Unauthorized role assignment. Your current role '{current_role}' is not permitted to create a '{role}' account.\",\n            \"allowed_roles_for_you\": valid_roles\n        }), 403\n\n    # 2. VALIDATION CHECKS\n    required = [\"username\", \"password\", \"full_name\"]\n    missing = [f for f in required if not data.get(f)]\n    if missing:\n        return jsonify({\"error\": f\"Missing: {', '.join(missing)}\"}), 400\n    \n    if any(u.get(\"username\") == data[\"username\"] for u in users):\n        return jsonify({\"error\": \"Username exists.\"}), 409\n    \n    # 3. EXECUTION\n    new_user = {\n        \"user_id\": str(uuid.uuid4()),\n        \"username\": data[\"username\"],\n        \"password_hash\": generate_password_hash(data[\"password\"]),\n        \"role\": role,\n        \"full_name\": data[\"full_name\"],\n        \"email\": data.get(\"email\", \"\"),\n        \"phone\": data.get(\"phone\", \"\"),\n        \"designation\": data.get(\"designation\", \"\"),\n        \"department\": data.get(\"department\", \"\"),\n        \"office_location\": data.get(\"office_location\", \"\"),\n        \"is_active\": data.get(\"is_active\", True),\n        \"is_recovery\": data.get(\"is_recovery\", False) if current_user and current_user.get(\"is_recovery\") else False,\n        \"created_at\": datetime.now().isoformat() + \"Z\",\n        \"last_login\": None\n    }\n    users.append(new_user)\n    save_users(users)\n    return jsonify({\"success\": True, \"user\": {k:v for k,v in new_user.items() if k != \"password_hash\"}}), 201\n\n@users_bp.route(\"/api/users/<user_id>\", methods=[\"GET\"])\n@admin_required\ndef get_user(user_id):\n    \"\"\"Get a specific user's profile (admin only). Recovery accounts are invisible.\"\"\"\n    users = load_users()\n    user = next((u for u in users if u.get(\"user_id\") == user_id), None)\n    # Hide the existence of recovery accounts from standard admins\n    current_user = _get_current_user(users)\n    is_rec = current_user.get(\"is_recovery\") if current_user else False\n    \n    if not user or (user.get(\"is_recovery\") and not is_rec):\n        return jsonify({\"error\": \"Not found.\"}), 404\n    return jsonify({k: v for k, v in user.items() if k != \"password_hash\"})\n\n@users_bp.route(\"/api/users/<user_id>\", methods=[\"PUT\"])\n@admin_required\ndef update_user(user_id):\n    \"\"\"Update any user's profile (admin only).\"\"\"\n    data = request.get_json() or {}\n    users = load_users()\n    target_user = next((u for u in users if u.get(\"user_id\") == user_id), None)\n    \n    # Standard admins can't even \"see\" that a recovery account exists to update it\n    current_user = _get_current_user(users)\n    is_rec = current_user.get(\"is_recovery\") if current_user else False\n    \n    if not target_user or (target_user.get(\"is_recovery\") and not is_rec):\n        return jsonify({\"error\": \"Not found.\"}), 404\n\n    current_user = _get_current_user(users)\n    current_role = (session.get(\"role\") or \"\").lower()\n\n    # 1. SECURITY CHECK\n    target_role = target_user.get(\"role\", \"\").lower()\n    \n    # Only superadmins can modify other superadmins\n    if target_role == \"superadmin\" and current_role != \"superadmin\":\n        return jsonify({\"error\": \"Unauthorized. Only SuperAdmins can modify SuperAdmin accounts.\"}), 403\n        \n    # Admins cannot modify superadmins, but can modify anyone else\n    if current_role == \"admin\" and target_role == \"superadmin\":\n         return jsonify({\"error\": \"Unauthorized. Admins cannot modify SuperAdmin accounts.\"}), 403\n\n    if \"role\" in data:\n        new_role = data[\"role\"].lower()\n        if current_role == \"superadmin\":\n            valid_roles = [\"superadmin\", \"admin\", \"officer\", \"viewer\"]\n        elif current_role == \"admin\":\n            valid_roles = [\"admin\", \"officer\", \"viewer\"]\n        else:\n            valid_roles = []\n\n        if new_role not in valid_roles:\n            return jsonify({\"error\": f\"Invalid role assignment: '{new_role}' is not allowed for your role.\"}), 403\n        target_user[\"role\"] = new_role\n\n    # 2. EXECUTION\n    allowed_fields = [\"full_name\", \"email\", \"phone\", \"designation\", \"department\", \"office_location\", \"is_active\"]\n    for field in allowed_fields:\n        if field in data:\n            target_user[field] = data[field]\n    \n    if \"is_recovery\" in data and current_user and current_user.get(\"is_recovery\"):\n        target_user[\"is_recovery\"] = data[\"is_recovery\"]\n    \n    if data.get(\"new_password\"):\n        target_user[\"password_hash\"] = generate_password_hash(data[\"new_password\"])\n    \n    save_users(users)\n    return jsonify({\"success\": True, \"user\": {k:v for k,v in target_user.items() if k != \"password_hash\"}})\n\n@users_bp.route(\"/api/users/<user_id>\", methods=[\"DELETE\"])\n@admin_required\ndef delete_user(user_id):\n    \"\"\"Delete a user (admin only).\"\"\"\n    users = load_users()\n    target_user = next((u for u in users if u.get(\"user_id\") == user_id), None)\n    \n    # Hide the existence of recovery accounts from standard admins\n    current_user = _get_current_user(users)\n    is_rec = current_user.get(\"is_recovery\") if current_user else False\n    \n    if not target_user or (target_user.get(\"is_recovery\") and not is_rec):\n        return jsonify({\"error\": \"Not found.\"}), 404\n\n    current_user = _get_current_user(users)\n    current_role = (session.get(\"role\") or \"\").lower()\n\n    if target_user.get(\"username\") == session.get(\"username\"):\n        return jsonify({\"error\": \"Cannot delete self.\"}), 403\n\n    target_role = target_user.get(\"role\", \"\").lower()\n    if target_role == \"superadmin\":\n        if current_role != \"superadmin\":\n            return jsonify({\"error\": \"Unauthorized. Only SuperAdmins can delete other SuperAdmins.\"}), 403\n    \n    if current_role != \"superadmin\" and target_role == \"admin\":\n        return jsonify({\"error\": \"Unauthorized to delete admins.\"}), 403\n    \n    # 2. EXECUTION\n    users = [u for u in users if u.get(\"user_id\") != user_id]\n    save_users(users)\n    return jsonify({\"success\": True})\n"
  },
  {
    "path": "routes/utils.py",
    "content": "from flask import Blueprint, jsonify\nfrom config import LAND_USE_OPTIONS, LAND_USE_COLORS, MUTATION_TYPES\n\nutils_bp = Blueprint('utils', __name__)\n\n@utils_bp.route(\"/api/config\", methods=[\"GET\"])\ndef app_config():\n    return jsonify({\n        \"land_use_options\": LAND_USE_OPTIONS,\n        \"land_use_colors\": LAND_USE_COLORS,\n        \"mutation_types\": MUTATION_TYPES\n    })\n"
  },
  {
    "path": "run_server.py",
    "content": "\nfrom app import create_app\nimport os\n\nif __name__ == '__main__':\n    app = create_app()\n    # Disable debug mode and reloader to avoid issues in background\n    app.run(host='127.0.0.1', port=5000, debug=False, use_reloader=False)\n"
  },
  {
    "path": "scripts/inspect_recovery.py",
    "content": "#!/usr/bin/env python3\r\n\"\"\"Inspect superadmin and recovery accounts in the database.\"\"\"\r\nfrom dotenv import load_dotenv\r\nimport os\r\nload_dotenv()\r\nfrom pymongo import MongoClient\r\ntry:\r\n    from config import MONGO_URI\r\nexcept Exception:\r\n    MONGO_URI = os.environ.get('MONGO_URI')\r\n\r\ndef main():\r\n    uri = MONGO_URI or os.environ.get('MONGO_URI')\r\n    if not uri:\r\n        raise SystemExit('MONGO_URI not found in config or environment')\r\n    client = MongoClient(uri)\r\n    db = client.get_database('indialims')\r\n    users = list(db.users.find({}, {'password_hash': 0}))\r\n    print('Total users:', len(users))\r\n    print('\\nSuperadmin users:')\r\n    for u in users:\r\n        if u.get('role') == 'superadmin':\r\n            print('-', u.get('username'), '| is_recovery=', u.get('is_recovery', False))\r\n\r\n    print('\\nRecovery-flagged users:')\r\n    for u in users:\r\n        if u.get('is_recovery'):\r\n            print('-', u.get('username'), '| role=', u.get('role'))\r\n\r\nif __name__ == '__main__':\r\n    main()\r\n"
  },
  {
    "path": "scripts/recover_superadmin_auto.py",
    "content": "#!/usr/bin/env python3\r\n\"\"\"Server-only recovery script to upsert or reset a superadmin account.\r\n\r\nRun this from the server/host with access to the MongoDB instance (SSH).\r\nIt prints the generated password once — copy it securely and rotate after first login.\r\nSupports `--dry-run` to show actions without modifying the database.\r\n\"\"\"\r\nimport secrets\r\nimport argparse\r\nimport os\r\nfrom datetime import datetime\r\nfrom dotenv import load_dotenv\r\n\r\n# Load .env into environment so MONGO_URI is available when running the script\r\nload_dotenv()\r\n\r\n\r\ndef random_password(length=20):\r\n    alphabet = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+\"\r\n    return ''.join(secrets.choice(alphabet) for _ in range(length))\r\n\r\n\r\ndef upsert_superadmin(username=None, password=None, dry_run=False):\r\n    now = datetime.utcnow().isoformat() + \"Z\"\r\n    username = username or f\"recovery_sa_{now.replace(':','').replace('-','').replace('T','_')}\"\r\n    password = password or random_password()\r\n\r\n    if dry_run:\r\n        print(\"[DRY-RUN] Would ensure a superadmin exists or be updated with the following:\")\r\n        print(\"USERNAME:\", username)\r\n        print(\"PASSWORD:\", password)\r\n        print(\"[DRY-RUN] No database connections or writes were performed.\")\r\n        return username, password, False\r\n\r\n    # Perform actual DB operations\r\n    # Import heavy dependencies only when performing real DB operations so\r\n    # `--dry-run` remains usable in minimal environments.\r\n    from werkzeug.security import generate_password_hash\r\n    from pymongo import MongoClient\r\n\r\n    # Load MONGO_URI from project config if available, otherwise from env\r\n    try:\r\n        from config import MONGO_URI\r\n    except Exception:\r\n        MONGO_URI = os.environ.get(\"MONGO_URI\")\r\n    if not MONGO_URI:\r\n        raise RuntimeError(\"MONGO_URI is not set in config.py or environment.\")\r\n\r\n    client = MongoClient(MONGO_URI)\r\n    db = client.get_database(\"indialims\")\r\n    users = db.users\r\n\r\n    sa = users.find_one({\"role\": \"superadmin\"})\r\n    if sa:\r\n        users.update_one({\"_id\": sa[\"_id\"]}, {\"$set\": {\r\n            \"username\": username,\r\n            \"password_hash\": generate_password_hash(password),\r\n            \"is_active\": True,\r\n            \"last_login\": now,\r\n            \"is_recovery\": True\r\n        }})\r\n        created = False\r\n    else:\r\n        users.insert_one({\r\n            \"user_id\": f\"recovery-sa-{now}\",\r\n            \"username\": username,\r\n            \"password_hash\": generate_password_hash(password),\r\n            \"role\": \"superadmin\",\r\n            \"full_name\": \"Recovery SuperAdmin\",\r\n            \"email\": \"\",\r\n            \"phone\": \"\",\r\n            \"is_active\": True,\r\n            \"is_recovery\": True,\r\n            \"created_at\": now,\r\n            \"last_login\": now\r\n        })\r\n        created = True\r\n    print(\"SuperAdmin created\" if created else \"SuperAdmin updated\")\r\n    print(\"USERNAME:\", username)\r\n    print(\"PASSWORD:\", password)\r\n    print(\"IMPORTANT: copy the password now and rotate on first login.\")\r\n    return username, password, created\r\n\r\n\r\nif __name__ == \"__main__\":\r\n    parser = argparse.ArgumentParser(description=\"Upsert/reset superadmin (server-only).\")\r\n    parser.add_argument(\"username\", nargs=\"?\", help=\"Optional username to set\")\r\n    parser.add_argument(\"password\", nargs=\"?\", help=\"Optional password to set\")\r\n    parser.add_argument(\"--dry-run\", action=\"store_true\", help=\"Show actions without writing to DB\")\r\n    parser.add_argument(\"--dump\", action=\"store_true\", help=\"Dump credentials to a file in the scripts folder\")\r\n    parser.add_argument(\"--dump-file\", help=\"Optional path for dumped credentials (overrides default)\")\r\n    args = parser.parse_args()\r\n\r\n    username, password, created = upsert_superadmin(args.username, args.password, dry_run=args.dry_run)\r\n\r\n    # If requested, write the last generated credentials to a file for operator convenience.\r\n    # Note: `upsert_superadmin` prints credentials; to capture them here we re-run a quiet\r\n    # generation when --dump is requested but do not modify DB again.\r\n    if args.dump and not args.dry_run:\r\n        # Use the exact credentials returned from the upsert operation\r\n        # (so the dump matches what was written to the DB)\r\n        dump_username = username\r\n        dump_password = password\r\n\r\n        # If user provided --dump-file use it; otherwise place in scripts folder\r\n        dump_path = args.dump_file\r\n        if not dump_path:\r\n            scripts_dir = os.path.dirname(__file__)\r\n            safe_now = datetime.utcnow().isoformat().replace(':', '').replace('.', '_')\r\n            dump_path = os.path.join(scripts_dir, f\"recovery_credentials_{safe_now}.txt\")\r\n\r\n        with open(dump_path, 'w', encoding='utf-8') as f:\r\n            f.write(f\"# Recovery credentials generated: {datetime.utcnow().isoformat()}Z\\n\")\r\n            f.write(f\"USERNAME: {dump_username}\\n\")\r\n            f.write(f\"PASSWORD: {dump_password}\\n\")\r\n            f.write(\"# IMPORTANT: rotate this password immediately after first login.\\n\")\r\n\r\n        print(f\"Credentials dumped to: {dump_path}\")\r\n"
  },
  {
    "path": "scripts/recovery_credentials_2026-04-25T050759_400298.txt",
    "content": "SuperAdmin updated\r\nUSERNAME: recovery_sa_20260425_154817.302101Z\r\nPASSWORD: WwI9v*V_J=MpH_SHi3+^\r\nIMPORTANT: copy the password now and rotate on first login. "
  },
  {
    "path": "static/css/style.css",
    "content": "/*\n * style.css - Custom Styling for India LIMS\n * Tailwind CSS is loaded via CDN; this file adds Leaflet & custom overrides.\n */\n\n/* ─── Base Overrides ──────────────────────────────────────────────────────── */\n* {\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;\n    margin: 0;\n    padding: 0;\n}\n\n/* ─── Leaflet Map Overrides ───────────────────────────────────────────────── */\n#map,\n#add-record-map,\n#view-record-map {\n    width: 100% !important;\n    height: 100% !important;\n    z-index: 1;\n}\n\n/* Ensure Leaflet controls don't conflict with our UI */\n.leaflet-control-container {\n    z-index: 400;\n}\n\n.leaflet-control-zoom {\n    border: none !important;\n    box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;\n    border-radius: 8px !important;\n    overflow: hidden;\n}\n\n.leaflet-control-zoom a {\n    width: 36px !important;\n    height: 36px !important;\n    line-height: 36px !important;\n    font-size: 18px !important;\n    color: #374151 !important;\n    background: white !important;\n    border-bottom: 1px solid #e5e7eb !important;\n}\n\n.leaflet-control-zoom a:hover {\n    background: #f3f4f6 !important;\n    color: #ea580c !important;\n}\n\n/* ─── Leaflet-Geoman Toolbar Styling ──────────────────────────────────────── */\n.leaflet-pm-toolbar {\n    z-index: 500 !important;\n}\n\n.pm-toolbar {\n    border-radius: 8px !important;\n    box-shadow: 0 2px 10px rgba(0,0,0,0.15) !important;\n    border: none !important;\n    overflow: hidden;\n}\n\n.pm-toolbar .button-container .pm-btn {\n    width: 40px !important;\n    height: 40px !important;\n    background: white !important;\n    border: none !important;\n    border-bottom: 1px solid #e5e7eb !important;\n    transition: background 0.15s;\n}\n\n.pm-toolbar .button-container .pm-btn:hover {\n    background: #fff7ed !important;\n}\n\n.pm-toolbar .button-container .pm-btn.active {\n    background: #fed7aa !important;\n    color: #ea580c !important;\n}\n\n.pm-toolbar .button-container:last-child .pm-btn {\n    border-bottom: none !important;\n}\n\n/* ─── Map Popup Styling ───────────────────────────────────────────────────── */\n.leaflet-popup-content-wrapper {\n    border-radius: 10px !important;\n    box-shadow: 0 4px 20px rgba(0,0,0,0.12) !important;\n    padding: 0 !important;\n}\n\n.leaflet-popup-content {\n    margin: 0 !important;\n    font-family: 'Segoe UI', system-ui, sans-serif;\n    font-size: 13px;\n    min-width: 220px;\n}\n\n.leaflet-popup-tip {\n    box-shadow: 0 4px 20px rgba(0,0,0,0.08) !important;\n}\n\n/* ─── Parcel Tooltip Styling ──────────────────────────────────────────────── */\n.parcel-tooltip {\n    background: white !important;\n    border: none !important;\n    border-radius: 10px !important;\n    box-shadow: 0 6px 20px rgba(0,0,0,0.15) !important;\n    padding: 0 !important;\n    font-family: 'Segoe UI', system-ui, sans-serif !important;\n    font-size: 13px !important;\n    color: #374151 !important;\n    max-width: 300px !important;\n    min-width: 250px !important;\n}\n\n.parcel-tooltip::before {\n    border-top-color: white !important;\n}\n\n.parcel-tooltip .tooltip-content {\n    padding: 14px 16px;\n}\n\n.parcel-tooltip .tooltip-title {\n    font-size: 15px;\n    font-weight: 700;\n    color: #1f2937;\n    margin-bottom: 10px;\n    border-bottom: 2px solid #ea580c;\n    padding-bottom: 8px;\n}\n\n.parcel-tooltip .tooltip-row {\n    display: flex;\n    justify-content: space-between;\n    margin-bottom: 6px;\n    line-height: 1.6;\n}\n\n.parcel-tooltip .tooltip-label {\n    font-weight: 600;\n    color: #6b7280;\n    margin-right: 10px;\n    min-width: 80px;\n}\n\n.parcel-tooltip .tooltip-value {\n    font-weight: 500;\n    color: #1f2937;\n    text-align: right;\n}\n\n.parcel-tooltip .tooltip-divider {\n    height: 1px;\n    background: #e5e7eb;\n    margin: 10px 0;\n}\n\n.parcel-tooltip .tooltip-hint {\n    margin-top: 10px;\n    padding-top: 8px;\n    border-top: 1px solid #e5e7eb;\n    font-size: 12px;\n    color: #ea580c;\n    font-weight: 600;\n    text-align: center;\n}\n\n/* ─── Custom Parcel Styling ───────────────────────────────────────────────── */\n.parcel-polygon {\n    stroke: #ea580c;\n    stroke-width: 2.5;\n    fill-opacity: 0.2;\n    stroke-opacity: 0.9;\n}\n\n.parcel-polygon:hover {\n    fill-opacity: 0.35;\n    stroke-width: 3.5;\n}\n\n.parcel-polygon-selected {\n    stroke: #dc2626;\n    stroke-width: 3.5;\n    fill-opacity: 0.35;\n    stroke-opacity: 1;\n}\n\n/* Land use color coding */\n.parcel-agricultural { fill: #22c55e; }\n.parcel-residential { fill: #3b82f6; }\n.parcel-commercial { fill: #f59e0b; }\n.parcel-industrial { fill: #8b5cf6; }\n.parcel-government { fill: #ef4444; }\n.parcel-forest { fill: #065f46; }\n.parcel-wasteland { fill: #9ca3af; }\n\n/* ─── Map Layer Switcher ─────────────────────────────────────────────────── */\ninput[type=\"radio\"][name=\"basemap\"],\ninput[type=\"radio\"][name=\"add-basemap\"],\ninput[type=\"radio\"][name=\"view-basemap\"] {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 14px;\n    height: 14px;\n    border: 2px solid #d1d5db;\n    border-radius: 50%;\n    background: white;\n    cursor: pointer;\n    position: relative;\n    flex-shrink: 0;\n}\n\ninput[type=\"radio\"][name=\"basemap\"]:checked,\ninput[type=\"radio\"][name=\"add-basemap\"]:checked,\ninput[type=\"radio\"][name=\"view-basemap\"]:checked {\n    border-color: #ea580c;\n    border-width: 2px;\n}\n\ninput[type=\"radio\"][name=\"basemap\"]:checked::after,\ninput[type=\"radio\"][name=\"add-basemap\"]:checked::after,\ninput[type=\"radio\"][name=\"view-basemap\"]:checked::after {\n    content: '';\n    position: absolute;\n    top: 2px;\n    left: 2px;\n    width: 6px;\n    height: 6px;\n    border-radius: 50%;\n    background: #ea580c;\n}\n\ninput[type=\"radio\"][name=\"basemap\"]:hover,\ninput[type=\"radio\"][name=\"add-basemap\"]:hover,\ninput[type=\"radio\"][name=\"view-basemap\"]:hover {\n    border-color: #ea580c;\n}\n\n/* ─── Public Viewer Map Layer Switcher (green theme) ─────────────────────── */\n.viewer-radio {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 14px;\n    height: 14px;\n    border: 2px solid #d1d5db;\n    border-radius: 50%;\n    background: white;\n    cursor: pointer;\n    position: relative;\n    flex-shrink: 0;\n}\n\n.viewer-radio:checked {\n    border-color: #16a34a;\n    border-width: 2px;\n}\n\n.viewer-radio:checked::after {\n    content: '';\n    position: absolute;\n    top: 2px;\n    left: 2px;\n    width: 6px;\n    height: 6px;\n    border-radius: 50%;\n    background: #16a34a;\n}\n\n.viewer-radio:hover {\n    border-color: #16a34a;\n}\n\n/* ─── Public Viewer Filter Selects (green theme) ────────────────────────── */\n#filter-state:focus,\n#filter-district:focus,\n#filter-village:focus,\n#filter-land-use:focus {\n    --tw-ring-color: #16a34a;\n    border-color: #16a34a;\n}\n\n/* ─── Main Tab Navigation (Left Sidebar) ─────────────────────────────────── */\n.main-tab-btn {\n    width: 100%;\n    border: 0;\n    background: transparent;\n    color: #6b7280;\n    font-size: 0.9rem;\n    font-weight: 500;\n    padding: 0.875rem 1rem;\n    cursor: pointer;\n    transition: all 0.2s;\n    display: flex;\n    align-items: center;\n    gap: 0.75rem;\n    position: relative;\n    border-left: 4px solid transparent;\n    border-radius: 0.5rem;\n    margin-bottom: 0.25rem;\n}\n\n.main-tab-btn:hover {\n    color: #374151;\n    background: #f9fafb;\n}\n\n.main-tab-btn.active {\n    color: #ea580c;\n    background: #fff7ed;\n    border-left-color: #ea580c;\n    font-weight: 600;\n}\n\n.main-tab-btn svg {\n    flex-shrink: 0;\n}\n\n.main-tab-btn span {\n    flex: 1;\n    text-align: left;\n}\n\n/* Logout button styling */\n#btn-logout {\n    margin-top: 0.25rem;\n    border-radius: 0.5rem;\n}\n\n#btn-logout:hover {\n    background: #fef2f2;\n    border-left-color: #dc2626;\n    color: #dc2626;\n}\n\n.main-tab-panel {\n    animation: fadeIn 0.25s ease-in;\n}\n\n/* ─── Sidebar Styling (Legacy Support) ────────────────────────────────────── */\n.sidebar-tab.active {\n    color: #ea580c;\n    border-bottom-color: #ea580c;\n}\n\n.sidebar-tab {\n    transition: color 0.15s, border-color 0.15s;\n    border-bottom: 2px solid transparent;\n    cursor: pointer;\n    background: none;\n    border-top: none;\n    border-left: none;\n    border-right: none;\n}\n\n.sidebar-tab:hover {\n    color: #374151;\n}\n\n/* ─── Form Sub-Tabs ───────────────────────────────────────────────────────── */\n.form-tab-btn {\n    flex: 1;\n    border: 0;\n    background: transparent;\n    color: #6b7280;\n    font-size: 0.875rem;\n    font-weight: 500;\n    padding: 0.75rem 1rem;\n    cursor: pointer;\n    transition: all 0.2s;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 0.5rem;\n    border-bottom: 2px solid transparent;\n}\n\n.form-tab-btn:hover {\n    color: #374151;\n    background: #f3f4f6;\n}\n\n.form-tab-btn.active {\n    color: #ea580c;\n    background: #fff7ed;\n    border-bottom-color: #ea580c;\n}\n\n.form-tab-panel {\n    animation: fadeIn 0.2s ease-in;\n}\n\n/* ─── Draw Settings Panel ─────────────────────────────────────────────────── */\n.draw-settings-btn {\n    border: 0;\n    border-radius: 0.375rem;\n    font-size: 0.75rem;\n    font-weight: 600;\n    padding: 0.5rem 0.75rem;\n    transition: background 0.15s;\n}\n\n#map-autofill-status {\n    min-height: 1rem;\n}\n\n/* ─── Dashboard KPI Cards ─────────────────────────────────────────────────── */\n.dashboard-kpi-card {\n    border: 1px solid #e5e7eb;\n    background: #f9fafb;\n    border-radius: 0.5rem;\n    padding: 0.55rem 0.7rem;\n}\n\n.dashboard-kpi-label {\n    font-size: 0.68rem;\n    color: #6b7280;\n    text-transform: uppercase;\n    letter-spacing: 0.04em;\n}\n\n.dashboard-kpi-value {\n    font-size: 1rem;\n    font-weight: 700;\n    color: #1f2937;\n    margin-top: 0.15rem;\n}\n\n.dashboard-row {\n    border: 1px solid #e5e7eb;\n    border-radius: 0.5rem;\n    background: #ffffff;\n    padding: 0.5rem 0.65rem;\n}\n\n.dashboard-row-bar {\n    height: 0.4rem;\n    background: #ffedd5;\n    border-radius: 9999px;\n    overflow: hidden;\n    margin-top: 0.35rem;\n}\n\n.dashboard-row-bar-fill {\n    height: 100%;\n    background: #ea580c;\n}\n\n/* ─── Dashboard Content Improvements ──────────────────────────────────────── */\n#main-tab-dashboard .bg-white.rounded-lg.shadow-md {\n    transition: box-shadow 0.2s ease;\n}\n\n#main-tab-dashboard .bg-white.rounded-lg.shadow-md:hover {\n    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n}\n\n#dashboard-land-use > div,\n#dashboard-districts > div {\n    transition: transform 0.15s ease;\n}\n\n#dashboard-land-use > div:hover,\n#dashboard-districts > div:hover {\n    transform: translateX(2px);\n}\n\n/* ─── Records List Styling ────────────────────────────────────────────────── */\n.records-header {\n    position: sticky;\n    top: 0;\n    z-index: 5;\n    background: #ffffff;\n    border-bottom: 1px solid #e5e7eb;\n    padding: 0.65rem 0.75rem;\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n}\n\n.records-view-tabs {\n    display: inline-flex;\n    border: 1px solid #e5e7eb;\n    border-radius: 0.5rem;\n    overflow: hidden;\n    margin-bottom: 0;\n    width: auto;\n    min-width: 180px;\n    flex-shrink: 0;\n}\n\n.records-view-tab {\n    border: 0;\n    background: #ffffff;\n    color: #4b5563;\n    font-size: 0.75rem;\n    font-weight: 600;\n    padding: 0.5rem 0.875rem;\n    cursor: pointer;\n    transition: background 0.15s, color 0.15s;\n    flex: 1;\n    white-space: nowrap;\n    min-width: 90px;\n}\n\n.records-view-tab:hover {\n    background: #f3f4f6;\n}\n\n.records-view-tab.active {\n    background: #fff7ed;\n    color: #c2410c;\n}\n\n.record-table-row {\n    border: 1px solid #e5e7eb;\n    border-radius: 0.5rem;\n    background: #ffffff;\n    padding: 0.55rem 0.65rem;\n    margin-bottom: 0.5rem;\n    transition: all 0.15s;\n    cursor: pointer;\n}\n\n.record-table-row:hover {\n    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);\n}\n\n.record-table-row.active {\n    border-color: #fdba74;\n    background: #fff7ed;\n}\n\n.view-record-btn-table {\n    cursor: pointer;\n    transition: all 0.15s;\n    font-weight: 500;\n}\n\n.view-record-btn-table:hover {\n    background: #ea580c !important;\n    transform: translateY(-1px);\n    box-shadow: 0 2px 4px rgba(234, 88, 12, 0.3);\n}\n\n/* ─── Record Card in Sidebar ──────────────────────────────────────────────── */\n.record-card {\n    padding: 12px 16px;\n    cursor: pointer;\n    transition: background 0.15s;\n    border-left: 3px solid transparent;\n    border-radius: 0.5rem;\n    margin-bottom: 0.5rem;\n    background: #ffffff;\n    border: 1px solid #e5e7eb;\n}\n\n.record-card:hover {\n    background: #f9fafb;\n    border-left-color: #ea580c;\n    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);\n}\n\n.record-card.active {\n    background: #fff7ed;\n    border-left-color: #ea580c;\n    box-shadow: 0 2px 8px rgba(234, 88, 12, 0.1);\n}\n\n.view-record-btn {\n    cursor: pointer;\n    transition: all 0.15s;\n}\n\n.view-record-btn:hover {\n    transform: translateY(-1px);\n    box-shadow: 0 2px 4px rgba(234, 88, 12, 0.3);\n}\n\n.record-card .land-use-badge {\n    display: inline-block;\n    padding: 2px 10px;\n    border-radius: 9999px;\n    font-size: 11px;\n    font-weight: 600;\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n}\n\n.badge-agricultural { background: #dcfce7; color: #166534; }\n.badge-residential { background: #dbeafe; color: #1e40af; }\n.badge-commercial { background: #fef3c7; color: #92400e; }\n.badge-industrial { background: #ede9fe; color: #5b21b6; }\n.badge-government { background: #fee2e2; color: #991b1b; }\n.badge-forest { background: #d1fae5; color: #065f46; }\n.badge-wasteland { background: #f3f4f6; color: #4b5563; }\n\n/* Soft-deleted badge */\n.badge-deleted { background: #fee2e2; color: #9f1239; padding: 2px 8px; border-radius: 9999px; font-size: 11px; font-weight: 700; text-transform: none; }\n\n/* Muted appearance for deleted records in lists */\n.record-card.deleted { opacity: 0.65; filter: grayscale(0.08); }\n.record-table-row.deleted { opacity: 0.6; filter: grayscale(0.08); }\n\n/* ─── Form Field Group Styling ────────────────────────────────────────────── */\nfieldset {\n    border-color: #e5e7eb;\n}\n\nfieldset legend {\n    font-size: 0.75rem;\n    font-weight: 600;\n    color: #6b7280;\n    padding: 0 0.5rem;\n}\n\n/* ─── Toast Notification ──────────────────────────────────────────────────── */\n#toast {\n    position: fixed;\n    bottom: 20px;\n    right: 20px;\n    z-index: 10000;\n    transform: translateY(20px);\n    opacity: 0;\n    transition: all 0.3s ease;\n    max-width: 400px;\n    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n}\n\n#toast.show {\n    display: block !important;\n    transform: translateY(0);\n    opacity: 1;\n}\n\n#toast .toast-inner {\n    padding: 12px 20px;\n    border-radius: 8px;\n    color: white;\n    font-size: 14px;\n    font-weight: 500;\n}\n\n#toast.success .toast-inner { background: #166534; }\n#toast.error .toast-inner { background: #991b1b; }\n#toast.info .toast-inner { background: #1e40af; }\n#toast.warning .toast-inner { background: #92400e; }\n\n/* ─── Form Input Improvements ─────────────────────────────────────────────── */\ninput[type=\"text\"],\ninput[type=\"number\"],\ninput[type=\"email\"],\ninput[type=\"date\"],\ninput[type=\"password\"],\nselect,\ntextarea {\n    transition: all 0.2s ease;\n}\n\ninput[type=\"text\"]:focus,\ninput[type=\"number\"]:focus,\ninput[type=\"email\"]:focus,\ninput[type=\"date\"]:focus,\ninput[type=\"password\"]:focus,\nselect:focus,\ntextarea:focus {\n    outline: none;\n}\n\ninput[readonly] {\n    cursor: not-allowed;\n}\n\n/* ─── Login Page Styling ──────────────────────────────────────────────────── */\n#captcha-question {\n    font-variant-numeric: tabular-nums;\n    letter-spacing: 2px;\n    user-select: none;\n}\n\n/* ─── Scrollbar Styling ───────────────────────────────────────────────────── */\n::-webkit-scrollbar {\n    width: 8px;\n}\n\n::-webkit-scrollbar-track {\n    background: #f1f1f1;\n}\n\n::-webkit-scrollbar-thumb {\n    background: #c4c4c4;\n    border-radius: 4px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n    background: #a0a0a0;\n}\n\n/* ─── Loading Spinner ─────────────────────────────────────────────────────── */\n.spinner {\n    display: inline-block;\n    width: 20px;\n    height: 20px;\n    border: 2px solid rgba(255,255,255,0.3);\n    border-radius: 50%;\n    border-top-color: #fff;\n    animation: spin 0.6s linear infinite;\n}\n\n@keyframes spin {\n    to { transform: rotate(360deg); }\n}\n\n/* ─── Print Styles (for Property Cards) ───────────────────────────────────── */\n@media print {\n    body * {\n        visibility: hidden;\n    }\n    #print-area, #print-area * {\n        visibility: visible;\n    }\n    #print-area {\n        position: absolute;\n        left: 0;\n        top: 0;\n    }\n}\n\n/* ─── Responsive Adjustments ──────────────────────────────────────────────── */\n@media (max-width: 1024px) {\n    /* Adjust dashboard grid for tablets */\n    #main-tab-dashboard .grid {\n        gap: 1rem;\n    }\n    \n    /* Reduce padding in records toolbar */\n    .bg-white.border-b.px-4.py-3 {\n        padding-left: 0.75rem;\n        padding-right: 0.75rem;\n    }\n    \n    /* Adjust search input width */\n    #search-input {\n        width: 12rem !important;\n    }\n    \n    /* Hide department column on tablets */\n    #users-table-body td:nth-child(4),\n    #users-table-body th:nth-child(4) {\n        display: none;\n    }\n}\n\n@media (max-width: 1024px) and (min-width: 768px) {\n    /* Make sidebar slightly smaller on tablet only */\n    .w-56 {\n        width: 64px !important;\n    }\n    \n    .main-tab-btn span,\n    #btn-logout span {\n        display: none;\n    }\n\n    .main-tab-btn {\n        padding: 0.75rem;\n        justify-content: center;\n    }\n    \n    #btn-logout {\n        padding: 0.75rem;\n        justify-content: center;\n    }\n}\n\n@media (max-width: 768px) {\n    /* Stack dashboard cards on mobile */\n    #main-tab-dashboard .grid {\n        grid-template-columns: 1fr !important;\n    }\n    \n    /* Hide some filters on mobile */\n    .records-header .records-view-tabs {\n        display: none;\n    }\n    \n    /* Hide contact and department columns on mobile */\n    #users-table-body td:nth-child(3),\n    #users-table-body th:nth-child(3),\n    #users-table-body td:nth-child(4),\n    #users-table-body th:nth-child(4) {\n        display: none;\n    }\n}\n\n@media (max-width: 767px) {\n    /* Ensure map takes full viewport on mobile */\n    #map {\n        height: calc(100vh - 60px) !important;\n    }\n\n    /* Parcel panel on mobile - full width with top border */\n    #parcel-panel {\n        max-width: 100vw !important;\n        border-radius: 0 !important;\n    }\n\n    /* Make filter sidebar touch-friendly */\n    #filter-sidebar {\n        -webkit-overflow-scrolling: touch;\n    }\n\n    /* Larger touch targets for filter inputs */\n    #filter-state,\n    #filter-district,\n    #filter-village,\n    #filter-land-use,\n    #search-input {\n        min-height: 44px;\n        font-size: 16px !important; /* Prevents iOS zoom on focus */\n    }\n\n    #btn-apply-filters,\n    #btn-clear-filters {\n        min-height: 44px;\n    }\n\n    /* Ensure overlay is clickable */\n    #filter-overlay {\n        z-index: 35;\n    }\n}\n"
  },
  {
    "path": "static/data/india-boundary.geojson",
    "content": "{\"type\": \"FeatureCollection\", \"features\": [{\"type\": \"Feature\", \"properties\": {\"name\": \"India\", \"ISO3166-1-Alpha-3\": \"IND\", \"ISO3166-1-Alpha-2\": \"IN\"}, \"geometry\": {\"type\": \"MultiPolygon\", \"coordinates\": [[[[77.800346, 35.495406], [77.815332, 35.47334], [77.834246, 35.452152], [77.857087, 35.436598], [77.883132, 35.431068], [77.912691, 35.44099], [77.957856, 35.482073], [77.985865, 35.494165], [78.044259, 35.491633], [78.055628, 35.452876], [78.038058, 35.398099], [78.009843, 35.347456], [78.001161, 35.268908], [78.036404, 35.194235], [78.130042, 35.055432], [78.13924, 35.018949], [78.148129, 34.94314], [78.162185, 34.908826], [78.202492, 34.865728], [78.211381, 34.848262], [78.273186, 34.658867], [78.296027, 34.624658], [78.335301, 34.594375], [78.379432, 34.578666], [78.607325, 34.546471], [78.664583, 34.526421], [78.750676, 34.471282], [78.801732, 34.415006], [78.839249, 34.396894], [78.885861, 34.385758], [78.922138, 34.372296], [78.95118, 34.349171], [78.95694, 34.339961], [78.976192, 34.309173], [78.98539, 34.286358], [78.988491, 34.263129], [78.985494, 34.239901], [78.976192, 34.217163], [78.903225, 34.158123], [78.806073, 34.122983], [78.730109, 34.079265], [78.721117, 33.994386], [78.770313, 33.872352], [78.787263, 33.808428], [78.794084, 33.74391], [78.781372, 33.552785], [78.800905, 33.494236], [78.824263, 33.461059], [78.915948, 33.387656], [78.917694, 33.386258], [78.964986, 33.344254], [78.993449, 33.330782], [79.023041, 33.31798], [79.053278, 33.307873], [79.075048, 33.295743], [79.098028, 33.287655], [79.125038, 33.290203], [79.150213, 33.292555], [79.260061, 33.291776], [79.340307, 33.275132], [79.420084, 33.275169], [79.456096, 33.250399], [79.423475, 33.157825], [79.37317, 33.11214], [79.381063, 33.061768], [79.399273, 33.026878], [79.339787, 33.013257], [79.333586, 32.994292], [79.378462, 32.978881], [79.414089, 32.972053], [79.471089, 32.908959], [79.564093, 32.793928], [79.613913, 32.765035], [79.620673, 32.728725], [79.57617, 32.670646], [79.531201, 32.648317], [79.515741, 32.60812], [79.472113, 32.565472], [79.443497, 32.534773], [79.40682, 32.529683], [79.379477, 32.57645], [79.320466, 32.592267], [79.287412, 32.532579], [79.235608, 32.504296], [79.215247, 32.507914], [79.191166, 32.50427], [79.1614, 32.496467], [79.132875, 32.48546], [79.112928, 32.472386], [79.102902, 32.449855], [79.09174, 32.390556], [79.075307, 32.37079], [79.066316, 32.370067], [79.039651, 32.375984], [79.028178, 32.377095], [79.014742, 32.373994], [78.989834, 32.364098], [78.976192, 32.361618], [78.943429, 32.346373], [78.911286, 32.354745], [78.881521, 32.376268], [78.855682, 32.400452], [78.77145, 32.461663], [78.754707, 32.493806], [78.73693, 32.545792], [78.734966, 32.560649], [78.740651, 32.578684], [78.749126, 32.595867], [78.750986, 32.612791], [78.736827, 32.629844], [78.713262, 32.637518], [78.694452, 32.629663], [78.664893, 32.599019], [78.647736, 32.585971], [78.630166, 32.578013], [78.589859, 32.569925], [78.447542, 32.566256], [78.408165, 32.558376], [78.384807, 32.547549], [78.380983, 32.528041], [78.409198, 32.476623], [78.448782, 32.426782], [78.452193, 32.416782], [78.448162, 32.410658], [78.441444, 32.404948], [78.43638, 32.396163], [78.434519, 32.383761], [78.435966, 32.377973], [78.43886, 32.373296], [78.462735, 32.281803], [78.461391, 32.260151], [78.456327, 32.242116], [78.457981, 32.229662], [78.468541, 32.226668], [78.469017, 32.226533], [78.476481, 32.224417], [78.513068, 32.20757], [78.534978, 32.181344], [78.551825, 32.15039], [78.649287, 32.036444], [78.678329, 32.016755], [78.722151, 31.994586], [78.733829, 31.984664], [78.744578, 31.9642], [78.741994, 31.945338], [78.720187, 31.902912], [78.714192, 31.883016], [78.706648, 31.840797], [78.700343, 31.821676], [78.674091, 31.78695], [78.670887, 31.770517], [78.695589, 31.746694], [78.695175, 31.737961], [78.692592, 31.7284], [78.693212, 31.71853], [78.698069, 31.711037], [78.710472, 31.69817], [78.71843, 31.6806], [78.724011, 31.673313], [78.731349, 31.668042], [78.739824, 31.666027], [78.758117, 31.666595], [78.795324, 31.633781], [78.818889, 31.607374], [78.814031, 31.595023], [78.796978, 31.578849], [78.699516, 31.510016], [78.695382, 31.488105], [78.733003, 31.467744], [78.762561, 31.44511], [78.760081, 31.412037], [78.745508, 31.373538], [78.73848, 31.334988], [78.740857, 31.317728], [78.745095, 31.308116], [78.753156, 31.301966], [78.784472, 31.288272], [78.796048, 31.288117], [78.821989, 31.293698], [78.848654, 31.290908], [78.861987, 31.291476], [78.886972, 31.284171], [78.921008, 31.269685], [78.940282, 31.301576], [78.959552, 31.325117], [78.988801, 31.343256], [79.015529, 31.383397], [79.049937, 31.422953], [79.07021, 31.461381], [79.07799, 31.461664], [79.131397, 31.438433], [79.157693, 31.414926], [79.179218, 31.393796], [79.192442, 31.369789], [79.207628, 31.351373], [79.238319, 31.329656], [79.239239, 31.311724], [79.25411, 31.299006], [79.262024, 31.278513], [79.24843, 31.256704], [79.25286, 31.245437], [79.278316, 31.240044], [79.288321, 31.225474], [79.298078, 31.203242], [79.303209, 31.183104], [79.302162, 31.16345], [79.319417, 31.14228], [79.367048, 31.111559], [79.372343, 31.094072], [79.382593, 31.078335], [79.388363, 31.069474], [79.394978, 31.036711], [79.401592, 31.023637], [79.414408, 31.020382], [79.430738, 31.023172], [79.446344, 31.023792], [79.46164, 31.021105], [79.47642, 31.014025], [79.480761, 31.010046], [79.484275, 31.005447], [79.486858, 31.000228], [79.488202, 30.994492], [79.497607, 30.981211], [79.512697, 30.966173], [79.529646, 30.953564], [79.544219, 30.947414], [79.565923, 30.942454], [79.577809, 30.938371], [79.589488, 30.940231], [79.630312, 30.962504], [79.648192, 30.96576], [79.6885, 30.967413], [79.739039, 30.979247], [79.76064, 30.977232], [79.833504, 30.961522], [79.85035, 30.954494], [79.862856, 30.941523], [79.880426, 30.912843], [79.903164, 30.890209], [79.914635, 30.883819], [79.91937, 30.881182], [79.934893, 30.872535], [80.005586, 30.847266], [80.028634, 30.830781], [80.044447, 30.8067], [80.062534, 30.784789], [80.092196, 30.774505], [80.109249, 30.778122], [80.140669, 30.793212], [80.157102, 30.79316], [80.169297, 30.785254], [80.196066, 30.748098], [80.224591, 30.734094], [80.231412, 30.724947], [80.207021, 30.686138], [80.19989, 30.680712], [80.191725, 30.679679], [80.18201, 30.680041], [80.172915, 30.679162], [80.16661, 30.674408], [80.16692, 30.661902], [80.177255, 30.649862], [80.190588, 30.637976], [80.19989, 30.625884], [80.2033, 30.60635], [80.197823, 30.591364], [80.188418, 30.576998], [80.179839, 30.559479], [80.21622, 30.566818], [80.252806, 30.565009], [80.325877, 30.546509], [80.424992, 30.497829], [80.475429, 30.480466], [80.508295, 30.462328], [80.525141, 30.458607], [80.545088, 30.461036], [80.559971, 30.465118], [80.575267, 30.466255], [80.596351, 30.459796], [80.694743, 30.411737], [80.723062, 30.392048], [80.735361, 30.378199], [80.754998, 30.345797], [80.767194, 30.331431], [80.781663, 30.320993], [80.866981, 30.288488], [80.943772, 30.270143], [80.976018, 30.255209], [80.996275, 30.22689], [81.003303, 30.212731], [80.996017, 30.196969], [80.988162, 30.196556], [80.920827, 30.17666], [80.903309, 30.180433], [80.893647, 30.198104], [80.883879, 30.210405], [80.883684, 30.21028], [80.867911, 30.200173], [80.850393, 30.181931], [80.83644, 30.170046], [80.849669, 30.143381], [80.829825, 30.117129], [80.769674, 30.077287], [80.755721, 30.064574], [80.725749, 30.022768], [80.715827, 30.013311], [80.680171, 29.992072], [80.654126, 29.970575], [80.641413, 29.963444], [80.622293, 29.958173], [80.586223, 29.954142], [80.57134, 29.946855], [80.562865, 29.929699], [80.549533, 29.89368], [80.527312, 29.862416], [80.476152, 29.80614], [80.454861, 29.790586], [80.395227, 29.776582], [80.368975, 29.757926], [80.354299, 29.730279], [80.354402, 29.70488], [80.363911, 29.679714], [80.377553, 29.652946], [80.386752, 29.626823], [80.385201, 29.604835], [80.373316, 29.58419], [80.350785, 29.562073], [80.345101, 29.558352], [80.332698, 29.552461], [80.327221, 29.548585], [80.323603, 29.54166], [80.32505, 29.535511], [80.327634, 29.52993], [80.327221, 29.52484], [80.320089, 29.515822], [80.311511, 29.508122], [80.29146, 29.494738], [80.29115, 29.494609], [80.282055, 29.4843], [80.273064, 29.478667], [80.266242, 29.47213], [80.263452, 29.459237], [80.257457, 29.450064], [80.228312, 29.441718], [80.217666, 29.434639], [80.213739, 29.416888], [80.220974, 29.400119], [80.242161, 29.367408], [80.249499, 29.326997], [80.254977, 29.316635], [80.263555, 29.315318], [80.272754, 29.315705], [80.280092, 29.31015], [80.282779, 29.291314], [80.278748, 29.268085], [80.258181, 29.202456], [80.248672, 29.204394], [80.236063, 29.213076], [80.218803, 29.211086], [80.213739, 29.196565], [80.230482, 29.154733], [80.233066, 29.139308], [80.220664, 29.126079], [80.20144, 29.121169], [80.180563, 29.121324], [80.16351, 29.123366], [80.132607, 29.110214], [80.113073, 29.072206], [80.104702, 29.027609], [80.107906, 28.994769], [80.107906, 28.994588], [80.099431, 28.977276], [80.085168, 28.967484], [80.068425, 28.959939], [80.052922, 28.949113], [80.04052, 28.932809], [80.033905, 28.915936], [80.031115, 28.897798], [80.030288, 28.87767], [80.036386, 28.837026], [80.054782, 28.824185], [80.073054, 28.820925], [80.081861, 28.819353], [80.11421, 28.802584], [80.147593, 28.76331], [80.162063, 28.753285], [80.181183, 28.74729], [80.216426, 28.741942], [80.233273, 28.732718], [80.238957, 28.726258], [80.249396, 28.710213], [80.257044, 28.702745], [80.265209, 28.699283], [80.283399, 28.695356], [80.291047, 28.689671], [80.318332, 28.640113], [80.329701, 28.627479], [80.349751, 28.620218], [80.369078, 28.622647], [80.388302, 28.627246], [80.408352, 28.626342], [80.426543, 28.616807], [80.468814, 28.571849], [80.488451, 28.562444], [80.493412, 28.575647], [80.484937, 28.639287], [80.487624, 28.656469], [80.497546, 28.670137], [80.517596, 28.680033], [80.534444, 28.679555], [80.555837, 28.678948], [80.559041, 28.673031], [80.557387, 28.66435], [80.556871, 28.655022], [80.563589, 28.647038], [80.581365, 28.638951], [80.598522, 28.633783], [80.635625, 28.627866], [80.648338, 28.619753], [80.668182, 28.586344], [80.67862, 28.574717], [80.695777, 28.567508], [80.727093, 28.559679], [80.743526, 28.549964], [80.781663, 28.514746], [80.798406, 28.505703], [80.81701, 28.502551], [80.857989, 28.502447], [80.872252, 28.496866], [80.880365, 28.48188], [80.881864, 28.466842], [80.88667, 28.452786], [80.905273, 28.440849], [80.921344, 28.436198], [80.941188, 28.432684], [80.960515, 28.432271], [80.976018, 28.436922], [80.9878, 28.430979], [80.991314, 28.420256], [80.993175, 28.407828], [81.000409, 28.397002], [81.047066, 28.389088], [81.146344, 28.372249], [81.169701, 28.361319], [81.190372, 28.338116], [81.210732, 28.278637], [81.224323, 28.250783], [81.282356, 28.191588], [81.295947, 28.167455], [81.280754, 28.161977], [81.276775, 28.153787], [81.283079, 28.146087], [81.299409, 28.142521], [81.296205, 28.12831], [81.307057, 28.123814], [81.323697, 28.126243], [81.338115, 28.132728], [81.347262, 28.144433], [81.351447, 28.15681], [81.357907, 28.165982], [81.373203, 28.167843], [81.396044, 28.160634], [81.417335, 28.147017], [81.435267, 28.129783], [81.448237, 28.111412], [81.45294, 28.097046], [81.454025, 28.086607], [81.458211, 28.07728], [81.473455, 28.066402], [81.561925, 28.025991], [81.582183, 28.013278], [81.595102, 27.994881], [81.59536, 27.994778], [81.615049, 27.981239], [81.665433, 27.9708], [81.688946, 27.963204], [81.710392, 27.94752], [81.750131, 27.909667], [81.800154, 27.884087], [81.827852, 27.865639], [81.855654, 27.850937], [81.883198, 27.849051], [81.906091, 27.863107], [81.946398, 27.905352], [81.975905, 27.916953], [82.02722, 27.912406], [82.050212, 27.905586], [82.051611, 27.905171], [82.071662, 27.89003], [82.090369, 27.872331], [82.107422, 27.863572], [82.151037, 27.848275], [82.270409, 27.760477], [82.347924, 27.726009], [82.401926, 27.677175], [82.440683, 27.666426], [82.526156, 27.675211], [82.652143, 27.70415], [82.673891, 27.696458], [82.679687, 27.694409], [82.69705, 27.669397], [82.709246, 27.630924], [82.718857, 27.556123], [82.72971, 27.518166], [82.752137, 27.494964], [82.876264, 27.487548], [82.901275, 27.480365], [82.947164, 27.457317], [83.010468, 27.443391], [83.132786, 27.444217], [83.169631, 27.431195], [83.21924, 27.393781], [83.231591, 27.381224], [83.243115, 27.362155], [83.249419, 27.348099], [83.259496, 27.338126], [83.282441, 27.330943], [83.304868, 27.331925], [83.32435, 27.341691], [83.341145, 27.356936], [83.362746, 27.385616], [83.369567, 27.398174], [83.370962, 27.410214], [83.355821, 27.428663], [83.353857, 27.44029], [83.355925, 27.452486], [83.360989, 27.462201], [83.387034, 27.470469], [83.480981, 27.469746], [83.590432, 27.45662], [83.663399, 27.43228], [83.801995, 27.365928], [83.847988, 27.351045], [83.85334, 27.346194], [83.854602, 27.34505], [83.861733, 27.354507], [83.867521, 27.358383], [83.877391, 27.361742], [83.87765, 27.369907], [83.873671, 27.380087], [83.87119, 27.38944], [83.842717, 27.418069], [83.834087, 27.434037], [83.853517, 27.440962], [83.899871, 27.443856], [83.922043, 27.449696], [83.923022, 27.449954], [83.935941, 27.446078], [83.975835, 27.439722], [84.007668, 27.440807], [84.028907, 27.453726], [84.079601, 27.509536], [84.099548, 27.516874], [84.11717, 27.513283], [84.121563, 27.494964], [84.130968, 27.486411], [84.141716, 27.480753], [84.165746, 27.472174], [84.175099, 27.462976], [84.185848, 27.438636], [84.195253, 27.436053], [84.225536, 27.440393], [84.239023, 27.431143], [84.248893, 27.412436], [84.267703, 27.388562], [84.289408, 27.376108], [84.577039, 27.329031], [84.606546, 27.310479], [84.631919, 27.277044], [84.648197, 27.240716], [84.657654, 27.203405], [84.659824, 27.165113], [84.654398, 27.125374], [84.644528, 27.103721], [84.630369, 27.080829], [84.62148, 27.057988], [84.627268, 27.03649], [84.640239, 27.028377], [84.760697, 26.999025], [84.771962, 26.99918], [84.785501, 27.008482], [84.801934, 27.013753], [84.817489, 27.0106], [84.828134, 26.994943], [84.828289, 26.994839], [84.851751, 26.982127], [84.901567, 26.967192], [84.924046, 26.955668], [84.938567, 26.9366], [84.944251, 26.915723], [84.952726, 26.897636], [84.975774, 26.886887], [84.988176, 26.883787], [85.000837, 26.882236], [85.018045, 26.874433], [85.016857, 26.85924], [85.018562, 26.845804], [85.043987, 26.843737], [85.100211, 26.863529], [85.122845, 26.8657], [85.162016, 26.851024], [85.16553, 26.820793], [85.165685, 26.786273], [85.194934, 26.758885], [85.28728, 26.736974], [85.302369, 26.737336], [85.337044, 26.746792], [85.353219, 26.757593], [85.355352, 26.759912], [85.369238, 26.775008], [85.385878, 26.788444], [85.40319, 26.787617], [85.421535, 26.782656], [85.439622, 26.78772], [85.475692, 26.805238], [85.519462, 26.826426], [85.598578, 26.854383], [85.609379, 26.851024], [85.687772, 26.811853], [85.701828, 26.796608], [85.709631, 26.76245], [85.702241, 26.68845], [85.712887, 26.653206], [85.727046, 26.6376], [85.780996, 26.599204], [85.789988, 26.597034], [85.800065, 26.600703], [85.809728, 26.603028], [85.817635, 26.596724], [85.819547, 26.588301], [85.819753, 26.579516], [85.821614, 26.571713], [85.828538, 26.566131], [85.844765, 26.568509], [85.866366, 26.579929], [85.934682, 26.632897], [85.952148, 26.642044], [85.975713, 26.64437], [86.01137, 26.654447], [86.041704, 26.645455], [86.110692, 26.606749], [86.115859, 26.602563], [86.122061, 26.60029], [86.144798, 26.60153], [86.146355, 26.601356], [86.152653, 26.600651], [86.167794, 26.596621], [86.17446, 26.593313], [86.179266, 26.588611], [86.185364, 26.584425], [86.195855, 26.582668], [86.202883, 26.58458], [86.225155, 26.597396], [86.263086, 26.609075], [86.284428, 26.61202], [86.301378, 26.609023], [86.308613, 26.60215], [86.309026, 26.587991], [86.316002, 26.580963], [86.323185, 26.580084], [86.344838, 26.582616], [86.353674, 26.582616], [86.383647, 26.572849], [86.444832, 26.543084], [86.475476, 26.531973], [86.494906, 26.527839], [86.510616, 26.520139], [86.52431, 26.509081], [86.537746, 26.49487], [86.537953, 26.49487], [86.5579, 26.484018], [86.625337, 26.456319], [86.695204, 26.418234], [86.713601, 26.414565], [86.724091, 26.421954], [86.730706, 26.433788], [86.738199, 26.44371], [86.751893, 26.445519], [86.78724, 26.433065], [86.796438, 26.431514], [86.821863, 26.438129], [86.846357, 26.45265], [86.865684, 26.472442], [86.875968, 26.494921], [86.907129, 26.51151], [86.935153, 26.520289], [86.972448, 26.531973], [86.985212, 26.540655], [87.015443, 26.568974], [87.029912, 26.579774], [87.041333, 26.580187], [87.044433, 26.561171], [87.045002, 26.544272], [87.056474, 26.494921], [87.056681, 26.49487], [87.066706, 26.465621], [87.083294, 26.432083], [87.10629, 26.404746], [87.135022, 26.394204], [87.188455, 26.399578], [87.2191, 26.408105], [87.229435, 26.398752], [87.236308, 26.3833], [87.2453, 26.370226], [87.258425, 26.36294], [87.30049, 26.34599], [87.314029, 26.343768], [87.326225, 26.353328], [87.344932, 26.389346], [87.356404, 26.403712], [87.384206, 26.418647], [87.416452, 26.426967], [87.449628, 26.428621], [87.480324, 26.423401], [87.552051, 26.386711], [87.586984, 26.378029], [87.623984, 26.392912], [87.648745, 26.409993], [87.659641, 26.41751], [87.68119, 26.424228], [87.697572, 26.41627], [87.710904, 26.405676], [87.727544, 26.403764], [87.74222, 26.410482], [87.749455, 26.425727], [87.756173, 26.446914], [87.768988, 26.451461], [87.785938, 26.445984], [87.804439, 26.437354], [87.821595, 26.437509], [87.852084, 26.46066], [87.869809, 26.464639], [87.870688, 26.460712], [87.894769, 26.442935], [87.897301, 26.443452], [87.902004, 26.435287], [87.908515, 26.41782], [87.914768, 26.40826], [87.928979, 26.396426], [87.961225, 26.378959], [87.975488, 26.366557], [88.006648, 26.369864], [88.044321, 26.405676], [88.074189, 26.453942], [88.082251, 26.49487], [88.07915, 26.507375], [88.079822, 26.517556], [88.087136, 26.5391], [88.101681, 26.581944], [88.14664, 26.661216], [88.163383, 26.705141], [88.167517, 26.725037], [88.169067, 26.744002], [88.167827, 26.762915], [88.159042, 26.802551], [88.155321, 26.845598], [88.151394, 26.862806], [88.142816, 26.878412], [88.12044, 26.90885], [88.11181, 26.924301], [88.096824, 26.959337], [88.076877, 26.99179], [88.055948, 27.018042], [88.042822, 27.028946], [88.027371, 27.035353], [88.009491, 27.045637], [87.9913, 27.081501], [87.975488, 27.095143], [87.970733, 27.10274], [87.969183, 27.110801], [87.970837, 27.119224], [87.985461, 27.14837], [87.989337, 27.218391], [88.004663, 27.249163], [88.029024, 27.298076], [88.035122, 27.322571], [88.032538, 27.333888], [88.020136, 27.35337], [88.015795, 27.364222], [88.014762, 27.378072], [88.017242, 27.389234], [88.02582, 27.412075], [88.029593, 27.418741], [88.034812, 27.423805], [88.039308, 27.42949], [88.041633, 27.438068], [88.039773, 27.442357], [88.030781, 27.450729], [88.028094, 27.455121], [88.02334, 27.474655], [88.0221, 27.484241], [88.02334, 27.494912], [88.048765, 27.545348], [88.11088, 27.639503], [88.134858, 27.723167], [88.149327, 27.748772], [88.159662, 27.77412], [88.154598, 27.815151], [88.166587, 27.833599], [88.164623, 27.845356], [88.156407, 27.851273], [88.143023, 27.855717], [88.118218, 27.860885], [88.104885, 27.879721], [88.097496, 27.904008], [88.099924, 27.928322], [88.115737, 27.947262], [88.126693, 27.95044], [88.151187, 27.94721], [88.16297, 27.9469], [88.174855, 27.949768], [88.197851, 27.958295], [88.37877, 27.982634], [88.399751, 27.994726], [88.399854, 27.994726], [88.399906, 27.994881], [88.455768, 28.03152], [88.475405, 28.036248], [88.50207, 28.028885], [88.51716, 28.039582], [88.530802, 28.059012], [88.552817, 28.078055], [88.594003, 28.106606], [88.610488, 28.105831], [88.631675, 28.083352], [88.652035, 28.069399], [88.71012, 28.061983], [88.735648, 28.055265], [88.780141, 28.028342], [88.802931, 28.011005], [88.817503, 27.994881], [88.81771, 27.994881], [88.81771, 27.994726], [88.819674, 27.977337], [88.809752, 27.944497], [88.810682, 27.927857], [88.818899, 27.915196], [88.842773, 27.892407], [88.851558, 27.877163], [88.855072, 27.859179], [88.853884, 27.843676], [88.805669, 27.655109], [88.783087, 27.622036], [88.767377, 27.586198], [88.748154, 27.560128], [88.741022, 27.54571], [88.741642, 27.53168], [88.757559, 27.511448], [88.757197, 27.494964], [88.754355, 27.464216], [88.759781, 27.435071], [88.773578, 27.408509], [88.795386, 27.385926], [88.808202, 27.378382], [88.819829, 27.373627], [88.830836, 27.367323], [88.852023, 27.342363], [88.864632, 27.33239], [88.892331, 27.315543], [88.884631, 27.286346], [88.876116, 27.280486], [88.861428, 27.270378], [88.801174, 27.256425], [88.775852, 27.240871], [88.754355, 27.212604], [88.738438, 27.179789], [88.730067, 27.150954], [88.733082, 27.148972], [88.742572, 27.142737], [88.805256, 27.113023], [88.827632, 27.097882], [88.840809, 27.075248], [88.845615, 27.049565], [88.845615, 26.994943], [88.845615, 26.994839], [88.851713, 26.945437], [88.867113, 26.964247], [88.88644, 26.978975], [88.906645, 26.981093], [88.924474, 26.961663], [88.926954, 26.949416], [88.925404, 26.939029], [88.9253, 26.929365], [88.932328, 26.919237], [88.942767, 26.913707], [88.954549, 26.912622], [88.965815, 26.915464], [88.975323, 26.921717], [88.996614, 26.922751], [89.021574, 26.912674], [89.04488, 26.897119], [89.060693, 26.881461], [89.074335, 26.856398], [89.082448, 26.836037], [89.096504, 26.821413], [89.128079, 26.813455], [89.184613, 26.810561], [89.212828, 26.813041], [89.240837, 26.819759], [89.252619, 26.826787], [89.262954, 26.836348], [89.273599, 26.843892], [89.286364, 26.844978], [89.300161, 26.844409], [89.341657, 26.854331], [89.368895, 26.837359], [89.407338, 26.813403], [89.442891, 26.797022], [89.50542, 26.803688], [89.546503, 26.797539], [89.586087, 26.784051], [89.611357, 26.765964], [89.613475, 26.748549], [89.597507, 26.720954], [89.609806, 26.712221], [89.628307, 26.712531], [89.66324, 26.725502], [89.685151, 26.724571], [89.738739, 26.703281], [89.75467, 26.700989], [89.760288, 26.70018], [89.800079, 26.70049], [89.804058, 26.699767], [89.81367, 26.696149], [89.817287, 26.696201], [89.822093, 26.701007], [89.825245, 26.707673], [89.826796, 26.713513], [89.826744, 26.715683], [89.827261, 26.717905], [89.827674, 26.722453], [89.829741, 26.72762], [89.834961, 26.731548], [89.839043, 26.731238], [89.847001, 26.72483], [89.850774, 26.723176], [89.858701, 26.722057], [89.85992, 26.721884], [89.880281, 26.716252], [89.890409, 26.714856], [89.912113, 26.716717], [89.975262, 26.731858], [90.089157, 26.741728], [90.127191, 26.751392], [90.152202, 26.771752], [90.17742, 26.832058], [90.210907, 26.851489], [90.229148, 26.852884], [90.265994, 26.851592], [90.284442, 26.85707], [90.300875, 26.868284], [90.314208, 26.880376], [90.328574, 26.890659], [90.348831, 26.896654], [90.382627, 26.891796], [90.475232, 26.83242], [90.587783, 26.780072], [90.717077, 26.767049], [90.944493, 26.77887], [91.007395, 26.782139], [91.012781, 26.784348], [91.062069, 26.804567], [91.091938, 26.804773], [91.127078, 26.800898], [91.198185, 26.802344], [91.232498, 26.795161], [91.261954, 26.779038], [91.27601, 26.774077], [91.296577, 26.774594], [91.313217, 26.778677], [91.330063, 26.785498], [91.345566, 26.794696], [91.358382, 26.806169], [91.370061, 26.824669], [91.378122, 26.842807], [91.388457, 26.858465], [91.406338, 26.869524], [91.420187, 26.871539], [91.460598, 26.869369], [91.475067, 26.865545], [91.484472, 26.852729], [91.507417, 26.808029], [91.520646, 26.79759], [91.539353, 26.79883], [91.57563, 26.810148], [91.593716, 26.810768], [91.637951, 26.798985], [91.653764, 26.797952], [91.702133, 26.803481], [91.731589, 26.816194], [91.795254, 26.853504], [91.825123, 26.858465], [91.843934, 26.84937], [91.849515, 26.834746], [91.852408, 26.818519], [91.863054, 26.804773], [91.87866, 26.803016], [91.886928, 26.814437], [91.885895, 26.83087], [91.874009, 26.844306], [91.895507, 26.853504], [91.895507, 26.868284], [91.89313, 26.881358], [91.908219, 26.885182], [91.925686, 26.878671], [91.957415, 26.854279], [91.975088, 26.846631], [92.03586, 26.854848], [92.072757, 26.887766], [92.080371, 26.921571], [92.083919, 26.937323], [92.066969, 26.994839], [92.066969, 26.994943], [92.049709, 27.026827], [91.999893, 27.071527], [91.987697, 27.104031], [91.987904, 27.122273], [91.990798, 27.140773], [91.995862, 27.158033], [92.002994, 27.172606], [92.005788, 27.176112], [92.008471, 27.179479], [92.021494, 27.19183], [92.027075, 27.19984], [92.029969, 27.20847], [92.033173, 27.227435], [92.03679, 27.236478], [92.050639, 27.251413], [92.081852, 27.275132], [92.088776, 27.29234], [92.084849, 27.304226], [92.023598, 27.44678], [91.975088, 27.472433], [91.963306, 27.468919], [91.900591, 27.45973], [91.856578, 27.466239], [91.816232, 27.461357], [91.779555, 27.456475], [91.727092, 27.459942], [91.727041, 27.460005], [91.704924, 27.468764], [91.680223, 27.472846], [91.657382, 27.479151], [91.641672, 27.494964], [91.632887, 27.511655], [91.604465, 27.532145], [91.59506, 27.546382], [91.573252, 27.619711], [91.579867, 27.657977], [91.626686, 27.716423], [91.634957, 27.746571], [91.63577, 27.782525], [91.605336, 27.810658], [91.566023, 27.821074], [91.564355, 27.851372], [91.628339, 27.852694], [91.680131, 27.849823], [91.729871, 27.804136], [91.826589, 27.807627], [91.856373, 27.764539], [91.864914, 27.729988], [91.908736, 27.7319], [91.952247, 27.72482], [91.968001, 27.746278], [91.988898, 27.769398], [92.064878, 27.761721], [92.10616, 27.786081], [92.126294, 27.812722], [92.239568, 27.865277], [92.24918, 27.862642], [92.258275, 27.848198], [92.275225, 27.811689], [92.288764, 27.793085], [92.303854, 27.78616], [92.31729, 27.803679], [92.329175, 27.832928], [92.334653, 27.831274], [92.339821, 27.815564], [92.350053, 27.802542], [92.367933, 27.803834], [92.375581, 27.821119], [92.381058, 27.842462], [92.392841, 27.856027], [92.404416, 27.853831], [92.417335, 27.828948], [92.427567, 27.821171], [92.439039, 27.82329], [92.475109, 27.846596], [92.518151, 27.839468], [92.574977, 27.847806], [92.592912, 27.868193], [92.636468, 27.89537], [92.683674, 27.91045], [92.71077, 27.949702], [92.736392, 27.985909], [92.701349, 28.025241], [92.701349, 28.037825], [92.689257, 28.048315], [92.65422, 28.052087], [92.638924, 28.057487], [92.63727, 28.071983], [92.65484, 28.105831], [92.678611, 28.133064], [92.70786, 28.155414], [92.770079, 28.192182], [92.779174, 28.195903], [92.782894, 28.190942], [92.785375, 28.183294], [92.790232, 28.178901], [92.805322, 28.178023], [92.819895, 28.179263], [92.834777, 28.183526], [92.85028, 28.19195], [92.86537, 28.205205], [92.889554, 28.235823], [92.903817, 28.249698], [92.922007, 28.258638], [92.960351, 28.270214], [92.974924, 28.282306], [93.046399, 28.302097], [93.129828, 28.325962], [93.207825, 28.340539], [93.218073, 28.394647], [93.186286, 28.431995], [93.218838, 28.457436], [93.259068, 28.496023], [93.286972, 28.520517], [93.302843, 28.556921], [93.31717, 28.603463], [93.355432, 28.624065], [93.446213, 28.671894], [93.551736, 28.678948], [93.607133, 28.672204], [93.62553, 28.67236], [93.64341, 28.68024], [93.664908, 28.690239], [93.705525, 28.691893], [93.722475, 28.696647], [93.781744, 28.680504], [93.86308, 28.704871], [93.927786, 28.682752], [93.961093, 28.729943], [93.981591, 28.768131], [93.993136, 28.801887], [93.992019, 28.844649], [94.01145, 28.85302], [94.026849, 28.864182], [94.079474, 28.883072], [94.135396, 28.897788], [94.173658, 28.930164], [94.250183, 28.933107], [94.291389, 28.977256], [94.347311, 29.024349], [94.309048, 29.059668], [94.270786, 29.09793], [94.287505, 29.147938], [94.323782, 29.146026], [94.335771, 29.149617], [94.346313, 29.159074], [94.36254, 29.185326], [94.372875, 29.196307], [94.396439, 29.207081], [94.422381, 29.210492], [94.474987, 29.210802], [94.498242, 29.21522], [94.514778, 29.22106], [94.528628, 29.23124], [94.582681, 29.302915], [94.599941, 29.316635], [94.63043, 29.319452], [94.668464, 29.30661], [94.704224, 29.284699], [94.756417, 29.230517], [94.767683, 29.213851], [94.761482, 29.174706], [94.776571, 29.166696], [94.798792, 29.166438], [94.815432, 29.168867], [94.854086, 29.170004], [94.891293, 29.160495], [94.989065, 29.153984], [95.047666, 29.140806], [95.099032, 29.11378], [95.116912, 29.108095], [95.191533, 29.09683], [95.199698, 29.094246], [95.207863, 29.089595], [95.212824, 29.081869], [95.214064, 29.073084], [95.216338, 29.064997], [95.224916, 29.059364], [95.281553, 29.052672], [95.367406, 29.036496], [95.429824, 29.046525], [95.487577, 29.068994], [95.511307, 29.131789], [95.521435, 29.137861], [95.522675, 29.161193], [95.511927, 29.197547], [95.515337, 29.209433], [95.550167, 29.212482], [95.552028, 29.22013], [95.550684, 29.230284], [95.553785, 29.239043], [95.56443, 29.245606], [95.572905, 29.24726], [95.58169, 29.247467], [95.592335, 29.249689], [95.646697, 29.22835], [95.717165, 29.218284], [95.747365, 29.273651], [95.744781, 29.340432], [95.776407, 29.345548], [95.79594, 29.352886], [95.863134, 29.323985], [95.953735, 29.359219], [96.014135, 29.364252], [96.074536, 29.369286], [96.141966, 29.368467], [96.150957, 29.354075], [96.166047, 29.303174], [96.176279, 29.286456], [96.205424, 29.256949], [96.235293, 29.241214], [96.26785, 29.24173], [96.304126, 29.261212], [96.323247, 29.27501], [96.337096, 29.279713], [96.349912, 29.274235], [96.366241, 29.257233], [96.366862, 29.244211], [96.342367, 29.210647], [96.327484, 29.180442], [96.316425, 29.171864], [96.210075, 29.145767], [96.193642, 29.136931], [96.183514, 29.123779], [96.174625, 29.108845], [96.175954, 29.017354], [96.195323, 28.941106], [96.248588, 28.945343], [96.294655, 28.992324], [96.366568, 29.036537], [96.457605, 28.994588], [96.451198, 28.981385], [96.452128, 28.970378], [96.460086, 28.96335], [96.474659, 28.962058], [96.492229, 28.948002], [96.500394, 28.929243], [96.510832, 28.885525], [96.523751, 28.864415], [96.576668, 28.808527], [96.592584, 28.757884], [96.59801, 28.70991], [96.501044, 28.687631], [96.502375, 28.644476], [96.409019, 28.558929], [96.336459, 28.479667], [96.301852, 28.420711], [96.369644, 28.378116], [96.441127, 28.395416], [96.495226, 28.42147], [96.512382, 28.428808], [96.561518, 28.447293], [96.604426, 28.444534], [96.716924, 28.436539], [96.897889, 28.354704], [96.933959, 28.336618], [96.965791, 28.330468], [96.997417, 28.337083], [97.032971, 28.35703], [97.078446, 28.375143], [97.11555, 28.366564], [97.182729, 28.314991], [97.193271, 28.3114], [97.20278, 28.311555], [97.210531, 28.308092], [97.220763, 28.283391], [97.229651, 28.274606], [97.285255, 28.235668], [97.323496, 28.217478], [97.328663, 28.190012], [97.298898, 28.12924], [97.292903, 28.095082], [97.306546, 28.069657], [97.355122, 28.023045], [97.362253, 27.994881], [97.356775, 27.980567], [97.340032, 27.951809], [97.335691, 27.935609], [97.336311, 27.913181], [97.338172, 27.901554], [97.333521, 27.894164], [97.314917, 27.884604], [97.292903, 27.87755], [97.288666, 27.884346], [97.288046, 27.897756], [97.276367, 27.910339], [97.260657, 27.912044], [97.241227, 27.907858], [97.222933, 27.899719], [97.210944, 27.889513], [97.198025, 27.873261], [97.182109, 27.857267], [97.131776, 27.816856], [97.123198, 27.812154], [97.112449, 27.809208], [97.106041, 27.805539], [97.098186, 27.791018], [97.092192, 27.785489], [97.072348, 27.779184], [97.06532, 27.773991], [97.062013, 27.763345], [97.060359, 27.750452], [97.056742, 27.746783], [97.050541, 27.746783], [97.036175, 27.74292], [97.004032, 27.734277], [96.990906, 27.726629], [96.957006, 27.688079], [96.93954, 27.674023], [96.899749, 27.649476], [96.883523, 27.636325], [96.87019, 27.61909], [96.862129, 27.599298], [96.862025, 27.578369], [96.866159, 27.568448], [96.877321, 27.553074], [96.880319, 27.54385], [96.881042, 27.534393], [96.884659, 27.515841], [96.888484, 27.507185], [96.890241, 27.492483], [96.877011, 27.463079], [96.881352, 27.444114], [96.905123, 27.408406], [97.067904, 27.2171], [97.095499, 27.191261], [97.101287, 27.183303], [97.1017, 27.165268], [97.104388, 27.156018], [97.111726, 27.148835], [97.120821, 27.143926], [97.129296, 27.13788], [97.134773, 27.127338], [97.133016, 27.119586], [97.122888, 27.093696], [97.119167, 27.087288], [97.106041, 27.082586], [97.09798, 27.085893], [97.089918, 27.092198], [97.077206, 27.096487], [97.066974, 27.095246], [97.058827, 27.092076], [97.047854, 27.087805], [97.038035, 27.08703], [97.02677, 27.091474], [97.018915, 27.098709], [97.011887, 27.107029], [97.002998, 27.114522], [96.865332, 27.171624], [96.842698, 27.188522], [96.841768, 27.204387], [96.851276, 27.221182], [96.859648, 27.240716], [96.853757, 27.248725], [96.821718, 27.273065], [96.809935, 27.285777], [96.789161, 27.317972], [96.776346, 27.330633], [96.758879, 27.341743], [96.724152, 27.356264], [96.705239, 27.36076], [96.687255, 27.361742], [96.675887, 27.357659], [96.659247, 27.341433], [96.649015, 27.336214], [96.636406, 27.335542], [96.629275, 27.338694], [96.623177, 27.343655], [96.614185, 27.348409], [96.5869, 27.353577], [96.576048, 27.345412], [96.56747, 27.328307], [96.547316, 27.306448], [96.531813, 27.298542], [96.510936, 27.292495], [96.490058, 27.290738], [96.474659, 27.295389], [96.407789, 27.29818], [96.142586, 27.25751], [96.074166, 27.229864], [96.013188, 27.190796], [95.974844, 27.142892], [95.938361, 27.079899], [95.91645, 27.051373], [95.888855, 27.027034], [95.861466, 27.014218], [95.804932, 27.004761], [95.777957, 26.995149], [95.734962, 26.947297], [95.709227, 26.907454], [95.699305, 26.896189], [95.686076, 26.89128], [95.639981, 26.886422], [95.624271, 26.880738], [95.613316, 26.867922], [95.603394, 26.844254], [95.591302, 26.823739], [95.574765, 26.816142], [95.554405, 26.816607], [95.531357, 26.820276], [95.489809, 26.810716], [95.462731, 26.777178], [95.439993, 26.735372], [95.410641, 26.701627], [95.392658, 26.691757], [95.31597, 26.669071], [95.278969, 26.652948], [95.260573, 26.647625], [95.246933, 26.648904], [95.239075, 26.649641], [95.223262, 26.670363], [95.204865, 26.667572], [95.195564, 26.660751], [95.181611, 26.641682], [95.173239, 26.633828], [95.163421, 26.629745], [95.141407, 26.624991], [95.132622, 26.620805], [95.122803, 26.611607], [95.119082, 26.604217], [95.115879, 26.57564], [95.116189, 26.569335], [95.114948, 26.562566], [95.109574, 26.5513], [95.099239, 26.536107], [95.069887, 26.506239], [95.054384, 26.494921], [95.040638, 26.475594], [95.042601, 26.459678], [95.061722, 26.426605], [95.066062, 26.407433], [95.064202, 26.392602], [95.050456, 26.359064], [95.043635, 26.327696], [95.041775, 26.287596], [95.046322, 26.24796], [95.058724, 26.217677], [95.066373, 26.211011], [95.074537, 26.208324], [95.082806, 26.206619], [95.090764, 26.202691], [95.094484, 26.195043], [95.08973, 26.187602], [95.082392, 26.179798], [95.078361, 26.171324], [95.08818, 26.104093], [95.101719, 26.093395], [95.137479, 26.082957], [95.150088, 26.070968], [95.151225, 26.049936], [95.139546, 26.029937], [95.10823, 25.995107], [95.065856, 25.953817], [95.049423, 25.943741], [95.012009, 25.931442], [95.00064, 25.922036], [94.994026, 25.900436], [94.993199, 25.880954], [95.013456, 25.777704], [95.015316, 25.755122], [95.008805, 25.737138], [94.999917, 25.731557], [94.978626, 25.729077], [94.969014, 25.725614], [94.961469, 25.717966], [94.871656, 25.598025], [94.868038, 25.594718], [94.857496, 25.589344], [94.853466, 25.586036], [94.852949, 25.580765], [94.86101, 25.571412], [94.860287, 25.567174], [94.850778, 25.562782], [94.831038, 25.562472], [94.822356, 25.559113], [94.80799, 25.542628], [94.782359, 25.504594], [94.764375, 25.491675], [94.745772, 25.486611], [94.711149, 25.482787], [94.692028, 25.476327], [94.659472, 25.455502], [94.65036, 25.44665], [94.630533, 25.42739], [94.608003, 25.394627], [94.550125, 25.245282], [94.553329, 25.204147], [94.576893, 25.173607], [94.612964, 25.161359], [94.653271, 25.154125], [94.689135, 25.138518], [94.706808, 25.109941], [94.713836, 25.068264], [94.708565, 25.02589], [94.689755, 24.99509], [94.673528, 24.974807], [94.654821, 24.889154], [94.639112, 24.862799], [94.599011, 24.813474], [94.593223, 24.783786], [94.593223, 24.765931], [94.589502, 24.74725], [94.581544, 24.73012], [94.569452, 24.716399], [94.555706, 24.708855], [94.543407, 24.70733], [94.531005, 24.707485], [94.516329, 24.704953], [94.507957, 24.689011], [94.48708, 24.620462], [94.474987, 24.597389], [94.430236, 24.570904], [94.418453, 24.560311], [94.410805, 24.545945], [94.397059, 24.495069], [94.381866, 24.48631], [94.370394, 24.477732], [94.363676, 24.46582], [94.361196, 24.425409], [94.352204, 24.413937], [94.340009, 24.406005], [94.328743, 24.394481], [94.32833, 24.388487], [94.33174, 24.371098], [94.330707, 24.362855], [94.323679, 24.354664], [94.302802, 24.341022], [94.29536, 24.332599], [94.289986, 24.31676], [94.286885, 24.283067], [94.282751, 24.266427], [94.233969, 24.160335], [94.222186, 24.122637], [94.218466, 24.08667], [94.214125, 24.069178], [94.203686, 24.057757], [94.199139, 24.048094], [94.193868, 24.009673], [94.19025, 23.995281], [94.144878, 23.938876], [94.13506, 23.919006], [94.133613, 23.899162], [94.13506, 23.877303], [94.131856, 23.856969], [94.128859, 23.853905], [94.11687, 23.841647], [94.099713, 23.842628], [94.085347, 23.857511], [94.071084, 23.876115], [94.055065, 23.887949], [93.997911, 23.916965], [93.97507, 23.920918], [93.94055, 23.928876], [93.912334, 23.939005], [93.883189, 23.94531], [93.803711, 23.936059], [93.782833, 23.945465], [93.743042, 23.995281], [93.711106, 24.005461], [93.660464, 24.011223], [93.612404, 24.009052], [93.588013, 23.995229], [93.585636, 23.987297], [93.581812, 23.979597], [93.576954, 23.973008], [93.571063, 23.968021], [93.561348, 23.965696], [93.55525, 23.970993], [93.549979, 23.978021], [93.543468, 23.980889], [93.526828, 23.975385], [93.495719, 23.959133], [93.474842, 23.957505], [93.456652, 23.95996], [93.449004, 23.968926], [93.445593, 23.981612], [93.439185, 23.995229], [93.406526, 24.025847], [93.3965, 24.037526], [93.368285, 24.07848], [93.348545, 24.088893], [93.32219, 24.080056], [93.309684, 24.063984], [93.302966, 24.041143], [93.302559, 24.035775], [93.301106, 24.016571], [93.302966, 23.995281], [93.308134, 23.977504], [93.316092, 23.961097], [93.337589, 23.928411], [93.345961, 23.918696], [93.351852, 23.913916], [93.355986, 23.906991], [93.35919, 23.890946], [93.35826, 23.855263], [93.374796, 23.739017], [93.406319, 23.719122], [93.412313, 23.708709], [93.425956, 23.690803], [93.430297, 23.680339], [93.432364, 23.647317], [93.400221, 23.454022], [93.399808, 23.426091], [93.405905, 23.36767], [93.407766, 23.356379], [93.407249, 23.347258], [93.403735, 23.338757], [93.384201, 23.313126], [93.377897, 23.296822], [93.37645, 23.279485], [93.380791, 23.237265], [93.372729, 23.169827], [93.373039, 23.154221], [93.37521, 23.142154], [93.3749, 23.129726], [93.333352, 23.052289], [93.318262, 23.03366], [93.298832, 23.01795], [93.272787, 23.00591], [93.246535, 23.004488], [93.225555, 23.020379], [93.212119, 23.039887], [93.196512, 23.053839], [93.177082, 23.058439], [93.152381, 23.049938], [93.140082, 23.031903], [93.138221, 23.005264], [93.143182, 22.977513], [93.151141, 22.956223], [93.171088, 22.920437], [93.177392, 22.900205], [93.175532, 22.881705], [93.161062, 22.860983], [93.125302, 22.821554], [93.115897, 22.798403], [93.112383, 22.799953], [93.079517, 22.77272], [93.070835, 22.69288], [93.073936, 22.671899], [93.081894, 22.65283], [93.104425, 22.615933], [93.110523, 22.596348], [93.109076, 22.574696], [93.098224, 22.535266], [93.099154, 22.51196], [93.109593, 22.479042], [93.117861, 22.462868], [93.153518, 22.427521], [93.16592, 22.386593], [93.174292, 22.269391], [93.173672, 22.259418], [93.169021, 22.246912], [93.161269, 22.242519], [93.151451, 22.240969], [93.129747, 22.231616], [93.124786, 22.232287], [93.123545, 22.230686], [93.123132, 22.218387], [93.141735, 22.187277], [93.119515, 22.180973], [93.104322, 22.187174], [93.090576, 22.197148], [93.072489, 22.20247], [93.045824, 22.206863], [93.036419, 22.206243], [93.024533, 22.201902], [93.022363, 22.196217], [93.023913, 22.188828], [93.02257, 22.120976], [93.025464, 22.112915], [93.00748, 22.105939], [92.995181, 22.103251], [92.985466, 22.095965], [92.967172, 22.062685], [92.965312, 22.04956], [92.968413, 22.036175], [92.974924, 22.023308], [92.977921, 22.016487], [92.979575, 22.009355], [92.979885, 22.002276], [92.978955, 21.995351], [92.977094, 21.993026], [92.974924, 21.990907], [92.968103, 21.987238], [92.961695, 21.986876], [92.955597, 21.989615], [92.950326, 21.995351], [92.937407, 22.013024], [92.920974, 22.021809], [92.906194, 22.017314], [92.89865, 21.995403], [92.889244, 21.966722], [92.877462, 21.957007], [92.867024, 21.966516], [92.862063, 21.995403], [92.853898, 22.024135], [92.836121, 22.046511], [92.771629, 22.104182], [92.708067, 22.147952], [92.683779, 22.154308], [92.67396, 22.147073], [92.671067, 22.133896], [92.674684, 22.105577], [92.67396, 22.095242], [92.669516, 22.092658], [92.664555, 22.093846], [92.662075, 22.094777], [92.656184, 22.075294], [92.65608, 22.060567], [92.658354, 22.046562], [92.658354, 22.032816], [92.650913, 22.019174], [92.639854, 22.013386], [92.627555, 22.012353], [92.615256, 22.008787], [92.603887, 21.995403], [92.597996, 21.989305], [92.591278, 21.984241], [92.583837, 21.980313], [92.575879, 21.977574], [92.575155, 21.986566], [92.573088, 21.995403], [92.559962, 22.061342], [92.537328, 22.128625], [92.549317, 22.138495], [92.5642, 22.13834], [92.575568, 22.143301], [92.577015, 22.168674], [92.518931, 22.495372], [92.517381, 22.51258], [92.504565, 22.543793], [92.500844, 22.560536], [92.502911, 22.618621], [92.495883, 22.695618], [92.491543, 22.71107], [92.480691, 22.726159], [92.453715, 22.747967], [92.442037, 22.762927], [92.435009, 22.793287], [92.426327, 22.87106], [92.411858, 22.887932], [92.378681, 22.900438], [92.359871, 22.926612], [92.352223, 22.960331], [92.35305, 23.029913], [92.327625, 23.171429], [92.328555, 23.211194], [92.332689, 23.226258], [92.350673, 23.260984], [92.357494, 23.278942], [92.356874, 23.289122], [92.352223, 23.298837], [92.347055, 23.315426], [92.346745, 23.325296], [92.349536, 23.344959], [92.348606, 23.354105], [92.344885, 23.359118], [92.331656, 23.368652], [92.326695, 23.375267], [92.325351, 23.382166], [92.326901, 23.396015], [92.325351, 23.403508], [92.31977, 23.412164], [92.306748, 23.423869], [92.301477, 23.431801], [92.296826, 23.44658], [92.291038, 23.495285], [92.252797, 23.609309], [92.250007, 23.642615], [92.261686, 23.685119], [92.259737, 23.702641], [92.259309, 23.706487], [92.238741, 23.716848], [92.222102, 23.707779], [92.209389, 23.661606], [92.1943, 23.648144], [92.180761, 23.665378], [92.170632, 23.703645], [92.150788, 23.731653], [92.108414, 23.718243], [92.092187, 23.701345], [92.059631, 23.659487], [92.042371, 23.64556], [92.019013, 23.640057], [92.00351, 23.647421], [91.961652, 23.691139], [91.949147, 23.709768], [91.936641, 23.72354], [91.922688, 23.722972], [91.916487, 23.709872], [91.91442, 23.688323], [91.915971, 23.65016], [91.942325, 23.53443], [91.940775, 23.495182], [91.918141, 23.459939], [91.905118, 23.446632], [91.889099, 23.435935], [91.833805, 23.413895], [91.817682, 23.400924], [91.791637, 23.368342], [91.762595, 23.321549], [91.743681, 23.272327], [91.749159, 23.232407], [91.763938, 23.200626], [91.779028, 23.131767], [91.791947, 23.101149], [91.795564, 23.089496], [91.791327, 23.080375], [91.776237, 23.06526], [91.773757, 23.064898], [91.764248, 23.065647], [91.760941, 23.065492], [91.75598, 23.061539], [91.755567, 23.056837], [91.75629, 23.05216], [91.75505, 23.048233], [91.738307, 23.024668], [91.731279, 23.01733], [91.714432, 23.003119], [91.706991, 22.995368], [91.694692, 22.988133], [91.667614, 22.982629], [91.654074, 22.978056], [91.646943, 22.976583], [91.639915, 22.978004], [91.632784, 22.980304], [91.625756, 22.981544], [91.616661, 22.978056], [91.61201, 22.969839], [91.608289, 22.960331], [91.602398, 22.952967], [91.586068, 22.944466], [91.582968, 22.947903], [91.583071, 22.957799], [91.576766, 22.968806], [91.563124, 22.974981], [91.549791, 22.977229], [91.536562, 22.981854], [91.523643, 22.995213], [91.519406, 23.005625], [91.518165, 23.035081], [91.515582, 23.038802], [91.506073, 23.041179], [91.503283, 23.044408], [91.498115, 23.069962], [91.475067, 23.143085], [91.470416, 23.153291], [91.463595, 23.184116], [91.461218, 23.184426], [91.446438, 23.197887], [91.445405, 23.203029], [91.446748, 23.208791], [91.446438, 23.215535], [91.439927, 23.223519], [91.439617, 23.22264], [91.436207, 23.215716], [91.436517, 23.214863], [91.428145, 23.229513], [91.413572, 23.248634], [91.394349, 23.262664], [91.372128, 23.261889], [91.360966, 23.247523], [91.355695, 23.224139], [91.355901, 23.179646], [91.374608, 23.095723], [91.370887, 23.062779], [91.337401, 23.071074], [91.313527, 23.101046], [91.300504, 23.142309], [91.27725, 23.303256], [91.272909, 23.313488], [91.267535, 23.321549], [91.265778, 23.329352], [91.272909, 23.338499], [91.282934, 23.347646], [91.287275, 23.355552], [91.283968, 23.361443], [91.270945, 23.364596], [91.25813, 23.373639], [91.247898, 23.393767], [91.234462, 23.436271], [91.229707, 23.461179], [91.22733, 23.468775], [91.220819, 23.477896], [91.203352, 23.487921], [91.195084, 23.495182], [91.185886, 23.511563], [91.177514, 23.544275], [91.169866, 23.561974], [91.140824, 23.6121], [91.136276, 23.629515], [91.139687, 23.65357], [91.152296, 23.655095], [91.166456, 23.654087], [91.175241, 23.670313], [91.168936, 23.685377], [91.1524, 23.69101], [91.136587, 23.69889], [91.132349, 23.720646], [91.141754, 23.739896], [91.157567, 23.744986], [91.176584, 23.746536], [91.194981, 23.755166], [91.205213, 23.771573], [91.225263, 23.826144], [91.228157, 23.84498], [91.224023, 23.857485], [91.212758, 23.88074], [91.211931, 23.89446], [91.216272, 23.908387], [91.250378, 23.964042], [91.26092, 23.977271], [91.274149, 23.988227], [91.291616, 23.994816], [91.304948, 23.99386], [91.308433, 23.992501], [91.316214, 23.989467], [91.327066, 23.987865], [91.339055, 23.995229], [91.349287, 24.036053], [91.350837, 24.074242], [91.363033, 24.099848], [91.404581, 24.103129], [91.480442, 24.088014], [91.517545, 24.085198], [91.55868, 24.089668], [91.581831, 24.096101], [91.596507, 24.10499], [91.606015, 24.118865], [91.613767, 24.140104], [91.624515, 24.203459], [91.629063, 24.215319], [91.639502, 24.211702], [91.657175, 24.171368], [91.666167, 24.155917], [91.68415, 24.145478], [91.703684, 24.142946], [91.72115, 24.148992], [91.732623, 24.164314], [91.732002, 24.181678], [91.725801, 24.202064], [91.723011, 24.220977], [91.732933, 24.234284], [91.796598, 24.221598], [91.807037, 24.221133], [91.809724, 24.214544], [91.811377, 24.192039], [91.813755, 24.183124], [91.844244, 24.155323], [91.864501, 24.150594], [91.87711, 24.158036], [91.884345, 24.175554], [91.905945, 24.260639], [91.906875, 24.280354], [91.896437, 24.320119], [91.899847, 24.33805], [91.921862, 24.34451], [91.930957, 24.336965], [91.953074, 24.322987], [91.972194, 24.316062], [91.971368, 24.329679], [91.948837, 24.363527], [91.949974, 24.375387], [92.033379, 24.36885], [92.062732, 24.37102], [92.08826, 24.38164], [92.107587, 24.405979], [92.111204, 24.434298], [92.105933, 24.49525], [92.110894, 24.514293], [92.123193, 24.525378], [92.138593, 24.533723], [92.14998, 24.542183], [92.153165, 24.544549], [92.163604, 24.5592], [92.168875, 24.574315], [92.173422, 24.608396], [92.182931, 24.647256], [92.233781, 24.777533], [92.236468, 24.793036], [92.236881, 24.80859], [92.235021, 24.824765], [92.234928, 24.824991], [92.221792, 24.856908], [92.221759, 24.857047], [92.217658, 24.874581], [92.223032, 24.891376], [92.252177, 24.902977], [92.290245, 24.891351], [92.290418, 24.891298], [92.358146, 24.853749], [92.358321, 24.853652], [92.359321, 24.846428], [92.359354, 24.846185], [92.363507, 24.84112], [92.363592, 24.841017], [92.37093, 24.837917], [92.380748, 24.836702], [92.442037, 24.855409], [92.477693, 24.86391], [92.491543, 24.883728], [92.49454, 24.894864], [92.491336, 24.912279], [92.491267, 24.912424], [92.483791, 24.928221], [92.474386, 24.93667], [92.474272, 24.936715], [92.449788, 24.946437], [92.450028, 24.946636], [92.458056, 24.953284], [92.457828, 24.953558], [92.454026, 24.958116], [92.453769, 24.958231], [92.444517, 24.962379], [92.444446, 24.962427], [92.412478, 24.984083], [92.41246, 24.984361], [92.412271, 24.987391], [92.41214, 24.987434], [92.41186, 24.987527], [92.394701, 24.99323], [92.38695, 24.99509], [92.386926, 24.995202], [92.386876, 24.995444], [92.384937, 25.004773], [92.381058, 25.023435], [92.356254, 25.037], [92.328142, 25.048007], [92.312122, 25.0686], [92.303544, 25.074285], [92.251557, 25.080796], [92.233677, 25.084956], [92.220138, 25.090382], [92.208046, 25.098107], [92.194093, 25.109166], [92.17921, 25.126684], [92.172699, 25.131955], [92.164741, 25.133557], [92.150995, 25.129733], [92.145517, 25.131697], [92.138593, 25.1364], [92.124123, 25.136968], [92.116165, 25.139449], [92.110377, 25.144875], [92.100455, 25.159086], [92.092911, 25.164718], [92.066039, 25.174537], [92.033999, 25.181668], [92.001753, 25.18296], [91.975088, 25.175364], [91.956278, 25.169783], [91.900571, 25.177586], [91.793704, 25.165338], [91.745335, 25.169369], [91.730142, 25.167457], [91.723734, 25.161928], [91.71774, 25.149836], [91.710712, 25.144668], [91.701823, 25.147148], [91.688904, 25.152988], [91.677432, 25.152626], [91.672264, 25.136503], [91.662033, 25.127201], [91.638675, 25.124049], [91.613147, 25.125134], [91.596197, 25.128907], [91.580177, 25.140895], [91.573459, 25.152264], [91.566121, 25.159964], [91.548758, 25.161101], [91.540179, 25.157639], [91.523436, 25.145185], [91.511654, 25.142497], [91.500905, 25.141154], [91.480752, 25.135263], [91.471036, 25.133919], [91.431866, 25.13795], [91.283968, 25.178981], [91.235702, 25.201925], [91.224953, 25.201615], [91.203146, 25.191332], [91.190847, 25.189471], [91.135346, 25.191228], [90.975149, 25.167819], [90.94342, 25.157432], [90.934821, 25.15636], [90.82198, 25.142291], [90.793972, 25.145391], [90.779399, 25.151954], [90.766893, 25.160533], [90.753974, 25.167561], [90.737955, 25.169421], [90.732684, 25.166889], [90.722038, 25.156605], [90.716354, 25.153556], [90.707672, 25.152988], [90.670568, 25.158776], [90.648968, 25.167819], [90.637599, 25.171075], [90.62344, 25.171436], [90.583339, 25.162031], [90.50138, 25.168801], [90.399784, 25.149009], [90.364644, 25.149991], [90.286923, 25.180066], [90.130085, 25.211589], [89.908134, 25.296907], [89.870204, 25.295563], [89.834599, 25.282437], [89.819044, 25.284969], [89.807365, 25.304503], [89.798529, 25.340056], [89.795015, 25.374163], [89.799234, 25.402893], [89.800699, 25.412869], [89.808568, 25.439507], [89.82323, 25.489143], [89.82509, 25.564487], [89.834392, 25.634767], [89.824212, 25.674093], [89.801526, 25.724684], [89.783129, 25.814446], [89.786953, 25.839147], [89.830051, 25.90798], [89.834392, 25.931752], [89.826434, 25.937488], [89.810724, 25.93878], [89.792017, 25.949115], [89.811551, 25.955213], [89.825607, 25.965703], [89.828656, 25.979397], [89.815272, 25.995107], [89.814135, 25.99614], [89.812688, 25.996347], [89.811034, 25.996037], [89.809329, 25.99521], [89.809174, 25.995107], [89.808864, 25.995107], [89.802043, 25.988647], [89.795531, 25.98596], [89.789434, 25.987976], [89.783956, 25.995107], [89.776411, 26.008233], [89.755844, 26.032417], [89.750211, 26.041564], [89.748713, 26.059031], [89.751141, 26.071536], [89.749849, 26.083887], [89.736724, 26.100579], [89.728766, 26.114169], [89.724321, 26.130654], [89.718482, 26.145537], [89.706545, 26.154167], [89.697656, 26.154787], [89.677709, 26.153133], [89.670371, 26.154115], [89.658175, 26.159231], [89.655798, 26.161298], [89.657245, 26.165381], [89.657829, 26.187174], [89.658382, 26.207859], [89.653008, 26.22269], [89.634921, 26.225842], [89.61332, 26.219383], [89.606396, 26.211011], [89.608463, 26.180677], [89.615284, 26.169101], [89.614406, 26.164192], [89.593993, 26.169722], [89.586759, 26.169205], [89.579937, 26.166104], [89.573426, 26.16073], [89.562781, 26.142591], [89.569395, 26.130086], [89.585777, 26.122903], [89.604225, 26.120732], [89.592546, 26.113963], [89.57787, 26.107245], [89.566915, 26.098977], [89.567018, 26.087091], [89.57694, 26.082647], [89.590789, 26.085024], [89.603864, 26.084662], [89.611357, 26.072208], [89.602882, 26.055258], [89.559163, 26.022237], [89.550689, 25.995055], [89.551825, 25.981516], [89.549087, 25.968855], [89.541697, 25.959709], [89.528416, 25.957125], [89.518184, 25.962086], [89.509451, 25.972628], [89.495343, 25.995055], [89.490641, 25.997329], [89.485628, 25.998724], [89.480615, 25.999241], [89.426407, 25.995055], [89.426355, 25.994952], [89.421187, 25.993195], [89.416123, 25.992626], [89.411266, 25.993195], [89.406615, 25.995055], [89.391422, 26.015467], [89.379743, 26.015312], [89.367754, 26.005442], [89.351476, 25.996864], [89.335301, 25.998569], [89.315147, 26.006786], [89.296906, 26.018155], [89.272979, 26.043476], [89.237633, 26.0581], [89.229261, 26.067196], [89.22306, 26.086574], [89.212621, 26.098977], [89.197842, 26.10771], [89.131593, 26.133755], [89.11919, 26.146622], [89.111697, 26.164502], [89.103377, 26.22517], [89.095884, 26.241035], [89.075885, 26.271059], [89.069426, 26.287389], [89.068857, 26.314261], [89.08033, 26.315604], [89.094799, 26.309506], [89.103532, 26.313692], [89.102912, 26.327335], [89.095729, 26.334518], [89.071855, 26.340616], [89.055215, 26.347023], [89.054078, 26.353948], [89.057282, 26.36263], [89.052631, 26.374619], [89.043743, 26.381078], [89.02178, 26.386246], [89.010877, 26.390173], [89.001781, 26.39715], [88.986537, 26.412704], [88.975323, 26.417872], [88.959303, 26.428569], [88.950157, 26.43694], [88.94101, 26.439421], [88.924577, 26.432135], [88.911968, 26.421231], [88.899462, 26.404488], [88.891246, 26.385781], [88.891866, 26.368779], [88.912433, 26.348935], [88.941217, 26.340099], [88.966021, 26.328472], [88.975323, 26.299946], [88.981731, 26.291833], [88.989689, 26.289404], [88.998629, 26.291523], [89.007983, 26.297156], [89.004365, 26.27571], [89.0116, 26.269199], [89.023537, 26.268837], [89.034338, 26.265788], [89.039712, 26.247185], [89.019455, 26.234472], [88.975323, 26.224344], [88.946849, 26.232922], [88.902718, 26.272919], [88.875794, 26.277364], [88.855641, 26.265116], [88.840654, 26.232043], [88.824841, 26.226566], [88.807995, 26.233439], [88.798486, 26.24765], [88.792027, 26.2646], [88.784017, 26.279947], [88.747947, 26.292505], [88.663404, 26.264444], [88.645834, 26.27602], [88.653276, 26.2831], [88.690121, 26.299843], [88.702988, 26.309661], [88.712238, 26.328265], [88.709706, 26.340616], [88.69627, 26.350279], [88.673378, 26.360718], [88.667435, 26.368159], [88.66852, 26.385729], [88.661079, 26.388881], [88.652242, 26.391517], [88.649865, 26.39839], [88.651932, 26.407071], [88.656273, 26.415133], [88.633018, 26.420094], [88.622373, 26.424538], [88.611521, 26.430843], [88.603563, 26.440196], [88.597878, 26.451461], [88.591161, 26.461228], [88.579172, 26.466034], [88.557984, 26.465724], [88.549768, 26.466654], [88.526823, 26.475388], [88.506825, 26.487635], [88.49866, 26.49487], [88.487911, 26.505773], [88.475405, 26.514972], [88.462383, 26.528821], [88.446467, 26.535539], [88.411016, 26.545099], [88.397891, 26.55869], [88.394997, 26.578379], [88.395927, 26.599566], [88.394377, 26.618015], [88.385282, 26.623544], [88.372518, 26.611607], [88.36027, 26.593107], [88.352829, 26.578689], [88.332365, 26.514662], [88.319342, 26.494921], [88.313865, 26.481537], [88.315105, 26.465052], [88.322546, 26.451772], [88.335621, 26.448206], [88.34022, 26.454149], [88.34301, 26.477196], [88.346524, 26.484018], [88.353552, 26.484586], [88.37691, 26.475336], [88.434581, 26.465156], [88.461143, 26.45389], [88.475405, 26.432031], [88.495456, 26.378081], [88.497626, 26.352708], [88.475405, 26.355808], [88.467912, 26.354051], [88.45799, 26.353018], [88.448637, 26.353431], [88.442022, 26.356118], [88.431687, 26.362216], [88.426003, 26.357927], [88.421197, 26.348729], [88.413807, 26.340202], [88.366781, 26.313434], [88.351692, 26.30098], [88.336602, 26.281808], [88.332933, 26.267235], [88.334535, 26.230803], [88.326267, 26.21592], [88.308697, 26.206102], [88.250303, 26.188015], [88.229787, 26.184036], [88.218884, 26.18016], [88.211959, 26.173752], [88.205654, 26.166414], [88.196921, 26.159645], [88.163796, 26.140473], [88.155632, 26.128484], [88.144469, 26.095876], [88.141369, 26.090502], [88.139922, 26.085334], [88.141162, 26.075464], [88.14478, 26.070503], [88.150981, 26.066989], [88.156407, 26.061408], [88.157905, 26.050452], [88.151032, 26.031332], [88.125866, 26.0134], [88.106539, 25.979966], [88.088039, 25.923225], [88.082664, 25.915939], [88.077393, 25.912786], [88.074396, 25.908135], [88.082044, 25.863849], [88.088969, 25.848656], [88.087574, 25.839974], [88.087574, 25.830983], [88.103335, 25.814808], [88.108038, 25.806281], [88.114911, 25.787264], [88.127726, 25.774914], [88.146433, 25.77574], [88.166225, 25.783647], [88.18271, 25.792432], [88.227462, 25.804266], [88.259915, 25.789021], [88.320273, 25.725253], [88.392981, 25.680759], [88.41944, 25.653784], [88.423781, 25.592237], [88.437785, 25.579835], [88.475405, 25.562575], [88.485586, 25.542628], [88.512767, 25.523714], [88.520467, 25.509297], [88.530286, 25.500202], [88.550956, 25.496481], [88.593589, 25.495086], [88.613175, 25.486404], [88.645421, 25.467336], [88.66635, 25.462891], [88.67808, 25.465889], [88.687072, 25.473898], [88.69565, 25.483614], [88.706192, 25.491675], [88.71415, 25.492812], [88.723142, 25.491417], [88.732651, 25.491003], [88.742572, 25.495189], [88.741952, 25.512759], [88.772338, 25.501959], [88.780968, 25.495189], [88.793112, 25.48196], [88.805824, 25.471676], [88.814919, 25.458602], [88.816315, 25.437208], [88.799313, 25.401706], [88.799623, 25.395609], [88.810372, 25.388426], [88.813266, 25.380054], [88.814041, 25.370391], [88.81833, 25.359642], [88.841998, 25.332822], [88.849026, 25.327189], [88.859258, 25.324709], [88.880187, 25.323468], [88.88613, 25.319076], [88.902873, 25.303418], [88.926489, 25.300472], [88.975323, 25.302643], [88.982713, 25.294478], [88.984935, 25.284969], [88.982351, 25.274892], [88.975323, 25.265229], [88.937754, 25.240166], [88.929073, 25.229417], [88.927988, 25.21593], [88.930778, 25.202649], [88.927884, 25.192727], [88.909642, 25.189161], [88.924163, 25.167871], [88.898635, 25.169318], [88.830216, 25.191332], [88.823601, 25.194846], [88.81709, 25.196809], [88.809132, 25.195259], [88.802517, 25.188128], [88.792337, 25.166579], [88.784224, 25.160946], [88.724537, 25.166475], [88.664593, 25.186888], [88.634259, 25.192262], [88.599119, 25.19314], [88.590127, 25.190143], [88.577725, 25.177482], [88.570697, 25.173245], [88.55938, 25.170764], [88.554677, 25.171126], [88.550026, 25.173142], [88.523154, 25.179808], [88.491838, 25.191125], [88.475405, 25.195104], [88.441299, 25.18973], [88.43148, 25.173038], [88.434168, 25.116659], [88.425796, 25.051056], [88.414117, 25.021523], [88.391018, 24.99509], [88.387865, 24.968761], [88.37567, 24.94561], [88.340892, 24.90414], [88.322753, 24.874684], [88.313606, 24.868302], [88.296812, 24.869698], [88.25485, 24.879955], [88.242758, 24.880601], [88.24188, 24.898766], [88.232836, 24.918273], [88.21878, 24.935146], [88.20245, 24.9453], [88.18302, 24.94698], [88.16452, 24.941114], [88.114911, 24.916413], [88.114911, 24.912021], [88.120337, 24.906775], [88.124936, 24.898145], [88.138062, 24.863496], [88.140955, 24.844583], [88.13124, 24.831767], [88.117391, 24.819261], [88.107779, 24.801097], [88.094808, 24.803112], [88.088245, 24.796136], [88.085352, 24.784018], [88.084008, 24.770453], [88.07698, 24.761177], [88.064268, 24.759162], [88.053209, 24.754976], [88.048455, 24.711206], [88.035432, 24.693817], [88.027164, 24.67573], [88.02179, 24.645603], [88.04091, 24.640435], [88.05724, 24.634105], [88.067265, 24.613667], [88.081838, 24.599869], [88.08499, 24.593875], [88.08499, 24.541733], [88.086023, 24.53672], [88.090002, 24.533], [88.098787, 24.522484], [88.107934, 24.507963], [88.10995, 24.500986], [88.138785, 24.49525], [88.401301, 24.369418], [88.475405, 24.315468], [88.49835, 24.310972], [88.5631, 24.308207], [88.612451, 24.292885], [88.634517, 24.294125], [88.649503, 24.315364], [88.660149, 24.338257], [88.681801, 24.33389], [88.714564, 24.315416], [88.737508, 24.287097], [88.743038, 24.247539], [88.74495, 24.243379], [88.747482, 24.226972], [88.749807, 24.223251], [88.746397, 24.214079], [88.734821, 24.198421], [88.731617, 24.189842], [88.731456, 24.189771], [88.715907, 24.182918], [88.69379, 24.175321], [88.683765, 24.166536], [88.67808, 24.154289], [88.678287, 24.144651], [88.68299, 24.124291], [88.681801, 24.112664], [88.675393, 24.091838], [88.674101, 24.08264], [88.67901, 24.071219], [88.697821, 24.056259], [88.700921, 24.04365], [88.725002, 24.038379], [88.725468, 24.028896], [88.71384, 24.014685], [88.701593, 23.995281], [88.708363, 23.98355], [88.714357, 23.964404], [88.717148, 23.943139], [88.71415, 23.925156], [88.704435, 23.913709], [88.666505, 23.8795], [88.653121, 23.869784], [88.633949, 23.866141], [88.613175, 23.867847], [88.593589, 23.866916], [88.577621, 23.85516], [88.574366, 23.845626], [88.575451, 23.836556], [88.577621, 23.827616], [88.577518, 23.818521], [88.57235, 23.806894], [88.557674, 23.785681], [88.552713, 23.774984], [88.552507, 23.765424], [88.559741, 23.750283], [88.561188, 23.741007], [88.540104, 23.649953], [88.541551, 23.638765], [88.550956, 23.634424], [88.56124, 23.632486], [88.566356, 23.62874], [88.564392, 23.616208], [88.561085, 23.608612], [88.561705, 23.602023], [88.57142, 23.592334], [88.581549, 23.588329], [88.601289, 23.590344], [88.609247, 23.588794], [88.624233, 23.573859], [88.647643, 23.535076], [88.664489, 23.518307], [88.69379, 23.499807], [88.704642, 23.495182], [88.719421, 23.468104], [88.731927, 23.472625], [88.743709, 23.489317], [88.756835, 23.498799], [88.770168, 23.488826], [88.767894, 23.467277], [88.719266, 23.348292], [88.69503, 23.312609], [88.685935, 23.293308], [88.686607, 23.271604], [88.69658, 23.252354], [88.710016, 23.241218], [88.775697, 23.221633], [88.786187, 23.221529], [88.794197, 23.224966], [88.800967, 23.230754], [88.809545, 23.236051], [88.822671, 23.237963], [88.839621, 23.23481], [88.909642, 23.212434], [88.918066, 23.208584], [88.926954, 23.206517], [88.954239, 23.207422], [88.959975, 23.202693], [88.95982, 23.194451], [88.954704, 23.183806], [88.945868, 23.174349], [88.919099, 23.153859], [88.864787, 23.100271], [88.8513, 23.07513], [88.848716, 23.023841], [88.839414, 22.995368], [88.838794, 22.975808], [88.842773, 22.964568], [88.861738, 22.942399], [88.872177, 22.926767], [88.893468, 22.879638], [88.905147, 22.866435], [88.933103, 22.858115], [88.944524, 22.848012], [88.946798, 22.834525], [88.945144, 22.815094], [88.941217, 22.795664], [88.936876, 22.782331], [88.926231, 22.767035], [88.914965, 22.7544], [88.908144, 22.740784], [88.910573, 22.722593], [88.92251, 22.693706], [88.92592, 22.680839], [88.930003, 22.658618], [88.942354, 22.637482], [88.952017, 22.611903], [88.95641, 22.584979], [88.950312, 22.561983], [88.927988, 22.548134], [88.959045, 22.536817], [88.970982, 22.527928], [88.979974, 22.490928], [88.998371, 22.451241], [89.002505, 22.427986], [89.000748, 22.420131], [88.99217, 22.406954], [88.989482, 22.39667], [88.989586, 22.386903], [89.001523, 22.333677], [89.006949, 22.321429], [89.023537, 22.294816], [89.00912, 22.285618], [89.018628, 22.264689], [89.062295, 22.211565], [89.070718, 22.194822], [89.076196, 22.17503], [89.078159, 22.150845], [89.060693, 22.130485], [89.060395, 22.129869], [89.060313, 22.130601], [89.043712, 22.137397], [89.048595, 22.122952], [89.045665, 22.112047], [89.039887, 22.100898], [89.036876, 22.085598], [89.04005, 22.069525], [89.054047, 22.043647], [89.057384, 22.031236], [89.065603, 21.967108], [89.060883, 21.938463], [89.036876, 21.925116], [89.023123, 21.931098], [89.013031, 21.929429], [89.008067, 21.920559], [89.009532, 21.904608], [88.993012, 21.912665], [88.96046, 21.934312], [88.944672, 21.938788], [88.937836, 21.948717], [88.934581, 21.969306], [88.928722, 21.986558], [88.91391, 21.986558], [88.90504, 21.969428], [88.910818, 21.946479], [88.924083, 21.926337], [88.937836, 21.91767], [88.951915, 21.913886], [88.968272, 21.904731], [88.99586, 21.884182], [89.01588, 21.853339], [89.030935, 21.770453], [89.043712, 21.733344], [89.047862, 21.728013], [89.067882, 21.709133], [89.082774, 21.678209], [89.085216, 21.674709], [89.088227, 21.633734], [89.082693, 21.617336], [89.06422, 21.609198], [89.053233, 21.609809], [89.044688, 21.613511], [89.038829, 21.619452], [89.036876, 21.626899], [89.033946, 21.633246], [89.027599, 21.624579], [89.021658, 21.611802], [89.019786, 21.606106], [89.000255, 21.60342], [88.975759, 21.615912], [88.933849, 21.643948], [88.896983, 21.650133], [88.891612, 21.65351], [88.886078, 21.661811], [88.881602, 21.672024], [88.872081, 21.74901], [88.866059, 21.766832], [88.858653, 21.766832], [88.855724, 21.748847], [88.841563, 21.716254], [88.838227, 21.701972], [88.842947, 21.684149], [88.859874, 21.652574], [88.858653, 21.636542], [88.844249, 21.622219], [88.823985, 21.617255], [88.805837, 21.62287], [88.797862, 21.640204], [88.814789, 21.653876], [88.81837, 21.65762], [88.818533, 21.665839], [88.816661, 21.675035], [88.810883, 21.691718], [88.804942, 21.671861], [88.800466, 21.661851], [88.7942, 21.65762], [88.784028, 21.655951], [88.779063, 21.65119], [88.77711, 21.643541], [88.776866, 21.63345], [88.781749, 21.614244], [88.791026, 21.598863], [88.796641, 21.584418], [88.790538, 21.568264], [88.762462, 21.555854], [88.727875, 21.559882], [88.70753, 21.578315], [88.722179, 21.609198], [88.687999, 21.678127], [88.684906, 21.697211], [88.703624, 21.810614], [88.698009, 21.825507], [88.694509, 21.83983], [88.714692, 21.897854], [88.71697, 21.951972], [88.722179, 21.966132], [88.733165, 21.9772], [88.746837, 21.987128], [88.758311, 21.999457], [88.763031, 22.017279], [88.754649, 22.037584], [88.717052, 22.058051], [88.708507, 22.072211], [88.714854, 22.086982], [88.730154, 22.103583], [88.763031, 22.130561], [88.743907, 22.12934], [88.726573, 22.121161], [88.712169, 22.109036], [88.701671, 22.095852], [88.694835, 22.081529], [88.692882, 22.065863], [88.699474, 22.053209], [88.718516, 22.048041], [88.730724, 22.037177], [88.723318, 22.013088], [88.701671, 21.97602], [88.698416, 21.962592], [88.689952, 21.94599], [88.678722, 21.936672], [88.667003, 21.945014], [88.656261, 21.941067], [88.642589, 21.940904], [88.63087, 21.945461], [88.625987, 21.955512], [88.627452, 21.970404], [88.631684, 21.979804], [88.646332, 22.00023], [88.653494, 22.018012], [88.652599, 22.031806], [88.648774, 22.046047], [88.64503, 22.075141], [88.639496, 22.097235], [88.639496, 22.109524], [88.656993, 22.151068], [88.674001, 22.162055], [88.683116, 22.184963], [88.679942, 22.204413], [88.660167, 22.205024], [88.661143, 22.198961], [88.656016, 22.183254], [88.649669, 22.169094], [88.646332, 22.167792], [88.644298, 22.162299], [88.634939, 22.144965], [88.63087, 22.1258], [88.622081, 22.116034], [88.619151, 22.109524], [88.618337, 22.102973], [88.619965, 22.078315], [88.625499, 22.059272], [88.625987, 22.048041], [88.620372, 22.029975], [88.60255, 22.006415], [88.598643, 21.990302], [88.600271, 21.947211], [88.598399, 21.929348], [88.567068, 21.850816], [88.564626, 21.832343], [88.56544, 21.771186], [88.564626, 21.766832], [88.581309, 21.769436], [88.604177, 21.778754], [88.624034, 21.778022], [88.632823, 21.750067], [88.630219, 21.731757], [88.623383, 21.710273], [88.612641, 21.697496], [88.598643, 21.70539], [88.591807, 21.70539], [88.59962, 21.637274], [88.595063, 21.612128], [88.578136, 21.62344], [88.578136, 21.571682], [88.572276, 21.557929], [88.55893, 21.555569], [88.54477, 21.561998], [88.536469, 21.5751], [88.530284, 21.564399], [88.529633, 21.553371], [88.534434, 21.542914], [88.543956, 21.534084], [88.51295, 21.524644], [88.493907, 21.534817], [88.487966, 21.556545], [88.495616, 21.581855], [88.502126, 21.589342], [88.5088, 21.594387], [88.514008, 21.600816], [88.516124, 21.612616], [88.516612, 21.62641], [88.518809, 21.639146], [88.52296, 21.649888], [88.529633, 21.65762], [88.509532, 21.712307], [88.504893, 21.741523], [88.512706, 21.763414], [88.532888, 21.795315], [88.531016, 21.824123], [88.51588, 21.850816], [88.495616, 21.876695], [88.514659, 21.916205], [88.514985, 21.937201], [88.495616, 21.951809], [88.499848, 21.933661], [88.492361, 21.922024], [88.481212, 21.909898], [88.474457, 21.890326], [88.477061, 21.871812], [88.492035, 21.837144], [88.495616, 21.818671], [88.4935, 21.805162], [88.488292, 21.803656], [88.481456, 21.805162], [88.474457, 21.800971], [88.468272, 21.777248], [88.457693, 21.758857], [88.45338, 21.741767], [88.452159, 21.722805], [88.454763, 21.70539], [88.468598, 21.677151], [88.472179, 21.663275], [88.468272, 21.643948], [88.456309, 21.62401], [88.441905, 21.616156], [88.426117, 21.611029], [88.409679, 21.59927], [88.393077, 21.600246], [88.383556, 21.631415], [88.382823, 21.671332], [88.392589, 21.698554], [88.380626, 21.696194], [88.364513, 21.686957], [88.355724, 21.684963], [88.346202, 21.688625], [88.340668, 21.697496], [88.336436, 21.708726], [88.331228, 21.719062], [88.326345, 21.711086], [88.324718, 21.701361], [88.327403, 21.691107], [88.345714, 21.6706], [88.349457, 21.660956], [88.348806, 21.650295], [88.34547, 21.636542], [88.33546, 21.61579], [88.321788, 21.605658], [88.309744, 21.610175], [88.304454, 21.63345], [88.302013, 21.745185], [88.298188, 21.764106], [88.289806, 21.780219], [88.273123, 21.797919], [88.261567, 21.798651], [88.264171, 21.775295], [88.295421, 21.676581], [88.297048, 21.653876], [88.294444, 21.642808], [88.28419, 21.621243], [88.283376, 21.609198], [88.287608, 21.600287], [88.300629, 21.589342], [88.304454, 21.581855], [88.305837, 21.571682], [88.303722, 21.568793], [88.297862, 21.566555], [88.28712, 21.558295], [88.278982, 21.555162], [88.266124, 21.55386], [88.254405, 21.554674], [88.249278, 21.558295], [88.245942, 21.579901], [88.237478, 21.601711], [88.224376, 21.613023], [88.208263, 21.602973], [88.189464, 21.641791], [88.181895, 21.665229], [88.180919, 21.684963], [88.19044, 21.704088], [88.203461, 21.724351], [88.21046, 21.74726], [88.201427, 21.774319], [88.209727, 21.78856], [88.203787, 21.805406], [88.154063, 21.888088], [88.156423, 21.897854], [88.159434, 21.906155], [88.146739, 21.959215], [88.149913, 21.976386], [88.156261, 21.990871], [88.164317, 22.003119], [88.206798, 22.053371], [88.222423, 22.077948], [88.212738, 22.100043], [88.20338, 22.15176], [88.198009, 22.167792], [88.165294, 22.185004], [88.118419, 22.200181], [88.081309, 22.217353], [88.077322, 22.240424], [88.059744, 22.229478], [88.043468, 22.224758], [88.027029, 22.227851], [88.009044, 22.240424], [87.985118, 22.265937], [87.972667, 22.283189], [87.967459, 22.298489], [87.964203, 22.319281], [87.950694, 22.356187], [87.94752, 22.373847], [87.946137, 22.394924], [87.94044, 22.413031], [87.927989, 22.424221], [87.906586, 22.42475], [87.906586, 22.418606], [87.922862, 22.397773], [87.935802, 22.35517], [87.94752, 22.277655], [87.95281, 22.262437], [87.965343, 22.244818], [87.991954, 22.215888], [88.008474, 22.204901], [88.025401, 22.200914], [88.057384, 22.198879], [88.09547, 22.19123], [88.132579, 22.179145], [88.160981, 22.156562], [88.173513, 22.116889], [88.164317, 22.089789], [88.140636, 22.060126], [88.111176, 22.034857], [88.084727, 22.020697], [88.048595, 22.020738], [88.043142, 22.017279], [88.045177, 22.006537], [88.049001, 21.995429], [88.050792, 21.985012], [88.046886, 21.97602], [88.0324, 21.95718], [87.983653, 21.854193], [87.97047, 21.836493], [87.957774, 21.828925], [87.948009, 21.825385], [87.909028, 21.798814], [87.902192, 21.79682], [87.899181, 21.794827], [87.895681, 21.789374], [87.894786, 21.778754], [87.892914, 21.774319], [87.867198, 21.7508], [87.833995, 21.729315], [87.755707, 21.691718], [87.703787, 21.655504], [87.683849, 21.650133], [87.6421, 21.652086], [87.621349, 21.650621], [87.477387, 21.613593], [87.468761, 21.611396], [87.404959, 21.586656], [87.341807, 21.562405], [87.293468, 21.553656], [87.256114, 21.561998], [87.245942, 21.556586], [87.235199, 21.554511], [87.211436, 21.554592], [87.200938, 21.551988], [87.113048, 21.507961], [87.063731, 21.475002], [86.917654, 21.327379], [86.866954, 21.256049], [86.83725, 21.174709], [86.844005, 21.082221], [86.952159, 20.849107], [86.965668, 20.805976], [86.953868, 20.780585], [86.93629, 20.787502], [86.916515, 20.788072], [86.878754, 20.780585], [86.889334, 20.764472], [86.910655, 20.756415], [86.957205, 20.753241], [86.979991, 20.755561], [86.986583, 20.751858], [86.994884, 20.738959], [87.000011, 20.728217], [87.001801, 20.71898], [86.998709, 20.711127], [86.988617, 20.70425], [86.975759, 20.710679], [86.961192, 20.712836], [86.948253, 20.709052], [86.940196, 20.697984], [86.951915, 20.697008], [86.961192, 20.695054], [86.968516, 20.691148], [86.974294, 20.684394], [86.9817, 20.68537], [86.986501, 20.684394], [86.990245, 20.679999], [86.994884, 20.670722], [87.019379, 20.680365], [87.033376, 20.690172], [87.043224, 20.697984], [87.027517, 20.679674], [87.003591, 20.65705], [86.976248, 20.637885], [86.923188, 20.621731], [86.774181, 20.511949], [86.751475, 20.489163], [86.733897, 20.462226], [86.72462, 20.431952], [86.72047, 20.368476], [86.741954, 20.379543], [86.756358, 20.393744], [86.769786, 20.400702], [86.788748, 20.390123], [86.788259, 20.402818], [86.789806, 20.4147], [86.796153, 20.437323], [86.809825, 20.415107], [86.7942, 20.37816], [86.768321, 20.343451], [86.751475, 20.32807], [86.739268, 20.324205], [86.719737, 20.31509], [86.701671, 20.303778], [86.693696, 20.293931], [86.701671, 20.284654], [86.719737, 20.291734], [86.754649, 20.314439], [86.717459, 20.286933], [86.628184, 20.235256], [86.583832, 20.218207], [86.53419, 20.206122], [86.511485, 20.197455], [86.495128, 20.184068], [86.490408, 20.174058], [86.478852, 20.12934], [86.468516, 20.10928], [86.467133, 20.094672], [86.41505, 20.036322], [86.391449, 20.020209], [86.396739, 20.009955], [86.404145, 20.002631], [86.414073, 19.998847], [86.42628, 19.999172], [86.397227, 19.982856], [86.348399, 20.008857], [86.275401, 20.067369], [86.231293, 20.064887], [86.209158, 20.066636], [86.199718, 20.077948], [86.195974, 20.080308], [86.173025, 20.1022], [86.168468, 20.113186], [86.163097, 20.134833], [86.158702, 20.143134], [86.152517, 20.143134], [86.14503, 20.122016], [86.154307, 20.089993], [86.158702, 20.081041], [86.167735, 20.072943], [86.187755, 20.063666], [86.213715, 20.042426], [86.230235, 20.035305], [86.268565, 20.026435], [86.273448, 20.02619], [86.284679, 20.027493], [86.289073, 20.026435], [86.293224, 20.02143], [86.294119, 20.015692], [86.2942, 20.010403], [86.295909, 20.006578], [86.302908, 19.999091], [86.308116, 19.992092], [86.314952, 19.986884], [86.326671, 19.984849], [86.341563, 19.979885], [86.356456, 19.970404], [86.372406, 19.964993], [86.268565, 19.910346], [86.111583, 19.855699], [86.089041, 19.844224], [86.07838, 19.843573], [86.066742, 19.851996], [86.056651, 19.856879], [86.04656, 19.852525], [86.037934, 19.845282], [86.032074, 19.841498], [86.01352, 19.839057], [85.888845, 19.802965], [85.846365, 19.795844], [85.808604, 19.773383], [85.788829, 19.765692], [85.778087, 19.764635], [85.744395, 19.765692], [85.734386, 19.763617], [85.714854, 19.754218], [85.684581, 19.747748], [85.624278, 19.718573], [85.625987, 19.722886], [85.635997, 19.72663], [85.63795, 19.731635], [85.614268, 19.727932], [85.549164, 19.690619], [85.480479, 19.666327], [85.456798, 19.663316], [85.44988, 19.666693], [85.44752, 19.674628], [85.446788, 19.690619], [85.443126, 19.69245], [85.43686, 19.691352], [85.430431, 19.691718], [85.42628, 19.697455], [85.427013, 19.704088], [85.431651, 19.704901], [85.43686, 19.704657], [85.439301, 19.707994], [85.450857, 19.716295], [85.47755, 19.723863], [85.521983, 19.739081], [85.525564, 19.734036], [85.523448, 19.728705], [85.518891, 19.721584], [85.514985, 19.711127], [85.558849, 19.738105], [85.576508, 19.745836], [85.567638, 19.7508], [85.563243, 19.756903], [85.564138, 19.76439], [85.570323, 19.773179], [85.567149, 19.791653], [85.570323, 19.875637], [85.55714, 19.881781], [85.514985, 19.889309], [85.476817, 19.901028], [85.463552, 19.902899], [85.440115, 19.896226], [85.41627, 19.879788], [85.374522, 19.837795], [85.319591, 19.793647], [85.30421, 19.79267], [85.290294, 19.789944], [85.278087, 19.78559], [85.268077, 19.780015], [85.246837, 19.762641], [85.220958, 19.736029], [85.20338, 19.707261], [85.206554, 19.683783], [85.189952, 19.670966], [85.175141, 19.636908], [85.165538, 19.6258], [85.150157, 19.613471], [85.140147, 19.596584], [85.139334, 19.579779], [85.151378, 19.567776], [85.151378, 19.560289], [85.139171, 19.557074], [85.122895, 19.549018], [85.108897, 19.538967], [85.102875, 19.529608], [85.104503, 19.514879], [85.108735, 19.506781], [85.115489, 19.507473], [85.124034, 19.519355], [85.128754, 19.509508], [85.130219, 19.498725], [85.128673, 19.487982], [85.124034, 19.478339], [85.135753, 19.481594], [85.141775, 19.487128], [85.145763, 19.493476], [85.151378, 19.498847], [85.162446, 19.503485], [85.171153, 19.504462], [85.178559, 19.504299], [85.185557, 19.505683], [85.202891, 19.517157], [85.201915, 19.527086], [85.1963, 19.538479], [85.199229, 19.554104], [85.206309, 19.548], [85.214529, 19.547268], [85.223643, 19.549954], [85.233246, 19.554104], [85.203787, 19.570502], [85.193044, 19.57392], [85.193044, 19.580797], [85.212901, 19.581122], [85.223643, 19.590237], [85.232595, 19.601549], [85.247569, 19.60871], [85.247569, 19.614936], [85.241873, 19.617011], [85.237315, 19.620551], [85.234386, 19.625678], [85.233246, 19.632554], [85.236339, 19.635321], [85.247813, 19.653144], [85.247569, 19.65648], [85.260753, 19.656073], [85.309093, 19.642239], [85.350597, 19.677639], [85.36378, 19.678046], [85.37436, 19.674709], [85.383067, 19.676093], [85.391449, 19.690619], [85.399181, 19.679145], [85.403168, 19.671291], [85.404959, 19.661933], [85.405284, 19.645941], [85.402192, 19.643988], [85.388682, 19.638251], [85.384613, 19.635972], [85.382172, 19.63109], [85.380382, 19.619615], [85.37794, 19.614936], [85.368419, 19.6081], [85.352306, 19.599433], [85.336762, 19.596096], [85.330089, 19.604967], [85.326427, 19.618801], [85.31837, 19.619534], [85.311046, 19.6105], [85.309093, 19.595038], [85.303477, 19.59748], [85.293793, 19.599555], [85.288422, 19.601264], [85.302501, 19.586656], [85.305837, 19.580146], [85.302908, 19.57392], [85.330577, 19.576402], [85.576508, 19.690619], [85.540294, 19.669582], [85.46339, 19.631049], [85.380219, 19.593817], [85.336681, 19.575181], [85.285411, 19.536282], [85.240571, 19.518052], [85.180431, 19.475002], [85.069509, 19.372138], [84.99586, 19.32331], [84.872081, 19.219875], [84.791026, 19.120592], [84.777761, 19.116197], [84.775157, 19.115302], [84.764415, 19.12344], [84.750662, 19.138373], [84.73878, 19.14765], [84.733653, 19.139146], [84.731212, 19.128811], [84.720958, 19.114244], [84.719981, 19.10163], [84.72283, 19.096015], [84.735362, 19.083197], [84.740489, 19.073676], [84.756847, 19.097642], [84.760997, 19.10163], [84.767833, 19.101793], [84.775238, 19.100491], [84.78004, 19.097073], [84.778331, 19.09101], [84.75115, 19.0539], [84.741059, 19.028144], [84.699474, 18.977484], [84.679535, 18.945705], [84.66505, 18.930121], [84.63087, 18.915717], [84.547862, 18.796088], [84.54184, 18.775784], [84.448253, 18.68065], [84.437185, 18.662787], [84.432628, 18.645697], [84.423676, 18.636461], [84.370453, 18.601304], [84.349457, 18.56981], [84.332856, 18.553697], [84.312266, 18.546698], [84.306326, 18.543687], [84.288585, 18.529771], [84.281749, 18.52558], [84.272716, 18.524319], [84.250011, 18.524237], [84.246918, 18.52558], [84.237641, 18.510932], [84.24936, 18.501654], [84.271007, 18.50019], [84.297699, 18.516303], [84.326182, 18.537421], [84.349946, 18.552924], [84.331228, 18.538642], [84.287934, 18.505764], [84.240082, 18.460761], [84.230479, 18.446234], [84.188324, 18.418036], [84.17449, 18.399848], [84.152843, 18.383612], [84.143891, 18.374823], [84.138927, 18.364163], [84.136485, 18.353583], [84.132823, 18.343411], [84.124034, 18.333808], [84.113129, 18.298285], [84.077403, 18.271552], [83.775157, 18.139594], [83.613943, 18.045152], [83.582286, 18.01911], [83.576182, 18.009752], [83.562836, 17.9831], [83.55836, 17.977525], [83.556651, 17.973456], [83.53891, 17.956285], [83.534434, 17.953315], [83.526134, 17.942532], [83.486583, 17.916897], [83.472911, 17.902493], [83.465587, 17.902493], [83.462901, 17.908515], [83.451915, 17.922919], [83.448985, 17.906887], [83.452403, 17.886217], [83.452973, 17.868476], [83.431163, 17.854234], [83.425304, 17.838609], [83.417735, 17.80622], [83.402354, 17.784491], [83.341319, 17.717434], [83.319102, 17.703192], [83.313243, 17.701158], [83.304373, 17.692206], [83.298595, 17.690131], [83.292735, 17.691148], [83.289561, 17.693264], [83.287446, 17.69538], [83.284679, 17.696357], [83.282237, 17.699286], [83.280284, 17.705959], [83.276378, 17.71308], [83.26824, 17.717475], [83.246918, 17.716254], [83.242035, 17.706529], [83.249278, 17.69538], [83.271983, 17.688137], [83.292491, 17.677476], [83.298595, 17.672797], [83.299083, 17.664293], [83.289236, 17.65762], [83.26824, 17.648586], [83.255138, 17.638129], [83.246918, 17.634223], [83.212901, 17.634914], [83.233897, 17.607733], [83.238943, 17.592963], [83.222911, 17.586493], [83.209239, 17.583686], [83.196137, 17.576809], [83.171397, 17.559801], [83.088227, 17.530707], [83.053233, 17.506496], [83.019867, 17.490912], [82.99643, 17.473456], [82.975841, 17.453681], [82.969574, 17.4501], [82.95281, 17.44595], [82.945486, 17.443101], [82.896821, 17.409329], [82.888682, 17.410142], [82.883311, 17.422065], [82.869151, 17.414008], [82.854259, 17.402818], [82.837657, 17.392768], [82.796397, 17.383043], [82.758149, 17.359198], [82.718516, 17.348619], [82.474864, 17.205959], [82.451915, 17.18244], [82.435069, 17.153795], [82.422862, 17.13996], [82.387706, 17.126939], [82.370372, 17.110093], [82.342052, 17.073188], [82.311697, 17.046617], [82.299327, 17.030341], [82.291677, 16.99844], [82.252696, 16.929185], [82.250743, 16.907864], [82.255138, 16.885321], [82.267914, 16.867499], [82.29835, 16.858466], [82.303559, 16.854193], [82.308116, 16.849555], [82.31422, 16.846625], [82.319591, 16.847235], [82.327159, 16.849433], [82.333344, 16.852118], [82.334646, 16.854071], [82.352061, 16.845404], [82.362478, 16.859076], [82.361176, 16.891343], [82.357432, 16.924872], [82.348888, 16.956488], [82.361176, 16.936021], [82.366547, 16.908637], [82.367442, 16.860297], [82.365571, 16.811591], [82.352061, 16.761298], [82.345958, 16.706122], [82.338145, 16.678412], [82.31422, 16.62816], [82.312348, 16.619859], [82.313813, 16.612982], [82.312836, 16.60814], [82.303966, 16.606431], [82.299327, 16.610093], [82.293793, 16.625637], [82.286876, 16.62816], [82.280935, 16.620836], [82.285492, 16.606513], [82.296072, 16.59101], [82.307384, 16.579779], [82.259044, 16.565497], [82.203461, 16.512681], [82.188243, 16.504055], [82.173025, 16.500922], [82.122895, 16.477362], [82.090668, 16.454779], [82.068614, 16.444281], [82.037364, 16.447659], [82.022716, 16.435736], [82.007823, 16.419582], [81.992686, 16.408433], [81.9817, 16.407864], [81.967947, 16.408922], [81.956228, 16.407172], [81.94809, 16.391547], [81.940766, 16.388373], [81.899913, 16.38581], [81.880544, 16.381659], [81.861664, 16.375393], [81.783214, 16.338568], [81.76295, 16.322577], [81.717621, 16.312201], [81.711599, 16.312201], [81.696056, 16.314927], [81.676931, 16.327379], [81.656261, 16.337348], [81.636404, 16.332709], [81.620942, 16.339504], [81.584972, 16.342108], [81.568126, 16.346381], [81.573904, 16.358344], [81.569184, 16.36758], [81.556977, 16.373033], [81.540212, 16.373684], [81.549083, 16.352362], [81.535981, 16.350043], [81.49586, 16.360663], [81.475352, 16.358629], [81.416677, 16.340155], [81.421153, 16.350979], [81.426117, 16.35871], [81.428233, 16.365668], [81.424083, 16.373684], [81.411388, 16.382514], [81.398936, 16.382636], [81.386974, 16.376776], [81.375743, 16.367499], [81.35963, 16.374335], [81.345225, 16.373603], [81.331554, 16.369778], [81.317393, 16.367499], [81.259613, 16.332709], [81.248546, 16.320136], [81.249848, 16.311021], [81.256033, 16.301825], [81.259613, 16.288642], [81.254161, 16.271308], [81.230317, 16.243598], [81.218516, 16.199449], [81.190278, 16.138861], [81.180024, 16.086737], [81.161957, 16.057074], [81.156016, 16.03852], [81.155447, 15.9855], [81.14975, 15.969631], [81.135102, 15.966498], [81.088227, 15.92121], [81.043305, 15.89411], [81.026866, 15.880845], [80.998871, 15.846747], [80.998383, 15.834703], [81.00294, 15.825263], [81.0088, 15.817816], [81.012543, 15.811957], [81.019216, 15.792385], [81.019054, 15.784166], [81.012543, 15.777818], [81.004731, 15.77851], [80.99586, 15.784329], [80.988292, 15.791205], [80.979015, 15.803412], [80.949474, 15.827867], [80.940684, 15.832994], [80.921641, 15.838609], [80.910004, 15.852688], [80.904307, 15.871283], [80.90268, 15.890815], [80.910167, 15.965888], [80.908946, 15.983466], [80.906016, 15.999823], [80.896495, 16.031073], [80.892263, 16.020941], [80.896495, 16.010565], [80.889008, 16.00373], [80.885753, 16.008694], [80.882823, 16.011461], [80.875336, 16.017401], [80.891287, 15.978258], [80.895763, 15.936021], [80.890147, 15.89525], [80.855724, 15.822699], [80.830251, 15.747626], [80.820079, 15.726874], [80.80714, 15.716376], [80.80836, 15.803941], [80.802745, 15.842434], [80.776134, 15.884263], [80.760102, 15.892971], [80.736339, 15.89761], [80.69044, 15.900702], [80.682465, 15.903266], [80.67628, 15.907172], [80.670177, 15.907701], [80.663097, 15.900702], [80.660411, 15.893948], [80.662852, 15.88935], [80.618419, 15.887681], [80.600108, 15.882229], [80.584321, 15.872545], [80.571137, 15.86872], [80.56072, 15.880845], [80.553884, 15.880845], [80.541677, 15.868069], [80.52589, 15.85692], [80.491873, 15.83926], [80.482677, 15.836412], [80.475597, 15.835924], [80.469086, 15.834662], [80.461436, 15.829657], [80.445486, 15.814399], [80.43629, 15.808295], [80.393321, 15.797024], [80.35963, 15.775824], [80.279633, 15.70067], [80.263194, 15.674547], [80.219493, 15.56745], [80.217459, 15.548163], [80.201345, 15.518297], [80.197602, 15.503079], [80.211192, 15.496649], [80.20045, 15.482164], [80.183849, 15.452297], [80.172862, 15.4383], [80.14031, 15.406399], [80.126801, 15.388129], [80.121267, 15.370022], [80.090587, 15.304877], [80.09254, 15.296576], [80.082367, 15.204088], [80.052989, 15.092597], [80.054535, 15.014146], [80.061534, 14.974677], [80.073416, 14.942043], [80.073009, 14.920559], [80.105154, 14.763861], [80.108165, 14.714342], [80.110688, 14.704779], [80.116954, 14.698147], [80.128184, 14.688463], [80.151866, 14.672797], [80.159841, 14.662665], [80.166352, 14.62816], [80.173595, 14.616523], [80.178477, 14.60578], [80.175955, 14.592963], [80.169688, 14.585842], [80.160899, 14.57982], [80.141856, 14.571234], [80.149181, 14.565009], [80.159353, 14.565741], [80.183279, 14.571112], [80.190196, 14.571234], [80.196951, 14.565009], [80.196056, 14.559027], [80.192393, 14.553209], [80.190196, 14.547593], [80.188243, 14.525702], [80.175955, 14.468736], [80.178722, 14.38939], [80.174978, 14.34455], [80.159434, 14.324774], [80.137055, 14.269924], [80.128184, 14.256537], [80.097911, 14.254299], [80.087169, 14.250312], [80.071788, 14.232245], [80.070079, 14.229193], [80.055024, 14.221584], [80.046641, 14.20661], [80.050629, 14.196112], [80.073497, 14.20185], [80.090994, 14.216742], [80.106619, 14.234036], [80.121104, 14.242011], [80.13559, 14.229193], [80.123383, 14.193508], [80.125824, 14.148505], [80.149669, 14.027737], [80.16391, 13.987982], [80.237153, 13.839667], [80.249278, 13.800279], [80.245453, 13.755601], [80.227387, 13.720526], [80.220958, 13.696967], [80.227712, 13.677069], [80.238048, 13.660224], [80.259044, 13.591742], [80.288097, 13.527086], [80.309581, 13.479315], [80.313731, 13.440904], [80.306895, 13.440904], [80.277761, 13.512519], [80.245453, 13.591742], [80.197276, 13.643622], [80.183604, 13.679185], [80.152517, 13.71894], [80.141856, 13.728949], [80.141775, 13.649115], [80.13559, 13.619086], [80.121267, 13.627265], [80.117686, 13.638861], [80.121267, 13.663764], [80.117849, 13.674506], [80.109874, 13.680732], [80.100841, 13.686265], [80.094005, 13.69477], [80.093598, 13.671332], [80.091319, 13.659125], [80.075531, 13.648383], [80.052989, 13.619086], [80.056488, 13.59516], [80.069509, 13.574408], [80.101329, 13.537095], [80.112071, 13.511542], [80.119395, 13.500678], [80.131847, 13.496161], [80.153087, 13.494208], [80.175955, 13.489325], [80.219737, 13.487779], [80.227712, 13.485582], [80.235118, 13.478095], [80.243419, 13.472154], [80.253429, 13.468655], [80.260183, 13.468451], [80.26588, 13.468248], [80.26238, 13.451239], [80.27475, 13.440009], [80.293142, 13.429145], [80.306895, 13.413031], [80.312266, 13.423163], [80.313731, 13.427232], [80.326996, 13.430365], [80.333995, 13.413031], [80.344005, 13.375434], [80.34783, 13.349066], [80.345551, 13.321234], [80.336274, 13.270941], [80.334158, 13.242906], [80.323741, 13.246894], [80.319835, 13.249172], [80.318044, 13.230902], [80.313731, 13.215033], [80.318207, 13.216986], [80.334158, 13.221177], [80.332774, 13.198147], [80.324229, 13.180365], [80.313813, 13.164293], [80.306895, 13.146064], [80.30714, 13.135647], [80.313731, 13.10578], [80.307953, 13.096015], [80.289724, 13.076728], [80.285818, 13.067572], [80.282725, 13.029364], [80.269054, 12.957261], [80.263194, 12.835354], [80.25172, 12.762519], [80.233653, 12.711005], [80.228689, 12.674994], [80.222179, 12.65998], [80.203868, 12.632148], [80.196462, 12.613715], [80.185069, 12.567125], [80.18336, 12.54682], [80.179698, 12.53262], [80.156016, 12.474514], [80.121267, 12.416815], [80.117035, 12.400946], [80.095958, 12.36225], [80.087169, 12.35102], [80.063731, 12.33747], [80.059825, 12.334215], [80.057465, 12.331244], [80.025727, 12.276516], [80.005138, 12.256049], [79.952159, 12.229926], [79.943858, 12.217841], [79.953136, 12.213568], [79.973806, 12.222398], [80.005138, 12.241767], [79.947602, 12.153632], [79.933604, 12.138739], [79.927501, 12.129869], [79.896983, 12.068752], [79.870128, 12.037421], [79.861827, 12.02204], [79.860525, 12.011542], [79.861827, 11.977973], [79.85963, 11.972724], [79.850352, 11.959133], [79.849539, 11.955064], [79.844981, 11.933254], [79.820323, 11.878648], [79.803233, 11.796088], [79.792735, 11.788072], [79.785899, 11.769192], [79.7671, 11.673651], [79.763438, 11.634263], [79.754161, 11.596991], [79.751964, 11.576972], [79.757335, 11.536078], [79.780772, 11.465074], [79.785981, 11.426174], [79.792979, 11.426174], [79.797374, 11.434882], [79.799164, 11.442206], [79.797618, 11.44831], [79.792979, 11.453518], [79.809337, 11.451361], [79.816661, 11.443061], [79.821951, 11.391059], [79.819347, 11.381496], [79.810313, 11.378892], [79.782725, 11.378892], [79.776052, 11.380357], [79.769298, 11.380927], [79.7588, 11.378363], [79.749848, 11.373684], [79.740408, 11.366441], [79.684337, 11.312201], [79.67628, 11.296454], [79.683116, 11.296454], [79.6963, 11.303412], [79.742035, 11.335598], [79.751964, 11.340522], [79.759288, 11.358344], [79.776378, 11.362982], [79.796886, 11.359036], [79.823741, 11.346666], [79.829926, 11.346096], [79.833018, 11.341986], [79.834972, 11.263861], [79.855317, 11.159857], [79.852224, 11.006985], [79.84669, 10.979844], [79.840668, 10.950141], [79.847341, 10.809638], [79.849864, 10.755194], [79.850271, 10.592108], [79.85963, 10.551459], [79.86378, 10.37873], [79.8567, 10.29328], [79.851329, 10.282213], [79.834321, 10.278754], [79.814626, 10.272406], [79.795095, 10.269232], [79.778575, 10.275377], [79.78004, 10.296617], [79.757091, 10.309068], [79.633149, 10.328599], [79.621267, 10.324164], [79.614106, 10.309556], [79.630382, 10.310492], [79.646495, 10.309638], [79.661794, 10.307074], [79.67628, 10.30272], [79.684581, 10.298733], [79.694672, 10.291653], [79.703624, 10.289049], [79.730805, 10.289049], [79.758962, 10.282213], [79.773692, 10.273668], [79.772472, 10.261705], [79.759776, 10.258246], [79.580089, 10.295885], [79.563487, 10.297105], [79.556651, 10.298651], [79.552745, 10.30272], [79.551768, 10.3133], [79.556895, 10.316067], [79.565196, 10.315741], [79.573904, 10.316962], [79.604015, 10.329088], [79.607107, 10.336005], [79.586925, 10.343695], [79.567719, 10.335883], [79.528575, 10.338324], [79.532237, 10.330024], [79.532237, 10.323188], [79.511485, 10.316596], [79.459727, 10.308295], [79.437266, 10.309556], [79.411957, 10.32099], [79.398123, 10.324652], [79.385509, 10.320136], [79.36378, 10.301907], [79.304942, 10.268134], [79.286143, 10.252834], [79.272227, 10.234442], [79.270274, 10.223334], [79.270681, 10.20954], [79.269216, 10.197821], [79.261974, 10.192857], [79.248302, 10.189195], [79.240001, 10.179755], [79.236339, 10.166571], [79.237478, 10.151923], [79.244314, 10.084947], [79.254161, 10.054267], [79.279063, 10.035834], [79.240733, 10.010565], [79.200369, 9.970282], [79.141856, 9.891181], [79.121349, 9.850246], [79.103526, 9.830146], [79.100841, 9.826077], [79.094086, 9.802314], [79.07781, 9.782172], [79.038829, 9.747219], [78.980805, 9.667182], [78.979828, 9.658637], [78.980235, 9.649115], [78.977306, 9.637926], [78.97283, 9.631293], [78.962087, 9.622707], [78.956798, 9.617499], [78.94752, 9.602851], [78.938324, 9.583686], [78.931488, 9.561672], [78.925304, 9.513617], [78.913259, 9.47427], [78.915294, 9.45303], [78.933442, 9.416449], [78.959727, 9.384955], [79.039887, 9.314683], [79.074718, 9.299872], [79.115571, 9.291246], [79.25172, 9.28852], [79.273774, 9.296698], [79.29713, 9.312649], [79.320079, 9.32331], [79.341075, 9.315863], [79.345714, 9.306545], [79.34254, 9.299018], [79.336681, 9.290473], [79.333669, 9.278266], [79.335704, 9.267646], [79.340587, 9.260932], [79.345225, 9.255927], [79.347341, 9.250312], [79.353526, 9.240912], [79.367931, 9.227607], [79.438243, 9.176337], [79.453624, 9.159003], [79.440278, 9.151353], [79.424001, 9.159084], [79.361013, 9.213446], [79.292735, 9.250312], [79.232432, 9.25788], [79.21697, 9.267401], [79.20102, 9.266506], [79.184093, 9.271796], [79.165375, 9.274237], [79.129405, 9.256334], [79.098643, 9.257229], [79.087169, 9.253811], [79.060802, 9.264594], [79.025727, 9.273749], [78.988943, 9.278266], [78.956798, 9.274848], [78.948985, 9.270494], [78.928966, 9.253811], [78.918468, 9.251614], [78.911306, 9.252143], [78.90447, 9.253567], [78.895518, 9.253811], [78.861013, 9.248969], [78.843761, 9.243964], [78.815196, 9.22842], [78.77711, 9.219387], [78.761567, 9.209703], [78.751964, 9.200588], [78.740733, 9.192816], [78.727794, 9.187445], [78.67628, 9.184149], [78.661306, 9.177191], [78.655121, 9.161566], [78.648936, 9.151435], [78.634776, 9.144599], [78.618826, 9.14053], [78.48585, 9.124823], [78.44988, 9.116604], [78.41212, 9.09984], [78.377452, 9.07746], [78.239757, 8.965562], [78.217621, 8.938951], [78.182791, 8.877102], [78.177908, 8.86107], [78.17449, 8.79442], [78.170421, 8.771877], [78.162364, 8.753485], [78.175792, 8.763902], [78.188731, 8.765448], [78.20045, 8.76968], [78.210216, 8.788275], [78.21518, 8.765448], [78.2046, 8.753323], [78.186534, 8.74494], [78.1692, 8.733629], [78.164806, 8.726508], [78.154959, 8.698879], [78.140473, 8.680609], [78.136729, 8.669867], [78.142589, 8.657904], [78.114594, 8.657904], [78.135509, 8.6258], [78.142345, 8.593085], [78.128266, 8.488023], [78.123302, 8.477525], [78.114594, 8.466132], [78.087169, 8.440497], [78.079845, 8.431952], [78.062755, 8.373196], [78.055919, 8.363715], [78.038829, 8.360907], [77.999766, 8.344916], [77.806163, 8.228664], [77.793468, 8.215318], [77.768321, 8.181545], [77.752452, 8.173814], [77.643809, 8.155707], [77.595225, 8.138983], [77.561046, 8.114569], [77.565278, 8.082506], [77.510916, 8.075995], [77.449718, 8.08747], [77.315603, 8.127753], [77.308604, 8.130927], [77.294688, 8.14704], [77.286794, 8.15351], [77.26295, 8.168402], [77.238943, 8.175116], [77.225271, 8.184394], [77.128754, 8.271796], [77.110362, 8.285183], [76.99586, 8.368109], [76.962576, 8.40469], [76.880138, 8.520697], [76.826508, 8.56981], [76.813731, 8.601264], [76.74171, 8.684516], [76.731293, 8.707953], [76.695649, 8.739814], [76.66033, 8.79975], [76.644542, 8.818996], [76.558604, 8.890692], [76.543956, 8.912502], [76.548106, 8.924221], [76.559093, 8.925238], [76.573253, 8.904446], [76.591156, 8.908149], [76.609874, 8.917955], [76.620616, 8.925442], [76.60727, 8.925767], [76.593761, 8.921047], [76.581554, 8.919908], [76.572276, 8.931627], [76.573985, 8.942572], [76.582286, 8.956366], [76.593272, 8.968207], [76.603282, 8.973212], [76.657563, 8.966376], [76.660818, 8.969875], [76.65211, 8.977281], [76.640391, 8.984442], [76.634288, 8.986884], [76.641938, 8.99136], [76.656505, 8.995063], [76.668468, 8.999823], [76.668468, 9.007392], [76.657888, 9.010688], [76.644054, 9.00788], [76.632091, 9.002346], [76.62672, 8.997463], [76.620616, 8.987128], [76.606456, 8.989407], [76.582774, 9.000556], [76.576834, 8.994127], [76.565929, 8.963284], [76.558604, 8.952094], [76.551117, 8.966376], [76.558116, 8.973049], [76.561534, 8.97899], [76.56544, 8.99433], [76.549653, 8.984442], [76.538829, 8.972602], [76.534434, 8.957831], [76.538097, 8.939114], [76.528087, 8.946967], [76.521495, 8.96133], [76.517914, 8.977444], [76.516856, 8.990627], [76.513845, 9.002143], [76.500173, 9.022284], [76.489268, 9.055569], [76.46046, 9.114936], [76.462413, 9.138332], [76.464529, 9.132514], [76.466645, 9.128974], [76.468272, 9.124579], [76.469086, 9.116604], [76.476573, 9.116604], [76.476248, 9.131252], [76.482758, 9.17182], [76.473318, 9.164252], [76.470958, 9.160142], [76.469086, 9.151353], [76.462413, 9.151353], [76.44337, 9.212144], [76.428233, 9.245347], [76.414561, 9.246975], [76.416026, 9.236314], [76.446788, 9.159613], [76.448741, 9.145168], [76.442556, 9.145168], [76.362966, 9.325181], [76.311778, 9.492987], [76.291026, 9.633205], [76.29713, 9.658433], [76.277354, 9.809272], [76.253184, 9.883734], [76.246755, 9.893744], [76.240245, 9.939399], [76.236339, 9.952704], [76.251475, 9.963121], [76.269542, 9.950995], [76.284679, 9.928534], [76.291026, 9.908596], [76.285655, 9.904364], [76.262218, 9.911566], [76.256847, 9.908596], [76.25766, 9.895819], [76.26059, 9.884711], [76.266856, 9.876044], [76.277354, 9.870754], [76.278575, 9.876899], [76.28419, 9.891181], [76.289073, 9.877143], [76.288259, 9.861233], [76.284434, 9.848212], [76.280772, 9.842841], [76.289806, 9.843817], [76.299978, 9.847805], [76.304698, 9.856269], [76.29713, 9.870754], [76.308604, 9.879625], [76.313813, 9.861314], [76.338878, 9.699408], [76.346202, 9.699408], [76.347341, 9.726874], [76.337576, 9.789252], [76.346202, 9.816107], [76.323985, 9.846177], [76.320079, 9.86164], [76.332693, 9.870754], [76.337413, 9.856187], [76.353038, 9.82982], [76.359223, 9.816107], [76.361095, 9.80386], [76.358653, 9.781806], [76.359223, 9.768297], [76.368012, 9.715562], [76.377126, 9.695502], [76.394054, 9.678941], [76.392426, 9.662828], [76.386567, 9.649848], [76.379161, 9.637763], [76.372895, 9.624335], [76.353282, 9.535346], [76.357432, 9.520697], [76.37672, 9.514472], [76.425792, 9.505276], [76.435069, 9.507636], [76.458995, 9.497301], [76.477061, 9.50373], [76.503266, 9.53498], [76.484386, 9.543769], [76.466563, 9.556383], [76.448497, 9.563422], [76.428233, 9.555406], [76.417247, 9.561428], [76.406993, 9.569078], [76.419688, 9.594184], [76.419607, 9.614488], [76.41505, 9.636176], [76.414561, 9.682685], [76.410004, 9.700263], [76.402599, 9.710598], [76.394054, 9.706244], [76.3921, 9.716946], [76.394786, 9.735175], [76.394054, 9.747219], [76.390798, 9.756985], [76.382091, 9.774726], [76.380382, 9.785102], [76.384044, 9.802639], [76.391287, 9.812201], [76.396495, 9.822659], [76.394054, 9.842841], [76.38559, 9.861518], [76.36378, 9.899237], [76.359223, 9.915432], [76.320811, 9.953315], [76.311534, 9.966946], [76.294281, 9.951321], [76.278168, 9.977932], [76.269298, 10.016262], [76.273936, 10.035834], [76.255056, 10.047675], [76.245128, 10.1022], [76.236339, 10.103461], [76.227712, 10.109565], [76.222911, 10.117621], [76.215343, 10.138251], [76.20753, 10.117133], [76.213552, 10.098822], [76.223481, 10.082099], [76.229015, 10.065904], [76.230968, 10.025824], [76.235037, 10.005072], [76.242686, 9.987454], [76.228201, 9.999945], [76.21461, 10.024482], [76.182384, 10.105658], [76.181163, 10.121161], [76.184906, 10.13581], [76.184581, 10.144924], [76.177745, 10.155341], [76.177501, 10.17064], [76.224132, 10.196926], [76.236339, 10.210273], [76.237315, 10.216986], [76.24171, 10.223944], [76.242686, 10.231024], [76.240245, 10.238959], [76.234386, 10.237942], [76.227794, 10.23428], [76.22283, 10.234442], [76.21339, 10.253241], [76.206309, 10.262885], [76.194835, 10.268622], [76.207856, 10.234565], [76.209483, 10.21601], [76.201671, 10.199693], [76.184093, 10.189358], [76.170584, 10.193752], [76.160492, 10.206244], [76.153168, 10.22016], [76.138194, 10.261054], [76.120616, 10.343573], [76.10613, 10.38467], [76.070079, 10.432807], [76.065115, 10.449856], [76.063324, 10.47016], [76.052419, 10.513983], [76.044607, 10.529283], [76.018728, 10.553412], [76.008067, 10.567084], [75.999685, 10.605536], [75.939138, 10.72606], [75.934744, 10.74433], [75.93214, 10.750963], [75.926931, 10.757717], [75.922048, 10.76557], [75.921072, 10.775702], [75.924083, 10.780707], [75.936534, 10.790717], [75.94158, 10.79621], [75.911143, 10.809231], [75.894542, 10.849758], [75.880138, 10.932685], [75.832774, 11.097846], [75.742931, 11.310126], [75.737804, 11.319485], [75.733653, 11.325181], [75.730805, 11.331732], [75.729259, 11.343655], [75.739513, 11.350735], [75.742442, 11.35871], [75.735525, 11.371527], [75.722179, 11.367499], [75.711192, 11.381781], [75.69516, 11.419989], [75.677094, 11.4501], [75.664806, 11.462633], [75.650157, 11.46776], [75.630138, 11.468736], [75.616547, 11.473619], [75.608897, 11.484931], [75.606293, 11.505316], [75.600841, 11.523383], [75.581879, 11.549791], [75.585297, 11.563381], [75.530772, 11.693671], [75.506114, 11.72724], [75.379731, 11.864976], [75.372569, 11.864732], [75.355805, 11.857652], [75.35255, 11.861558], [75.349376, 11.871975], [75.324555, 11.899115], [75.307628, 11.932196], [75.326508, 11.935858], [75.400238, 11.920233], [75.400238, 11.926459], [75.386567, 11.935126], [75.382823, 11.945502], [75.385997, 11.958319], [75.393403, 11.97484], [75.380219, 11.968695], [75.361339, 11.951239], [75.349132, 11.947577], [75.331716, 11.94953], [75.316091, 11.955634], [75.302257, 11.966051], [75.290375, 11.981106], [75.278819, 12.003241], [75.281098, 12.010647], [75.314708, 12.008368], [75.329763, 12.012641], [75.324962, 12.02265], [75.305593, 12.041775], [75.30421, 12.043158], [75.264334, 12.013332], [75.260509, 11.997789], [75.276703, 11.967434], [75.245616, 12.002916], [75.239268, 12.008368], [75.233246, 12.010484], [75.228201, 12.015082], [75.224457, 12.019355], [75.222179, 12.02204], [75.216482, 12.021064], [75.210623, 12.016995], [75.207042, 12.01203], [75.208507, 12.008368], [75.193696, 12.022284], [75.18629, 12.041571], [75.180512, 12.077297], [75.143809, 12.161322], [75.13087, 12.207831], [75.139659, 12.241767], [75.139659, 12.248603], [75.12672, 12.245836], [75.11964, 12.239], [75.113943, 12.23017], [75.105479, 12.221259], [75.10613, 12.23371], [75.104747, 12.24608], [75.098643, 12.269029], [75.082856, 12.305894], [75.018403, 12.41474], [75.010997, 12.435777], [75.009939, 12.443793], [75.006602, 12.449449], [74.99936, 12.454901], [74.992035, 12.461737], [74.98878, 12.471381], [74.985362, 12.49136], [74.976736, 12.507025], [74.955251, 12.537258], [74.879649, 12.725491], [74.870128, 12.742865], [74.855724, 12.759426], [74.854259, 12.765286], [74.854747, 12.787014], [74.852306, 12.797309], [74.844005, 12.80801], [74.834972, 12.815823], [74.827647, 12.824652], [74.824962, 12.838202], [74.85141, 12.825507], [74.859141, 12.824612], [74.866954, 12.830024], [74.881114, 12.848049], [74.889822, 12.851874], [74.901134, 12.85342], [74.917491, 12.85814], [74.932872, 12.866441], [74.940929, 12.878607], [74.894786, 12.871161], [74.850597, 12.857164], [74.817556, 12.861762], [74.770274, 13.052436], [74.783376, 13.084662], [74.774099, 13.093411], [74.769216, 13.099555], [74.76295, 13.118801], [74.761892, 13.143134], [74.759776, 13.155219], [74.75294, 13.160386], [74.74879, 13.170844], [74.737804, 13.221381], [74.733409, 13.262763], [74.715099, 13.331041], [74.707205, 13.348375], [74.696137, 13.365953], [74.689464, 13.381578], [74.694591, 13.393134], [74.687511, 13.410346], [74.68336, 13.442816], [74.680919, 13.468248], [74.690766, 13.439114], [74.693696, 13.415269], [74.708263, 13.413031], [74.701915, 13.420356], [74.702403, 13.442613], [74.701427, 13.461371], [74.677094, 13.52676], [74.677989, 13.54914], [74.671397, 13.603949], [74.673513, 13.619086], [74.669932, 13.622016], [74.667979, 13.625149], [74.667166, 13.628607], [74.666677, 13.632758], [74.672862, 13.628892], [74.677094, 13.630316], [74.679698, 13.636461], [74.680919, 13.646959], [74.687185, 13.646959], [74.696788, 13.640448], [74.709972, 13.64057], [74.723806, 13.645575], [74.735606, 13.653795], [74.720551, 13.652289], [74.712901, 13.656195], [74.701427, 13.673733], [74.688324, 13.68594], [74.688975, 13.690863], [74.701427, 13.69477], [74.687755, 13.711819], [74.676036, 13.701077], [74.660492, 13.660061], [74.646983, 13.692084], [74.623057, 13.774156], [74.614594, 13.835517], [74.595958, 13.871487], [74.583995, 13.903876], [74.550792, 13.945502], [74.536388, 13.969875], [74.504731, 14.01024], [74.495372, 14.030585], [74.494802, 14.040717], [74.496267, 14.06151], [74.495372, 14.071519], [74.491384, 14.080634], [74.479177, 14.095649], [74.474864, 14.105699], [74.473155, 14.161689], [74.467459, 14.181383], [74.438731, 14.22012], [74.429047, 14.240424], [74.43336, 14.263373], [74.447032, 14.253974], [74.471039, 14.248684], [74.494965, 14.247463], [74.508962, 14.250312], [74.500011, 14.257961], [74.436371, 14.27969], [74.427745, 14.28384], [74.415375, 14.301093], [74.412608, 14.344794], [74.403575, 14.362982], [74.396983, 14.372748], [74.394379, 14.384914], [74.394216, 14.410793], [74.391856, 14.419013], [74.386729, 14.424221], [74.381602, 14.43122], [74.374278, 14.467271], [74.353852, 14.505072], [74.351899, 14.530219], [74.367035, 14.512356], [74.377452, 14.484605], [74.388845, 14.462714], [74.407237, 14.461982], [74.397634, 14.475409], [74.401134, 14.479722], [74.427745, 14.476264], [74.385427, 14.524115], [74.372406, 14.530219], [74.374685, 14.533759], [74.377126, 14.541002], [74.379242, 14.544582], [74.368337, 14.549709], [74.364106, 14.54914], [74.358735, 14.544582], [74.355479, 14.557074], [74.355805, 14.562405], [74.358735, 14.571234], [74.342947, 14.563666], [74.340994, 14.551174], [74.342947, 14.536851], [74.339041, 14.523993], [74.32488, 14.517239], [74.309825, 14.519436], [74.300548, 14.527411], [74.303559, 14.537665], [74.295909, 14.565009], [74.295177, 14.587307], [74.307384, 14.601874], [74.339041, 14.605943], [74.339041, 14.612779], [74.323904, 14.618598], [74.312266, 14.615465], [74.301117, 14.60932], [74.28712, 14.605943], [74.27589, 14.610175], [74.268077, 14.61994], [74.266856, 14.631293], [74.276215, 14.640082], [74.269216, 14.658922], [74.263194, 14.701606], [74.25294, 14.71894], [74.23406, 14.734076], [74.22047, 14.738105], [74.208751, 14.731838], [74.194347, 14.715806], [74.180675, 14.742174], [74.173839, 14.749945], [74.169444, 14.750881], [74.157725, 14.748969], [74.153331, 14.749945], [74.146495, 14.761868], [74.145844, 14.763617], [74.111339, 14.780341], [74.096446, 14.793362], [74.091319, 14.811428], [74.109141, 14.802639], [74.114024, 14.808417], [74.114268, 14.82095], [74.118663, 14.831855], [74.128754, 14.83747], [74.140473, 14.838772], [74.151541, 14.83511], [74.159516, 14.82567], [74.169688, 14.844468], [74.178966, 14.856269], [74.190684, 14.863105], [74.208018, 14.866645], [74.21811, 14.865383], [74.226899, 14.862738], [74.233084, 14.864569], [74.235199, 14.876532], [74.230724, 14.880113], [74.220876, 14.882066], [74.211192, 14.887356], [74.208018, 14.900824], [74.19752, 14.890855], [74.166189, 14.876532], [74.159516, 14.869778], [74.154063, 14.862128], [74.14088, 14.852688], [74.125011, 14.845852], [74.111827, 14.846177], [74.103038, 14.85692], [74.091156, 14.8876], [74.081065, 14.893988], [74.077159, 14.898383], [74.035981, 14.921291], [74.030284, 14.935207], [74.031016, 14.949774], [74.035981, 14.979641], [74.031261, 14.995307], [74.020193, 15.001776], [74.006847, 15.004869], [73.995779, 15.010688], [73.972992, 15.056871], [73.964366, 15.064643], [73.913097, 15.078925], [73.922537, 15.107856], [73.928966, 15.120592], [73.937022, 15.130439], [73.958263, 15.143134], [73.962169, 15.152045], [73.954112, 15.16828], [73.947276, 15.160834], [73.891287, 15.343207], [73.882579, 15.355292], [73.869477, 15.359565], [73.848399, 15.360093], [73.827403, 15.36518], [73.810395, 15.37759], [73.782888, 15.407904], [73.839366, 15.404039], [73.848399, 15.404486], [73.860118, 15.407864], [73.871918, 15.401842], [73.884939, 15.398017], [73.899587, 15.407904], [73.93336, 15.395413], [73.945323, 15.385688], [73.954112, 15.366278], [73.958507, 15.367174], [73.961681, 15.368232], [73.964203, 15.370185], [73.967784, 15.373765], [73.959972, 15.380683], [73.937022, 15.411322], [73.927582, 15.417385], [73.885916, 15.435207], [73.879161, 15.435777], [73.865082, 15.434312], [73.858572, 15.435207], [73.854177, 15.4383], [73.846853, 15.447089], [73.841563, 15.448879], [73.806163, 15.452216], [73.796397, 15.448879], [73.795177, 15.460761], [73.800466, 15.475002], [73.808849, 15.488023], [73.817556, 15.496649], [73.828136, 15.50141], [73.872244, 15.510932], [73.863129, 15.516343], [73.859548, 15.521145], [73.860525, 15.525865], [73.865977, 15.531399], [73.865977, 15.537584], [73.796397, 15.500393], [73.786388, 15.492174], [73.766287, 15.49726], [73.751231, 15.513129], [73.756114, 15.537584], [73.741222, 15.559963], [73.736827, 15.571723], [73.735118, 15.586005], [73.737071, 15.598538], [73.74171, 15.604193], [73.746593, 15.608385], [73.748709, 15.616767], [73.760427, 15.632025], [73.787364, 15.641791], [73.838145, 15.654283], [73.838145, 15.661119], [73.801036, 15.661851], [73.783865, 15.659125], [73.776622, 15.650865], [73.76824, 15.646918], [73.733246, 15.625067], [73.728282, 15.619574], [73.718272, 15.627143], [73.707042, 15.643704], [73.697927, 15.662828], [73.690766, 15.693793], [73.685883, 15.704291], [73.686778, 15.713121], [73.700857, 15.723212], [73.689871, 15.724473], [73.672374, 15.726508], [73.656016, 15.730292], [73.646251, 15.736884], [73.643565, 15.749091], [73.646251, 15.792141], [73.638845, 15.817206], [73.604666, 15.880845], [73.587576, 15.902899], [73.528982, 15.949164], [73.510102, 15.967597], [73.490977, 15.993476], [73.487315, 16.014228], [73.515961, 16.017401], [73.515961, 16.024848], [73.499278, 16.024319], [73.483897, 16.028225], [73.472504, 16.036363], [73.46811, 16.048489], [73.464854, 16.051663], [73.45753, 16.054185], [73.450369, 16.058905], [73.447032, 16.068915], [73.452973, 16.074164], [73.45753, 16.079535], [73.460704, 16.08633], [73.460704, 16.14468], [73.46339, 16.159573], [73.470225, 16.168891], [73.479259, 16.177232], [73.487966, 16.189358], [73.465017, 16.182278], [73.45281, 16.166571], [73.448009, 16.143297], [73.447032, 16.113593], [73.440196, 16.162014], [73.440196, 16.202338], [73.435232, 16.217841], [73.430837, 16.218736], [73.425955, 16.215155], [73.419688, 16.216702], [73.413585, 16.223944], [73.410411, 16.230455], [73.406098, 16.2508], [73.378673, 16.332709], [73.377778, 16.337958], [73.378673, 16.35692], [73.375662, 16.360256], [73.358246, 16.367499], [73.359386, 16.380601], [73.364431, 16.389309], [73.373057, 16.395657], [73.384939, 16.401597], [73.367442, 16.408637], [73.352061, 16.418118], [73.341319, 16.432074], [73.333995, 16.468166], [73.319184, 16.509752], [73.313813, 16.518297], [73.305837, 16.52265], [73.302989, 16.533026], [73.303722, 16.555569], [73.307302, 16.563178], [73.314952, 16.557603], [73.321951, 16.548489], [73.323497, 16.544989], [73.344005, 16.523586], [73.355724, 16.519436], [73.378673, 16.518297], [73.369477, 16.52147], [73.362315, 16.525458], [73.356212, 16.530748], [73.350841, 16.538153], [73.35434, 16.543158], [73.365001, 16.554389], [73.371267, 16.559272], [73.354015, 16.559638], [73.336436, 16.565619], [73.322765, 16.576158], [73.317231, 16.590277], [73.316091, 16.598293], [73.314626, 16.599433], [73.316091, 16.600043], [73.323497, 16.606431], [73.326996, 16.607123], [73.358246, 16.620673], [73.358409, 16.603949], [73.363048, 16.596829], [73.368663, 16.599026], [73.371267, 16.610175], [73.366466, 16.628079], [73.354259, 16.63467], [73.338878, 16.631659], [73.323497, 16.620673], [73.312185, 16.644721], [73.310557, 16.651435], [73.312511, 16.658393], [73.321462, 16.670844], [73.323497, 16.679104], [73.321625, 16.69245], [73.31658, 16.699897], [73.300304, 16.713202], [73.289561, 16.74022], [73.295258, 16.768052], [73.304454, 16.793524], [73.303722, 16.813137], [73.296886, 16.80565], [73.286632, 16.820868], [73.274669, 16.848822], [73.269379, 16.875637], [73.279552, 16.887641], [73.279063, 16.891913], [73.269379, 16.929185], [73.270518, 16.945014], [73.274913, 16.958645], [73.289399, 16.983832], [73.26238, 16.996975], [73.254568, 17.006741], [73.262706, 17.018541], [73.251231, 17.032172], [73.251475, 17.042426], [73.258962, 17.044094], [73.269379, 17.031643], [73.277354, 17.054674], [73.275727, 17.078599], [73.267426, 17.101386], [73.255138, 17.120998], [73.261974, 17.134955], [73.26059, 17.150539], [73.255138, 17.165188], [73.236176, 17.199205], [73.236013, 17.220608], [73.218028, 17.253241], [73.214854, 17.268744], [73.211192, 17.275092], [73.193126, 17.294013], [73.187022, 17.305976], [73.203461, 17.298733], [73.217133, 17.286851], [73.231212, 17.279283], [73.249034, 17.284857], [73.234711, 17.302314], [73.207367, 17.353746], [73.195974, 17.369371], [73.170584, 17.389716], [73.166515, 17.402167], [73.165294, 17.413031], [73.177094, 17.426663], [73.185395, 17.441881], [73.17335, 17.457424], [73.180675, 17.477688], [73.165538, 17.503404], [73.131684, 17.539374], [73.125987, 17.556545], [73.12794, 17.564683], [73.136404, 17.567206], [73.149425, 17.567288], [73.163422, 17.569485], [73.200694, 17.586493], [73.185883, 17.592515], [73.154063, 17.595608], [73.139171, 17.600775], [73.128184, 17.611558], [73.121349, 17.62641], [73.120291, 17.642035], [73.12615, 17.655422], [73.118907, 17.663479], [73.110525, 17.675727], [73.105479, 17.68891], [73.108165, 17.699774], [73.117361, 17.711249], [73.12322, 17.723334], [73.123302, 17.735785], [73.115001, 17.748196], [73.109874, 17.758287], [73.106619, 17.785549], [73.101329, 17.796332], [73.08546, 17.813666], [73.079438, 17.824612], [73.073741, 17.857123], [73.065196, 17.878363], [73.018565, 17.951728], [73.0171, 17.972113], [73.035411, 17.991848], [73.018891, 17.992499], [73.008637, 18.00023], [73.005219, 18.012356], [73.0088, 18.025946], [73.019542, 18.035875], [73.032481, 18.041083], [73.042247, 18.048977], [73.043468, 18.066962], [73.031993, 18.06391], [73.022716, 18.057685], [73.015147, 18.04914], [73.0088, 18.039008], [73.00115, 18.052069], [72.987315, 18.066555], [72.980968, 18.080552], [72.983165, 18.081732], [72.986664, 18.087714], [72.988129, 18.096015], [72.98463, 18.104193], [72.978363, 18.111884], [72.976085, 18.118638], [72.967296, 18.19599], [72.954845, 18.217515], [72.926931, 18.223944], [72.935232, 18.244778], [72.939952, 18.251288], [72.934825, 18.25788], [72.933279, 18.266588], [72.935069, 18.276313], [72.939952, 18.286038], [72.953136, 18.281317], [72.95753, 18.274604], [72.959239, 18.265855], [72.964529, 18.25495], [72.972667, 18.244696], [72.976736, 18.24258], [72.98227, 18.244208], [72.995128, 18.245063], [73.012462, 18.24315], [73.0324, 18.23786], [73.050955, 18.227484], [73.063487, 18.210354], [73.047618, 18.201158], [73.047618, 18.188788], [73.057465, 18.185614], [73.071544, 18.204088], [73.073985, 18.224189], [73.065278, 18.257554], [73.071544, 18.273017], [73.056977, 18.280951], [73.042979, 18.282375], [73.012462, 18.279202], [72.994884, 18.283596], [72.977387, 18.295071], [72.968028, 18.310614], [72.974783, 18.327582], [72.958751, 18.328111], [72.950857, 18.332953], [72.933604, 18.354926], [72.927908, 18.357571], [72.922537, 18.356635], [72.917328, 18.356391], [72.912608, 18.361151], [72.909841, 18.368069], [72.910167, 18.372504], [72.911632, 18.377346], [72.912608, 18.385647], [72.910167, 18.399237], [72.904307, 18.408515], [72.897227, 18.417304], [72.891449, 18.429389], [72.889659, 18.441311], [72.891449, 18.480902], [72.895763, 18.502997], [72.907237, 18.523586], [72.924653, 18.535631], [72.946788, 18.532416], [72.961111, 18.517727], [72.990245, 18.469957], [73.0088, 18.450507], [73.014985, 18.469957], [73.004405, 18.480048], [72.98878, 18.489936], [72.980968, 18.508857], [72.977875, 18.524115], [72.970388, 18.541002], [72.960297, 18.554674], [72.950206, 18.56037], [72.921153, 18.554877], [72.910167, 18.558987], [72.905772, 18.577135], [72.882172, 18.635484], [72.864513, 18.648912], [72.855317, 18.681138], [72.851736, 18.719672], [72.852387, 18.769924], [72.856944, 18.7862], [72.865571, 18.799384], [72.879161, 18.807359], [72.887055, 18.807034], [72.902029, 18.799506], [72.912608, 18.799994], [72.926931, 18.826646], [72.938975, 18.816962], [72.974783, 18.779486], [72.966319, 18.76142], [72.969574, 18.746975], [72.980642, 18.736762], [72.995128, 18.731024], [72.988048, 18.753852], [72.99171, 18.779364], [73.002452, 18.801459], [73.016287, 18.813625], [72.987966, 18.824937], [72.981293, 18.832709], [72.974783, 18.848334], [72.976085, 18.854397], [72.979747, 18.863349], [72.9817, 18.871487], [72.977794, 18.874986], [72.972179, 18.874254], [72.96697, 18.872138], [72.963145, 18.868964], [72.961681, 18.865139], [72.946137, 18.858303], [72.91627, 18.87051], [72.896821, 18.894477], [72.912608, 18.923489], [72.928071, 18.911363], [72.934581, 18.920803], [72.936778, 18.947414], [72.949229, 18.951483], [72.96339, 18.947984], [72.977224, 18.948432], [72.988292, 18.964423], [72.953624, 18.964423], [72.953624, 18.971259], [72.968272, 18.972602], [72.992035, 18.982489], [73.042166, 18.995754], [73.046641, 18.995185], [73.056814, 19.015326], [73.039806, 19.017524], [73.011892, 19.009426], [72.988292, 18.998603], [72.983572, 19.010443], [72.987804, 19.036933], [72.980968, 19.046373], [72.991873, 19.067206], [72.985688, 19.074286], [72.973806, 19.078559], [72.967296, 19.09101], [72.974783, 19.163072], [72.967296, 19.163072], [72.950043, 19.127916], [72.935557, 19.115058], [72.93393, 19.080308], [72.926931, 19.06684], [72.94337, 19.055121], [72.934255, 19.034084], [72.891449, 18.991767], [72.891449, 19.00137], [72.885265, 19.025865], [72.869314, 19.01142], [72.851085, 18.987128], [72.836274, 18.959133], [72.827647, 18.918362], [72.8213, 18.903306], [72.812511, 18.897447], [72.802745, 18.909817], [72.802013, 18.920478], [72.805349, 18.930976], [72.807384, 18.942816], [72.802745, 18.957587], [72.795909, 18.957587], [72.792328, 18.951239], [72.788259, 18.946519], [72.775401, 18.93651], [72.770356, 18.948188], [72.776052, 18.959703], [72.795909, 18.984931], [72.802419, 18.999661], [72.804942, 19.011054], [72.802745, 19.040188], [72.812999, 19.035549], [72.817068, 19.032701], [72.821788, 19.041978], [72.824229, 19.053534], [72.8213, 19.063178], [72.809581, 19.06684], [72.821951, 19.097113], [72.82309, 19.108466], [72.821056, 19.119208], [72.811778, 19.137356], [72.809581, 19.146308], [72.812185, 19.153713], [72.822439, 19.163764], [72.82309, 19.169257], [72.818044, 19.17536], [72.811209, 19.175523], [72.805186, 19.17182], [72.799571, 19.156887], [72.792166, 19.150458], [72.784923, 19.150377], [72.781586, 19.159654], [72.783376, 19.168647], [72.802501, 19.211615], [72.819835, 19.240627], [72.830089, 19.252427], [72.815115, 19.253648], [72.802094, 19.23786], [72.789399, 19.217353], [72.775401, 19.204657], [72.772227, 19.265123], [72.775645, 19.299018], [72.785411, 19.31391], [72.839366, 19.319078], [72.867035, 19.316799], [72.885509, 19.296047], [72.900564, 19.290758], [72.930349, 19.286566], [72.946544, 19.289862], [72.959727, 19.294827], [72.972667, 19.293687], [72.988292, 19.27912], [73.010509, 19.224311], [73.026622, 19.205512], [73.049653, 19.217719], [73.03004, 19.227729], [73.028005, 19.244086], [73.029145, 19.259345], [73.019298, 19.266059], [73.012462, 19.272447], [73.001801, 19.286566], [72.987804, 19.300686], [72.970958, 19.307034], [72.916759, 19.303371], [72.898936, 19.307034], [72.883556, 19.316392], [72.855317, 19.339504], [72.836925, 19.348049], [72.818533, 19.330268], [72.802094, 19.331936], [72.787608, 19.345201], [72.763438, 19.380194], [72.759776, 19.388983], [72.74822, 19.444241], [72.746837, 19.467963], [72.754405, 19.481757], [72.769216, 19.490465], [72.879161, 19.532375], [72.8567, 19.541327], [72.835948, 19.536689], [72.815929, 19.52851], [72.795909, 19.526801], [72.787852, 19.53026], [72.781261, 19.536566], [72.777029, 19.545559], [72.775401, 19.557196], [72.770681, 19.566148], [72.76002, 19.573065], [72.741954, 19.580797], [72.734548, 19.580797], [72.738292, 19.570258], [72.740733, 19.560736], [72.739757, 19.55268], [72.734548, 19.546617], [72.749278, 19.534247], [72.754893, 19.527045], [72.755626, 19.519355], [72.749197, 19.5126], [72.741547, 19.516018], [72.722504, 19.536282], [72.710785, 19.587592], [72.718028, 19.606676], [72.709646, 19.64997], [72.686697, 19.724799], [72.694102, 19.720201], [72.699962, 19.714179], [72.7046, 19.706529], [72.707774, 19.697455], [72.72641, 19.705797], [72.72641, 19.715237], [72.718598, 19.727932], [72.714041, 19.745836], [72.717296, 19.751532], [72.724376, 19.752672], [72.731293, 19.754584], [72.734548, 19.762641], [72.73406, 19.771918], [72.732758, 19.779731], [72.728282, 19.793647], [72.690115, 19.748969], [72.677908, 19.746568], [72.675792, 19.764146], [72.679861, 19.800482], [72.672862, 19.812812], [72.650076, 19.841986], [72.649181, 19.851996], [72.65919, 19.866441], [72.664236, 19.881578], [72.666189, 19.916571], [72.662771, 19.941799], [72.665212, 19.952948], [72.676443, 19.957506], [72.684337, 19.964016], [72.694509, 19.979315], [72.70338, 19.997626], [72.707774, 20.012763], [72.707774, 20.071112], [72.710297, 20.082913], [72.728282, 20.129462], [72.734548, 20.173529], [72.737641, 20.184272], [72.752289, 20.203762], [72.755626, 20.215155], [72.753591, 20.218207], [72.749034, 20.218329], [72.744395, 20.219387], [72.741954, 20.225043], [72.741954, 20.245551], [72.743012, 20.247748], [72.74822, 20.280341], [72.756602, 20.295966], [72.767833, 20.311957], [72.777517, 20.330064], [72.781586, 20.352281], [72.786794, 20.35224], [72.798188, 20.35932], [72.810313, 20.368842], [72.817068, 20.375922], [72.820811, 20.386135], [72.82309, 20.416815], [72.829438, 20.437934], [72.840017, 20.458726], [72.879161, 20.512437], [72.886485, 20.531562], [72.890391, 20.55268], [72.891449, 20.575141], [72.890636, 20.587063], [72.886241, 20.608466], [72.885265, 20.619208], [72.888438, 20.628485], [72.902599, 20.641913], [72.905772, 20.653306], [72.909434, 20.698676], [72.90797, 20.718329], [72.898936, 20.732164], [72.940278, 20.759426], [72.946788, 20.759467], [72.944998, 20.770006], [72.936371, 20.776272], [72.926443, 20.780992], [72.919444, 20.787421], [72.919119, 20.7956], [72.922862, 20.80268], [72.924978, 20.810614], [72.919444, 20.821519], [72.900238, 20.79914], [72.885753, 20.807115], [72.880138, 20.829901], [72.888438, 20.851955], [72.900401, 20.868109], [72.893809, 20.870063], [72.881196, 20.865668], [72.875336, 20.862494], [72.836925, 20.835842], [72.845876, 20.851793], [72.854259, 20.887152], [72.864919, 20.897284], [72.836681, 20.929918], [72.856456, 20.947984], [72.886485, 20.962104], [72.888438, 20.982611], [72.875987, 20.98135], [72.85377, 20.974921], [72.832693, 20.972357], [72.82309, 20.982611], [72.8213, 20.991604], [72.816091, 20.99901], [72.80836, 21.003974], [72.799327, 21.005845], [72.787608, 21.006903], [72.781993, 21.010159], [72.771983, 21.023871], [72.766856, 21.03856], [72.783946, 21.040432], [72.82309, 21.03384], [72.844981, 21.038316], [72.849864, 21.048], [72.815929, 21.121487], [72.809581, 21.130032], [72.799571, 21.128811], [72.755626, 21.109565], [72.752778, 21.113715], [72.746349, 21.118354], [72.741954, 21.123236], [72.737641, 21.117377], [72.735606, 21.110745], [72.734548, 21.092475], [72.730154, 21.087836], [72.700369, 21.074774], [72.705821, 21.10635], [72.720551, 21.129706], [72.728689, 21.148098], [72.714041, 21.16413], [72.706554, 21.157701], [72.686697, 21.158149], [72.676443, 21.153632], [72.672699, 21.145738], [72.671886, 21.134426], [72.673025, 21.112616], [72.671235, 21.099799], [72.666352, 21.097398], [72.658702, 21.097602], [72.637869, 21.085395], [72.629649, 21.087958], [72.615001, 21.105862], [72.615489, 21.119818], [72.630382, 21.135972], [72.693533, 21.178453], [72.711762, 21.194037], [72.721039, 21.198798], [72.734548, 21.19892], [72.723643, 21.209133], [72.687673, 21.220445], [72.679861, 21.22899], [72.673188, 21.239081], [72.659353, 21.244696], [72.646983, 21.253607], [72.645763, 21.273383], [72.625173, 21.259345], [72.618419, 21.252916], [72.60613, 21.266588], [72.602061, 21.277655], [72.597911, 21.307563], [72.588634, 21.331977], [72.586762, 21.341213], [72.590505, 21.348538], [72.603201, 21.352729], [72.63852, 21.357164], [72.645763, 21.362779], [72.638682, 21.371243], [72.623383, 21.370266], [72.594086, 21.362779], [72.578868, 21.367011], [72.567638, 21.37641], [72.564626, 21.38581], [72.573741, 21.390082], [72.635102, 21.407457], [72.672862, 21.428127], [72.676443, 21.431627], [72.728282, 21.465806], [72.747081, 21.452867], [72.752615, 21.463568], [72.748546, 21.479641], [72.738292, 21.482856], [72.683279, 21.465806], [72.66505, 21.457221], [72.612478, 21.421861], [72.583669, 21.417426], [72.589854, 21.432766], [72.61964, 21.487372], [72.635102, 21.499335], [72.651052, 21.507025], [72.690115, 21.540676], [72.703949, 21.548407], [72.726817, 21.553168], [72.75115, 21.565131], [72.773204, 21.580512], [72.789073, 21.595526], [72.810395, 21.635077], [72.82309, 21.643948], [72.833995, 21.644477], [72.858735, 21.6376], [72.871593, 21.636542], [72.889659, 21.64053], [72.935557, 21.658352], [72.965343, 21.678778], [73.022472, 21.698554], [73.084972, 21.729682], [73.108165, 21.733344], [73.116954, 21.73725], [73.126964, 21.746772], [73.132009, 21.757799], [73.12615, 21.766832], [73.118826, 21.764716], [73.083995, 21.746405], [73.046641, 21.733344], [73.041026, 21.73017], [73.022472, 21.71601], [73.012462, 21.712836], [72.991466, 21.708441], [72.950206, 21.689154], [72.926931, 21.684963], [72.882823, 21.69302], [72.858165, 21.694973], [72.840343, 21.688381], [72.819184, 21.675035], [72.798106, 21.673529], [72.755626, 21.684963], [72.733246, 21.687486], [72.676443, 21.678127], [72.653005, 21.680569], [72.634939, 21.684638], [72.616059, 21.685452], [72.570486, 21.670478], [72.550304, 21.666693], [72.534679, 21.672919], [72.528494, 21.695136], [72.532074, 21.713853], [72.550141, 21.751939], [72.556326, 21.774319], [72.562755, 21.839423], [72.566905, 21.849351], [72.58546, 21.858873], [72.598806, 21.881008], [72.618419, 21.925116], [72.634044, 21.937486], [72.65211, 21.941067], [72.690115, 21.938788], [72.702322, 21.947577], [72.706798, 21.966132], [72.714041, 21.982978], [72.734548, 21.986558], [72.734548, 21.994045], [72.72641, 21.995754], [72.7199, 21.996324], [72.713715, 21.995795], [72.707774, 21.994045], [72.700938, 21.988918], [72.69752, 21.982123], [72.695649, 21.975898], [72.693533, 21.972317], [72.623057, 21.96662], [72.611583, 21.955512], [72.602712, 21.94359], [72.581798, 21.928168], [72.558604, 21.915269], [72.542735, 21.910834], [72.533376, 21.920315], [72.507091, 21.962307], [72.50172, 21.97602], [72.504568, 21.994208], [72.517751, 22.032131], [72.522227, 22.054267], [72.521495, 22.066148], [72.519542, 22.077541], [72.519542, 22.088446], [72.53004, 22.107489], [72.533376, 22.118557], [72.535899, 22.14053], [72.542166, 22.156724], [72.557384, 22.176663], [72.57545, 22.194525], [72.590505, 22.205024], [72.614106, 22.211737], [72.634451, 22.210395], [72.652354, 22.202338], [72.669607, 22.18891], [72.681895, 22.181586], [72.691661, 22.181952], [72.699718, 22.184963], [72.707774, 22.185777], [72.719412, 22.182196], [72.729259, 22.177436], [72.74822, 22.164049], [72.790212, 22.217231], [72.812266, 22.234117], [72.847423, 22.240424], [72.865082, 22.233466], [72.881114, 22.220404], [72.897227, 22.213039], [72.916026, 22.22309], [72.92213, 22.236518], [72.922699, 22.254584], [72.917491, 22.27143], [72.905772, 22.281399], [72.899669, 22.269192], [72.886078, 22.262397], [72.869477, 22.262397], [72.854259, 22.270819], [72.838634, 22.276557], [72.818207, 22.273424], [72.798676, 22.26557], [72.785411, 22.256903], [72.756602, 22.24372], [72.726573, 22.248928], [72.659434, 22.273912], [72.646495, 22.273912], [72.635265, 22.271633], [72.625011, 22.271389], [72.606619, 22.28384], [72.596365, 22.286933], [72.573741, 22.288235], [72.553071, 22.282457], [72.521169, 22.253119], [72.50172, 22.240424], [72.479015, 22.233385], [72.466156, 22.232571], [72.457042, 22.236721], [72.454845, 22.245307], [72.46697, 22.255845], [72.463878, 22.263983], [72.448741, 22.266832], [72.432302, 22.255845], [72.413748, 22.246324], [72.391856, 22.253485], [72.386241, 22.266506], [72.381033, 22.289944], [72.380382, 22.312405], [72.388194, 22.322333], [72.404959, 22.332953], [72.408051, 22.353461], [72.397227, 22.366767], [72.371349, 22.355902], [72.361013, 22.343451], [72.34962, 22.324368], [72.340505, 22.303209], [72.336599, 22.284817], [72.328298, 22.260728], [72.309093, 22.257229], [72.288341, 22.261298], [72.275157, 22.260321], [72.263194, 22.276353], [72.265961, 22.28734], [72.264659, 22.295233], [72.240408, 22.301907], [72.223399, 22.303168], [72.208507, 22.300442], [72.200938, 22.291449], [72.206309, 22.273912], [72.161794, 22.280951], [72.148774, 22.277899], [72.166026, 22.260321], [72.190684, 22.251288], [72.209239, 22.259426], [72.240408, 22.288235], [72.246104, 22.27558], [72.243175, 22.265937], [72.237315, 22.256293], [72.234141, 22.243557], [72.239106, 22.238918], [72.268321, 22.226752], [72.306488, 22.22956], [72.317231, 22.184882], [72.307628, 22.079006], [72.298025, 22.061672], [72.295665, 22.051174], [72.291759, 22.053412], [72.283458, 22.043606], [72.276134, 22.031643], [72.275157, 22.027533], [72.25766, 22.012152], [72.24936, 22.008287], [72.237315, 22.007025], [72.220958, 22.010972], [72.216482, 22.007799], [72.214366, 21.994045], [72.206309, 21.994045], [72.19988, 22.006985], [72.19516, 22.003648], [72.191173, 21.993638], [72.186371, 21.986558], [72.182465, 21.982733], [72.173106, 21.975653], [72.163422, 21.973212], [72.15919, 21.98314], [72.151703, 21.993883], [72.134288, 22.004584], [72.11378, 22.010565], [72.097016, 22.007025], [72.119314, 21.993638], [72.128103, 21.983547], [72.124848, 21.972317], [72.114919, 21.969387], [72.076508, 21.979722], [72.08253, 21.973375], [72.104503, 21.959215], [72.104828, 21.951483], [72.102061, 21.945054], [72.096202, 21.94302], [72.087413, 21.948432], [72.073253, 21.954495], [72.055186, 21.953518], [72.038422, 21.947577], [72.028656, 21.938788], [72.087576, 21.919664], [72.104503, 21.91767], [72.1421, 21.924994], [72.156505, 21.925849], [72.145356, 21.91767], [72.145356, 21.910834], [72.15919, 21.904608], [72.152354, 21.897854], [72.16977, 21.890448], [72.167328, 21.879136], [72.160004, 21.865912], [72.162608, 21.852851], [72.169444, 21.841376], [72.161876, 21.839179], [72.13087, 21.846259], [72.123302, 21.853746], [72.111339, 21.869859], [72.098481, 21.88052], [72.077647, 21.894436], [72.057953, 21.898749], [72.049327, 21.880439], [72.052908, 21.876532], [72.061534, 21.873114], [72.071788, 21.870795], [72.080333, 21.869859], [72.086436, 21.865627], [72.104503, 21.835761], [72.086762, 21.830268], [72.062836, 21.834377], [72.004161, 21.857733], [71.997325, 21.854926], [71.99464, 21.839504], [71.994151, 21.812201], [71.996104, 21.799506], [72.001475, 21.787909], [72.005382, 21.792955], [72.009613, 21.7956], [72.021821, 21.800971], [72.021495, 21.79442], [72.022309, 21.780911], [72.021821, 21.774319], [72.054942, 21.78559], [72.073904, 21.788723], [72.087413, 21.784491], [72.103201, 21.775214], [72.117442, 21.776597], [72.166026, 21.793931], [72.171072, 21.790961], [72.172699, 21.777737], [72.177013, 21.766669], [72.195811, 21.757799], [72.200043, 21.750067], [72.202403, 21.748114], [72.214366, 21.712836], [72.219005, 21.706], [72.223806, 21.700588], [72.230235, 21.69599], [72.240408, 21.691718], [72.251801, 21.690904], [72.263031, 21.692328], [72.271739, 21.690904], [72.278331, 21.671942], [72.285411, 21.665025], [72.292491, 21.659491], [72.295665, 21.653876], [72.296641, 21.642157], [72.298513, 21.633979], [72.298839, 21.626288], [72.295665, 21.616034], [72.292003, 21.611762], [72.279145, 21.600531], [72.275157, 21.595526], [72.242198, 21.48078], [72.2199, 21.444729], [72.16684, 21.392076], [72.15919, 21.373277], [72.151622, 21.360175], [72.116059, 21.32274], [72.104503, 21.315009], [72.110037, 21.304836], [72.111501, 21.296088], [72.107758, 21.289984], [72.097016, 21.287665], [72.10255, 21.2744], [72.099376, 21.26496], [72.093598, 21.258205], [72.090831, 21.252916], [72.093516, 21.243557], [72.104503, 21.222154], [72.104747, 21.205064], [72.090831, 21.191474], [72.084809, 21.188707], [72.07545, 21.185696], [72.066661, 21.181342], [72.062836, 21.174709], [72.055024, 21.168362], [71.990733, 21.136379], [71.972423, 21.13227], [71.92628, 21.130032], [71.806163, 21.062079], [71.788585, 21.047512], [71.76295, 21.032294], [71.743663, 21.027574], [71.730235, 21.030707], [71.717621, 21.03205], [71.699743, 21.021306], [71.68336, 21.011461], [71.639985, 20.996812], [71.583507, 20.958238], [71.549571, 20.951239], [71.557302, 20.96601], [71.562348, 20.972968], [71.569347, 20.979193], [71.551117, 20.973578], [71.536794, 20.964301], [71.493419, 20.929145], [71.484386, 20.917141], [71.480724, 20.900377], [71.46225, 20.885321], [71.378103, 20.859442], [71.318614, 20.849311], [71.140636, 20.765692], [71.116954, 20.766913], [71.110118, 20.759467], [71.09547, 20.763739], [71.083263, 20.756537], [71.072602, 20.745673], [71.062348, 20.738959], [71.044444, 20.740546], [71.024181, 20.744534], [71.00766, 20.741604], [71.005951, 20.736558], [71.000824, 20.722154], [70.982432, 20.71015], [70.972992, 20.709621], [70.902531, 20.70552], [70.84018, 20.70189], [70.819672, 20.702948], [70.801606, 20.709052], [70.785167, 20.722154], [70.774587, 20.727362], [70.763194, 20.724555], [70.751638, 20.719631], [70.740245, 20.718492], [70.730805, 20.723212], [70.698578, 20.746405], [70.562174, 20.799767], [70.541515, 20.807847], [70.484141, 20.84394], [70.463064, 20.848863], [70.451427, 20.855902], [70.417003, 20.890448], [70.398774, 20.90351], [70.336681, 20.927069], [70.299978, 20.958564], [70.26352, 20.980414], [70.152192, 21.078518], [70.054445, 21.153155], [69.988332, 21.2334], [69.968019, 21.258055], [69.966156, 21.263414], [69.958263, 21.273342], [69.911632, 21.315009], [69.844981, 21.389594], [69.814708, 21.433661], [69.808604, 21.465806], [69.788748, 21.469306], [69.771495, 21.47842], [69.756358, 21.490424], [69.629731, 21.616034], [69.568207, 21.650133], [69.552745, 21.665188], [69.519786, 21.704779], [69.49936, 21.720201], [69.478526, 21.752631], [69.4463, 21.766303], [69.427419, 21.781236], [69.397716, 21.815253], [69.391775, 21.83218], [69.395193, 21.861314], [69.39088, 21.876695], [69.367849, 21.856106], [69.355479, 21.853176], [69.336192, 21.862454], [69.243907, 21.94595], [69.21225, 21.967475], [69.198416, 21.979722], [69.150645, 22.041205], [69.119314, 22.062079], [69.103038, 22.076361], [69.092052, 22.099595], [69.048188, 22.157904], [69.012543, 22.185614], [68.994151, 22.203925], [68.980479, 22.235541], [68.953298, 22.276028], [68.944591, 22.295071], [68.944021, 22.318264], [68.951427, 22.334866], [68.960623, 22.349514], [68.965017, 22.366767], [68.967621, 22.387193], [68.974376, 22.404731], [68.983246, 22.419379], [68.992931, 22.431586], [69.007498, 22.443183], [69.047537, 22.464016], [69.073578, 22.481879], [69.081391, 22.480536], [69.085216, 22.472886], [69.082286, 22.45954], [69.074474, 22.449449], [69.056, 22.44184], [69.048188, 22.431586], [69.062266, 22.407701], [69.071788, 22.399482], [69.088552, 22.396877], [69.097423, 22.398871], [69.11378, 22.407864], [69.122732, 22.411078], [69.132009, 22.410468], [69.139822, 22.407131], [69.146983, 22.404853], [69.154633, 22.407701], [69.168793, 22.42064], [69.178559, 22.427639], [69.188243, 22.426418], [69.202159, 22.414862], [69.20574, 22.398668], [69.18214, 22.366441], [69.185313, 22.349677], [69.169688, 22.327948], [69.173839, 22.308295], [69.19044, 22.292304], [69.212087, 22.281399], [69.212087, 22.273912], [69.286469, 22.284613], [69.335704, 22.306138], [69.346446, 22.324897], [69.359874, 22.329169], [69.435395, 22.33039], [69.468923, 22.337836], [69.479666, 22.342231], [69.485362, 22.347561], [69.500011, 22.366767], [69.502696, 22.37226], [69.508556, 22.374254], [69.515147, 22.37519], [69.519867, 22.377631], [69.525645, 22.392076], [69.522797, 22.403876], [69.516938, 22.416083], [69.513682, 22.431586], [69.515961, 22.448147], [69.521983, 22.44892], [69.530772, 22.446112], [69.541026, 22.452094], [69.547862, 22.452094], [69.559825, 22.411526], [69.561697, 22.391588], [69.554698, 22.377631], [69.569021, 22.370551], [69.601085, 22.363837], [69.616059, 22.355902], [69.617442, 22.365058], [69.619884, 22.371039], [69.629731, 22.383775], [69.640391, 22.375963], [69.647227, 22.380439], [69.652354, 22.389879], [69.657725, 22.396877], [69.661306, 22.400214], [69.665212, 22.404975], [69.669444, 22.409247], [69.674653, 22.411078], [69.680837, 22.409613], [69.694509, 22.403957], [69.699392, 22.404283], [69.708995, 22.418891], [69.725352, 22.464545], [69.733409, 22.480048], [69.742198, 22.472968], [69.788748, 22.423163], [69.794932, 22.418606], [69.806977, 22.420966], [69.812999, 22.429511], [69.816905, 22.440985], [69.822276, 22.452094], [69.833263, 22.463813], [69.841807, 22.466376], [69.851085, 22.465237], [69.86378, 22.465766], [69.884776, 22.473578], [69.905284, 22.486396], [69.962087, 22.535346], [69.972992, 22.541449], [69.973888, 22.539984], [69.996267, 22.541449], [70.002696, 22.543158], [70.020844, 22.555121], [70.059093, 22.558417], [70.144705, 22.547797], [70.178477, 22.561957], [70.189708, 22.577786], [70.207286, 22.614976], [70.234874, 22.648871], [70.267263, 22.705959], [70.319102, 22.778266], [70.323253, 22.787665], [70.321544, 22.811713], [70.32252, 22.822659], [70.32545, 22.826321], [70.342947, 22.843166], [70.375255, 22.902655], [70.387706, 22.912014], [70.404633, 22.919501], [70.43686, 22.954901], [70.45338, 22.96662], [70.462738, 22.966539], [70.474294, 22.964057], [70.486339, 22.96369], [70.497569, 22.970038], [70.504731, 22.97956], [70.51238, 22.995347], [70.517914, 23.003892], [70.527192, 23.023586], [70.528494, 23.047065], [70.523692, 23.07038], [70.514903, 23.089545], [70.50294, 23.104397], [70.484223, 23.122138], [70.467133, 23.129706], [70.459646, 23.114], [70.450938, 23.105658], [70.410655, 23.086005], [70.398774, 23.069037], [70.398936, 23.059068], [70.401052, 23.045396], [70.40447, 23.033352], [70.410981, 23.023179], [70.401866, 22.999254], [70.402029, 22.99022], [70.403819, 22.981879], [70.403494, 22.967841], [70.401622, 22.953843], [70.398774, 22.945502], [70.392426, 22.940863], [70.384532, 22.93887], [70.363536, 22.938707], [70.357677, 22.936713], [70.352387, 22.933051], [70.34669, 22.931301], [70.339529, 22.935289], [70.334483, 22.940131], [70.330414, 22.943305], [70.325694, 22.945014], [70.319102, 22.945502], [70.314626, 22.943508], [70.308767, 22.939765], [70.303477, 22.938137], [70.300059, 22.944281], [70.29363, 22.951402], [70.291759, 22.95303], [70.269298, 22.958564], [70.258311, 22.971869], [70.249685, 22.987454], [70.233084, 23.000149], [70.231944, 22.974921], [70.222016, 22.959174], [70.204112, 22.952216], [70.178477, 22.95303], [70.171723, 22.951972], [70.167735, 22.949123], [70.163097, 22.948961], [70.154307, 22.956041], [70.150727, 22.961005], [70.149669, 22.964301], [70.147472, 22.966051], [70.140636, 22.96662], [70.12908, 22.969306], [70.125011, 22.975979], [70.126231, 22.984809], [70.130626, 22.993964], [70.109548, 22.959174], [70.120453, 22.952582], [70.132335, 22.941067], [70.135427, 22.929999], [70.120128, 22.925035], [70.061778, 22.918199], [69.996918, 22.893785], [69.940684, 22.886135], [69.884288, 22.867825], [69.852875, 22.863593], [69.833181, 22.858466], [69.806814, 22.845282], [69.780121, 22.82746], [69.760102, 22.808336], [69.728852, 22.754828], [69.715831, 22.746894], [69.694347, 22.749701], [69.653819, 22.760484], [69.585704, 22.761176], [69.575694, 22.76435], [69.556977, 22.77851], [69.544444, 22.781684], [69.510753, 22.780504], [69.500011, 22.781684], [69.489431, 22.784735], [69.482595, 22.788479], [69.477306, 22.79206], [69.472179, 22.794664], [69.424653, 22.804267], [69.405935, 22.813666], [69.372569, 22.818996], [69.338145, 22.833075], [69.23878, 22.841946], [69.201915, 22.85224], [69.13559, 22.888373], [69.039887, 22.950507], [68.866222, 23.019965], [68.691254, 23.13052], [68.660655, 23.154242], [68.654063, 23.157782], [68.650645, 23.162055], [68.651134, 23.171454], [68.649669, 23.180854], [68.622569, 23.191555], [68.609141, 23.206773], [68.599946, 23.225043], [68.595714, 23.240383], [68.598318, 23.236965], [68.609386, 23.226711], [68.620128, 23.245429], [68.62322, 23.256171], [68.623057, 23.267646], [68.605317, 23.259589], [68.57781, 23.255276], [68.552257, 23.259711], [68.541026, 23.278225], [68.561046, 23.316107], [68.568533, 23.318427], [68.599132, 23.322252], [68.606944, 23.325588], [68.603038, 23.332831], [68.592052, 23.340074], [68.57838, 23.343411], [68.571056, 23.346381], [68.566661, 23.352973], [68.563731, 23.359931], [68.561046, 23.363918], [68.555186, 23.363959], [68.548513, 23.361518], [68.543142, 23.358588], [68.541026, 23.357001], [68.52947, 23.363105], [68.520681, 23.369208], [68.515147, 23.377021], [68.513194, 23.388088], [68.515147, 23.399319], [68.520763, 23.407538], [68.529552, 23.411933], [68.541026, 23.411689], [68.536388, 23.421332], [68.530121, 23.425686], [68.522146, 23.42475], [68.513194, 23.418524], [68.501475, 23.423285], [68.49643, 23.417873], [68.490489, 23.409329], [68.475352, 23.404853], [68.462576, 23.409125], [68.458832, 23.419827], [68.461111, 23.433173], [68.465505, 23.445787], [68.451915, 23.43773], [68.44044, 23.434272], [68.430919, 23.436591], [68.423676, 23.445787], [68.432953, 23.465888], [68.447439, 23.487454], [68.465587, 23.505276], [68.48585, 23.514106], [68.481293, 23.525133], [68.475597, 23.524115], [68.468028, 23.519843], [68.458669, 23.52147], [68.454112, 23.526272], [68.444021, 23.541205], [68.437348, 23.548814], [68.441417, 23.53205], [68.447439, 23.516343], [68.446951, 23.502997], [68.431163, 23.493598], [68.411632, 23.494696], [68.406098, 23.508734], [68.410818, 23.548814], [68.404959, 23.597602], [68.408865, 23.621324], [68.427501, 23.631415], [68.471528, 23.618638], [68.491466, 23.618842], [68.492686, 23.6376], [68.486176, 23.641425], [68.476899, 23.63935], [68.468761, 23.640815], [68.465505, 23.65526], [68.469412, 23.659247], [68.488943, 23.664781], [68.504731, 23.674221], [68.523936, 23.67593], [68.533539, 23.679185], [68.538829, 23.684027], [68.554698, 23.706488], [68.583507, 23.735297], [68.601736, 23.748684], [68.637869, 23.761542], [68.654552, 23.799058], [68.670746, 23.815741], [68.680186, 23.817776], [68.715587, 23.815741], [68.725597, 23.818915], [68.745128, 23.833075], [68.756521, 23.836249], [68.768809, 23.841132], [68.821625, 23.878404], [68.805675, 23.884426], [68.78004, 23.874091], [68.766938, 23.878404], [68.740245, 23.853461], [68.717784, 23.84101], [68.691905, 23.836656], [68.654063, 23.836249], [68.633474, 23.833075], [68.623057, 23.824408], [68.609386, 23.795233], [68.598888, 23.781887], [68.587738, 23.775702], [68.554698, 23.768541], [68.538341, 23.761298], [68.52475, 23.752021], [68.51002, 23.744086], [68.47283, 23.737494], [68.449718, 23.723334], [68.427745, 23.714301], [68.410818, 23.700507], [68.34197, 23.623969], [68.351573, 23.617825], [68.353038, 23.610663], [68.348888, 23.60342], [68.34197, 23.596625], [68.327322, 23.5869], [68.317638, 23.586127], [68.308116, 23.589057], [68.294119, 23.590399], [68.230805, 23.58808], [68.181407, 23.594062], [68.1574, 23.600816], [68.143403, 23.612698], [68.150156, 23.631372], [68.157069, 23.632131], [68.167229, 23.63173], [68.174327, 23.631415], [68.17437, 23.631448], [68.17543, 23.631406], [68.178674, 23.633718], [68.181895, 23.647854], [68.184753, 23.652181], [68.192673, 23.65628], [68.199663, 23.659597], [68.209321, 23.662746], [68.220787, 23.65843], [68.220789, 23.658427], [68.232578, 23.644912], [68.232669, 23.645015], [68.232677, 23.645006], [68.245128, 23.659125], [68.258931, 23.660481], [68.27589, 23.665839], [68.280447, 23.685981], [68.258962, 23.674709], [68.240977, 23.674018], [68.237165, 23.675876], [68.224864, 23.681871], [68.208751, 23.696234], [68.19752, 23.714545], [68.195811, 23.733466], [68.197927, 23.753404], [68.197765, 23.774807], [68.187723, 23.764264], [68.177736, 23.753962], [68.169275, 23.748231], [68.163748, 23.757717], [68.166026, 23.773179], [68.175629, 23.798245], [68.179857, 23.827937], [68.182465, 23.835761], [68.183035, 23.842108], [68.183517, 23.8433], [68.207288, 23.877019], [68.21504, 23.881748], [68.23292, 23.889111], [68.239948, 23.893116], [68.243049, 23.893426], [68.252867, 23.891695], [68.256794, 23.891928], [68.26036, 23.895545], [68.257931, 23.899473], [68.253746, 23.90278], [68.251937, 23.904821], [68.253074, 23.911229], [68.25266, 23.920091], [68.254934, 23.929109], [68.263926, 23.93593], [68.274003, 23.937687], [68.277672, 23.933501], [68.280049, 23.925724], [68.286198, 23.916371], [68.300409, 23.910505], [68.314052, 23.915906], [68.325989, 23.927791], [68.335033, 23.940995], [68.333689, 23.948048], [68.329865, 23.958151], [68.330433, 23.966574], [68.342629, 23.968564], [68.348882, 23.964611], [68.352396, 23.956549], [68.353843, 23.94717], [68.353636, 23.939289], [68.385469, 23.960425], [68.431564, 23.967143], [68.547836, 23.966316], [68.646435, 23.965696], [68.724466, 23.965179], [68.725086, 24.10406], [68.725551, 24.208911], [68.725913, 24.289216], [68.747307, 24.331177], [68.79919, 24.329085], [68.813815, 24.30844], [68.819654, 24.250304], [68.838774, 24.236455], [68.848903, 24.244025], [68.880116, 24.297355], [68.883268, 24.305365], [68.885077, 24.313556], [68.890296, 24.319473], [68.904197, 24.320584], [68.913189, 24.317251], [68.921974, 24.310507], [68.929415, 24.302368], [68.94869, 24.270277], [68.962643, 24.257228], [68.980781, 24.255394], [69.007808, 24.264567], [69.048477, 24.285237], [69.067701, 24.288441], [69.091886, 24.281904], [69.148213, 24.25666], [69.166713, 24.253172], [69.206246, 24.258572], [69.280815, 24.283739], [69.563071, 24.276762], [69.59232, 24.264618], [69.670558, 24.188706], [69.714587, 24.168578], [69.769054, 24.162557], [69.972039, 24.165219], [70.015964, 24.174055], [70.052137, 24.202064], [70.062989, 24.220306], [70.087174, 24.28255], [70.097871, 24.298828], [70.109705, 24.3049], [70.144586, 24.307897], [70.202205, 24.325571], [70.222773, 24.326707], [70.242565, 24.330609], [70.2791, 24.355078], [70.29853, 24.363424], [70.353204, 24.366266], [70.370774, 24.372364], [70.416353, 24.401948], [70.520946, 24.424919], [70.562907, 24.424092], [70.575206, 24.400036], [70.569211, 24.389882], [70.551952, 24.379366], [70.546474, 24.373139], [70.545647, 24.362183], [70.555362, 24.327018], [70.56022, 24.287253], [70.567661, 24.272809], [70.584714, 24.2579], [70.621508, 24.241157], [70.755453, 24.231442], [70.776021, 24.236687], [70.813589, 24.254464], [70.834105, 24.261285], [70.851468, 24.264877], [70.857566, 24.271672], [70.844543, 24.288286], [70.840926, 24.30583], [70.856532, 24.323814], [70.917717, 24.361718], [70.936321, 24.367196], [70.955338, 24.365904], [70.977145, 24.357248], [70.996679, 24.356602], [71.007118, 24.363966], [71.014352, 24.375206], [71.025101, 24.38629], [71.073057, 24.402103], [71.082772, 24.411508], [71.07502, 24.436417], [71.040087, 24.446778], [71.00009, 24.452901], [70.977145, 24.464942], [70.974355, 24.472202], [70.973114, 24.479644], [70.973218, 24.48724], [70.974768, 24.494966], [70.977145, 24.515068], [70.980504, 24.521889], [70.981899, 24.52853], [70.980866, 24.534731], [70.977145, 24.54039], [70.957767, 24.556022], [70.954821, 24.584521], [70.962779, 24.615785], [70.977145, 24.639686], [71.043343, 24.669064], [71.063858, 24.682577], [71.056632, 24.69282], [71.036935, 24.72074], [71.00319, 24.808203], [70.943039, 24.894063], [70.915237, 24.946618], [70.893326, 25.001886], [70.859891, 25.139449], [70.848574, 25.163323], [70.831418, 25.183322], [70.768372, 25.233086], [70.734886, 25.267348], [70.723104, 25.287295], [70.718556, 25.310808], [70.710495, 25.335871], [70.670394, 25.375558], [70.654478, 25.39659], [70.646623, 25.431369], [70.652824, 25.545884], [70.657475, 25.63363], [70.653857, 25.674455], [70.632308, 25.701378], [70.592259, 25.708819], [70.554432, 25.698794], [70.516657, 25.68386], [70.477227, 25.676315], [70.360129, 25.673473], [70.303698, 25.684583], [70.264579, 25.697296], [70.249231, 25.707683], [70.234452, 25.730989], [70.213988, 25.786334], [70.195591, 25.806953], [70.15425, 25.839406], [70.114976, 25.881729], [70.083246, 25.929943], [70.064643, 25.980327], [70.064281, 25.99552], [70.072911, 26.047455], [70.073893, 26.083112], [70.078285, 26.099648], [70.132029, 26.18047], [70.146912, 26.217419], [70.151614, 26.254058], [70.144018, 26.294313], [70.142467, 26.313692], [70.147945, 26.333226], [70.157144, 26.354], [70.160503, 26.371311], [70.156989, 26.410947], [70.162931, 26.493319], [70.158074, 26.530113], [70.1296, 26.562514], [70.093685, 26.580394], [70.056064, 26.589076], [69.815562, 26.580291], [69.772258, 26.59507], [69.700221, 26.653], [69.659396, 26.677701], [69.50416, 26.735165], [69.472844, 26.766584], [69.465093, 26.80777], [69.48597, 26.926833], [69.507571, 27.050081], [69.534443, 27.125581], [69.575577, 27.188419], [69.666114, 27.270016], [69.730606, 27.310324], [69.848015, 27.410369], [69.908063, 27.497289], [69.993536, 27.571083], [70.016894, 27.60059], [70.090688, 27.79355], [70.101953, 27.81174], [70.189028, 27.89179], [70.199002, 27.90096], [70.267731, 27.945272], [70.323852, 28.000437], [70.341939, 28.01147], [70.359509, 28.016327], [70.398266, 28.02165], [70.43692, 28.035344], [70.456247, 28.039788], [70.477227, 28.037256], [70.506786, 28.028962], [70.534898, 28.015965], [70.559806, 27.998447], [70.592879, 27.964496], [70.621198, 27.944135], [70.633187, 27.93163], [70.64161, 27.911269], [70.637631, 27.874217], [70.640732, 27.854787], [70.671738, 27.791095], [70.710391, 27.74115], [70.761758, 27.709783], [70.831573, 27.701463], [70.91379, 27.717818], [71.027581, 27.768048], [71.150675, 27.822411], [71.226329, 27.845356], [71.31144, 27.861711], [71.397791, 27.868378], [71.477115, 27.862435], [71.560727, 27.868533], [71.70139, 27.906773], [71.860864, 27.950207], [71.874403, 27.95969], [71.879984, 27.974909], [71.89156, 28.097097], [71.896934, 28.115546], [71.908303, 28.135571], [71.988195, 28.228097], [72.110978, 28.317627], [72.149942, 28.353774], [72.17764, 28.397053], [72.197794, 28.44488], [72.256499, 28.645591], [72.28027, 28.687165], [72.354581, 28.767186], [72.382176, 28.784006], [72.52563, 28.84992], [72.658541, 28.911001], [72.77254, 28.96335], [72.901524, 29.022622], [72.918164, 29.032854], [72.930359, 29.047685], [72.962915, 29.116829], [72.98865, 29.15463], [73.050869, 29.228217], [73.1289, 29.360302], [73.177993, 29.443449], [73.232873, 29.536596], [73.284756, 29.683745], [73.327441, 29.80521], [73.370332, 29.927322], [73.385215, 29.942308], [73.557815, 30.012536], [73.739716, 30.048451], [73.778266, 30.067313], [73.944354, 30.188288], [73.946831, 30.204077], [73.948902, 30.217278], [73.934639, 30.261048], [73.911901, 30.303784], [73.891644, 30.329778], [73.842345, 30.35298], [73.845239, 30.35696], [73.85175, 30.372669], [73.867666, 30.387449], [73.887614, 30.396544], [73.906114, 30.394838], [73.904253, 30.401453], [73.902083, 30.415612], [73.899913, 30.422124], [73.916656, 30.417163], [73.929265, 30.419075], [73.939083, 30.426051], [73.98983, 30.487804], [73.996031, 30.500982], [74.004402, 30.50894], [74.04409, 30.518655], [74.057526, 30.531367], [74.061453, 30.555604], [74.058249, 30.574466], [74.057112, 30.591571], [74.067448, 30.610536], [74.082227, 30.625884], [74.09587, 30.637149], [74.111476, 30.646244], [74.132663, 30.655494], [74.155711, 30.659577], [74.163669, 30.664899], [74.16677, 30.67911], [74.170077, 30.687172], [74.18465, 30.696835], [74.191161, 30.717041], [74.198809, 30.724999], [74.215139, 30.737453], [74.235706, 30.764118], [74.244078, 30.771249], [74.259167, 30.781016], [74.266712, 30.788354], [74.265782, 30.795434], [74.256687, 30.815278], [74.260201, 30.819412], [74.279941, 30.824683], [74.300198, 30.83817], [74.31136, 30.855947], [74.304022, 30.874034], [74.31074, 30.884473], [74.319215, 30.893413], [74.329757, 30.899614], [74.34247, 30.901939], [74.353632, 30.901112], [74.379677, 30.894498], [74.401277, 30.893103], [74.409132, 30.901009], [74.41337, 30.915065], [74.424325, 30.932325], [74.441585, 30.946174], [74.458535, 30.953409], [74.477242, 30.956096], [74.499463, 30.95651], [74.519306, 30.962452], [74.534809, 30.97625], [74.548762, 30.992166], [74.564058, 31.004362], [74.564265, 31.025032], [74.579458, 31.042706], [74.600955, 31.056607], [74.658833, 31.083789], [74.650254, 31.093142], [74.631961, 31.107508], [74.616975, 31.11526], [74.596924, 31.115776], [74.553103, 31.108955], [74.536773, 31.115156], [74.523544, 31.129315], [74.514862, 31.141821], [74.509591, 31.156084], [74.506387, 31.175411], [74.509074, 31.195668], [74.525094, 31.235407], [74.525301, 31.280211], [74.532225, 31.303207], [74.571603, 31.389248], [74.583695, 31.40656], [74.600025, 31.42413], [74.61191, 31.440201], [74.617181, 31.458908], [74.614598, 31.477821], [74.584522, 31.517509], [74.555583, 31.612232], [74.499463, 31.699875], [74.489437, 31.711192], [74.492641, 31.714551], [74.503287, 31.730571], [74.505457, 31.732018], [74.513312, 31.735118], [74.516206, 31.737392], [74.518273, 31.741836], [74.521167, 31.753773], [74.523027, 31.758476], [74.530778, 31.767829], [74.535119, 31.771137], [74.53698, 31.775736], [74.537393, 31.788913], [74.540804, 31.810876], [74.550415, 31.826999], [74.565608, 31.840538], [74.641056, 31.890561], [74.657179, 31.895677], [74.669995, 31.904152], [74.698417, 31.950247], [74.763012, 31.938982], [74.783683, 31.942858], [74.802493, 31.968851], [74.811278, 32.003732], [74.828641, 32.025437], [74.873186, 32.011691], [74.889103, 32.028899], [74.908843, 32.031896], [74.931374, 32.029829], [74.955662, 32.032206], [74.967858, 32.037891], [74.974059, 32.04497], [74.978503, 32.053393], [74.986151, 32.063212], [74.995556, 32.067398], [75.020154, 32.065847], [75.030179, 32.066416], [75.022635, 32.094683], [75.040825, 32.09799], [75.102113, 32.078095], [75.110278, 32.07763], [75.11896, 32.08104], [75.129812, 32.090239], [75.13839, 32.093494], [75.161334, 32.087965], [75.17415, 32.086828], [75.173116, 32.109721], [75.195751, 32.128634], [75.226447, 32.14207], [75.249908, 32.148892], [75.263344, 32.150907], [75.294659, 32.148892], [75.29807, 32.159795], [75.308405, 32.193902], [75.31657, 32.210748], [75.34861, 32.242012], [75.359048, 32.261675], [75.35295, 32.284387], [75.326906, 32.312473], [75.298403, 32.332427], [75.230167, 32.380195], [75.196164, 32.3973], [75.125988, 32.411744], [75.092191, 32.429339], [75.054881, 32.455539], [75.023668, 32.466262], [74.990699, 32.463368], [74.948427, 32.44877], [74.906983, 32.445178], [74.83815, 32.474866], [74.799393, 32.47267], [74.763529, 32.462671], [74.724978, 32.46081], [74.689322, 32.471352], [74.662553, 32.498379], [74.632581, 32.568168], [74.629584, 32.588089], [74.636922, 32.606254], [74.647981, 32.623617], [74.656042, 32.64067], [74.657282, 32.660592], [74.650151, 32.701235], [74.649738, 32.723146], [74.656972, 32.745341], [74.682294, 32.787328], [74.689115, 32.808825], [74.685291, 32.831227], [74.671752, 32.840581], [74.653562, 32.837428], [74.635888, 32.82208], [74.626276, 32.80265], [74.622039, 32.785416], [74.614081, 32.770404], [74.59341, 32.757872], [74.573773, 32.75255], [74.52096, 32.745289], [74.500909, 32.746013], [74.484476, 32.753532], [74.455331, 32.778595], [74.438691, 32.785984], [74.418951, 32.784615], [74.386394, 32.767768], [74.367688, 32.763531], [74.34619, 32.76689], [74.329344, 32.776424], [74.316321, 32.791075], [74.306503, 32.809911], [74.315939, 32.82175], [74.316989, 32.823067], [74.322936, 32.830529], [74.333788, 32.849262], [74.336682, 32.870139], [74.324486, 32.921402], [74.322006, 32.972045], [74.311257, 32.994292], [74.283972, 33.008813], [74.190851, 33.022094], [74.153851, 33.040181], [74.098453, 33.104828], [74.065794, 33.132604], [74.029414, 33.154153], [74.002335, 33.177692], [73.988486, 33.20862], [73.991793, 33.252571], [74.001715, 33.270167], [74.017838, 33.279494], [74.055355, 33.292026], [74.071995, 33.302284], [74.085121, 33.314324], [74.095973, 33.328484], [74.105481, 33.345201], [74.136281, 33.418013], [74.157778, 33.494236], [74.141552, 33.549374], [74.088118, 33.58529], [74.024246, 33.614254], [73.976704, 33.648387], [73.968332, 33.662081], [73.963061, 33.676757], [73.960787, 33.692286], [73.963061, 33.72156], [73.966162, 33.734531], [73.970709, 33.747063], [73.976704, 33.758922], [74.011327, 33.81047], [74.034581, 33.828608], [74.142275, 33.84424], [74.177518, 33.857547], [74.212555, 33.878424], [74.23953, 33.901007], [74.262474, 33.929946], [74.272293, 33.96214], [74.259684, 33.994386], [74.228368, 34.012886], [74.192091, 34.011853], [74.154264, 34.004101], [74.118401, 34.002525], [74.065277, 34.018338], [74.04502, 34.019423], [73.976704, 34.004592], [73.945801, 34.009786], [73.916242, 34.02795], [73.893711, 34.054305], [73.88441, 34.0842], [73.893298, 34.11474], [73.916862, 34.135256], [73.976704, 34.161559], [73.995411, 34.176442], [73.998304, 34.196803], [73.990553, 34.218507], [73.976704, 34.23755], [73.95438, 34.287185], [73.93774, 34.304212], [73.907457, 34.306796], [73.879035, 34.30429], [73.8533, 34.307416], [73.829116, 34.315348], [73.804208, 34.327492], [73.78271, 34.346819], [73.774856, 34.370952], [73.779817, 34.396067], [73.79625, 34.418546], [73.806792, 34.425161], [73.831596, 34.4364], [73.840381, 34.44441], [73.845549, 34.456477], [73.846996, 34.494175], [73.846996, 34.49433], [73.863119, 34.517067], [73.910971, 34.544973], [73.925957, 34.564971], [73.925441, 34.593393], [73.917483, 34.619283], [73.919343, 34.642538], [73.948592, 34.662743], [73.962234, 34.668169], [74.120881, 34.690855], [74.146823, 34.701862], [74.221753, 34.747751], [74.285832, 34.768887], [74.348877, 34.773434], [74.412129, 34.764494], [74.66493, 34.688323], [75.017777, 34.629722], [75.111001, 34.633649], [75.17632, 34.64538], [75.212907, 34.644967], [75.224544, 34.638622], [75.236885, 34.631892], [75.251355, 34.613082], [75.267478, 34.598303], [75.306958, 34.574015], [75.348093, 34.55722], [75.611643, 34.49836], [75.655568, 34.496965], [75.713652, 34.508515], [75.734839, 34.508618], [75.777111, 34.503812], [75.795818, 34.507921], [75.816178, 34.521667], [75.873952, 34.571379], [75.939271, 34.612049], [75.966246, 34.619077], [75.973378, 34.622332], [75.98671, 34.636027], [76.007484, 34.66512], [76.023401, 34.677161], [76.058334, 34.683414], [76.121792, 34.660883], [76.154969, 34.661658], [76.262042, 34.684654], [76.399915, 34.750748], [76.438156, 34.762892], [76.476499, 34.757776], [76.519908, 34.731111], [76.535824, 34.726202], [76.553187, 34.72584], [76.63959, 34.741136], [76.652613, 34.746976], [76.743873, 34.819271], [76.753278, 34.83834], [76.749558, 34.892083], [76.757619, 34.915131], [76.782011, 34.930892], [76.817047, 34.940762], [76.852601, 34.943346], [76.879576, 34.937093], [76.905931, 34.923141], [76.921123, 34.920712], [76.933836, 34.928877], [76.952956, 34.94655], [77.013108, 34.986393], [77.02861, 35.003394], [77.035225, 35.034503], [77.02706, 35.062099], [77.023133, 35.086335], [77.042426, 35.107014], [77.048971, 35.110442], [77.424659, 35.302924], [77.800346, 35.495406]]], [[[80.968272, 15.795233], [80.983165, 15.780748], [80.988536, 15.770738], [80.984874, 15.761786], [80.97169, 15.750474], [80.942638, 15.741034], [80.942556, 15.741075], [80.917979, 15.755113], [80.8685, 15.811957], [80.879161, 15.840074], [80.888357, 15.846747], [80.937266, 15.818915], [80.968272, 15.795233]]], [[[73.646332, 10.084703], [73.644054, 10.076972], [73.640961, 10.069322], [73.637055, 10.062567], [73.632579, 10.057278], [73.631684, 10.067776], [73.633311, 10.077867], [73.637462, 10.087063], [73.643891, 10.09455], [73.643972, 10.094468], [73.645193, 10.09219], [73.646332, 10.084703]]], [[[73.662283, 10.144599], [73.662201, 10.144517], [73.66033, 10.142524], [73.659353, 10.13996], [73.657888, 10.139065], [73.654633, 10.141506], [73.65561, 10.144761], [73.657481, 10.145087], [73.659679, 10.144355], [73.662283, 10.144599]]], [[[72.64975, 10.56684], [72.63852, 10.559394], [72.633067, 10.556545], [72.627533, 10.554796], [72.627452, 10.554755], [72.632091, 10.560004], [72.648611, 10.573961], [72.648692, 10.57392], [72.650564, 10.572577], [72.650401, 10.571194], [72.649587, 10.569322], [72.64975, 10.56684]]], [[[72.548839, 10.779446], [72.548757, 10.779324], [72.541189, 10.772895], [72.540863, 10.776516], [72.542491, 10.777899], [72.545421, 10.778306], [72.548839, 10.779446]]], [[[73.671397, 10.829739], [73.681244, 10.82807], [73.684418, 10.827541], [73.67628, 10.824368], [73.672862, 10.824693], [73.671886, 10.827094], [73.671397, 10.829657], [73.671397, 10.829739]]], [[[72.750011, 11.119086], [72.747569, 11.115424], [72.746755, 11.111965], [72.744965, 11.109524], [72.739513, 11.108832], [72.739513, 11.113959], [72.742035, 11.115953], [72.745779, 11.116889], [72.750011, 11.119086]]], [[[73.013194, 11.502183], [73.010916, 11.490139], [73.0088, 11.484687], [73.005626, 11.479641], [73.004405, 11.488837], [73.004405, 11.496487], [73.007091, 11.501288], [73.013194, 11.502183]]], [[[72.716156, 11.694159], [72.716075, 11.694078], [72.708669, 11.689154], [72.705251, 11.687323], [72.701101, 11.686428], [72.700938, 11.686428], [72.705089, 11.692328], [72.709239, 11.694159], [72.716156, 11.694159]]], [[[93.872813, 12.268866], [93.85377, 12.260565], [93.851899, 12.271552], [93.860606, 12.281195], [93.872813, 12.268866]]], [[[94.280935, 13.443549], [94.290782, 13.422919], [94.274913, 13.420315], [94.261892, 13.429267], [94.280935, 13.443549]]], [[[93.920258, 7.024319], [93.928722, 7.012356], [93.929861, 6.959052], [93.921072, 6.933824], [93.893565, 6.876776], [93.889415, 6.841783], [93.892833, 6.834052], [93.899262, 6.827135], [93.904145, 6.819241], [93.903087, 6.808295], [93.898448, 6.804185], [93.889903, 6.800971], [93.880056, 6.800442], [93.871755, 6.804511], [93.859386, 6.804267], [93.847911, 6.785305], [93.834239, 6.745551], [93.812673, 6.753974], [93.809337, 6.769029], [93.813243, 6.788031], [93.813731, 6.808295], [93.805431, 6.816718], [93.792166, 6.825873], [93.784434, 6.836412], [93.793224, 6.849189], [93.783702, 6.857978], [93.775727, 6.867377], [93.773123, 6.877997], [93.779552, 6.890204], [93.770763, 6.893866], [93.766124, 6.901109], [93.764496, 6.911526], [93.76531, 6.924302], [93.748057, 6.92357], [93.737478, 6.943264], [93.72877, 6.970526], [93.71754, 6.992621], [93.703624, 7.006293], [93.684337, 7.020901], [93.664561, 7.026516], [93.649181, 7.013088], [93.649669, 7.119615], [93.653575, 7.131496], [93.676443, 7.181871], [93.685395, 7.184516], [93.70753, 7.184394], [93.717296, 7.18769], [93.72462, 7.194973], [93.730154, 7.202216], [93.73463, 7.205512], [93.739919, 7.207099], [93.76531, 7.219143], [93.772146, 7.211737], [93.797618, 7.234605], [93.813975, 7.241848], [93.830821, 7.235907], [93.855317, 7.214179], [93.865001, 7.20067], [93.870372, 7.180569], [93.87672, 7.16356], [93.880626, 7.144273], [93.883149, 7.104722], [93.887055, 7.089301], [93.903087, 7.060858], [93.912934, 7.035102], [93.920258, 7.024319]]], [[[93.731212, 7.369371], [93.734386, 7.350816], [93.728526, 7.337104], [93.715505, 7.325832], [93.696951, 7.314765], [93.67921, 7.298326], [93.644216, 7.257514], [93.628184, 7.253323], [93.617931, 7.259955], [93.616384, 7.267279], [93.617442, 7.276068], [93.615082, 7.287421], [93.61085, 7.294501], [93.594005, 7.314765], [93.606944, 7.326402], [93.610362, 7.347317], [93.61671, 7.367255], [93.638682, 7.376166], [93.667247, 7.382758], [93.677989, 7.3994], [93.683767, 7.421698], [93.696951, 7.445054], [93.696951, 7.417792], [93.699474, 7.398749], [93.709483, 7.388739], [93.72169, 7.381171], [93.731212, 7.369371]]], [[[93.429454, 7.955268], [93.433116, 7.954291], [93.459809, 7.910793], [93.46046, 7.899848], [93.457367, 7.880194], [93.454438, 7.869289], [93.447927, 7.871405], [93.43686, 7.883287], [93.421235, 7.891791], [93.41505, 7.892239], [93.40211, 7.890082], [93.389171, 7.88581], [93.378429, 7.880764], [93.367849, 7.878607], [93.354259, 7.883287], [93.344981, 7.891343], [93.340343, 7.901272], [93.341075, 7.912584], [93.348155, 7.924872], [93.340668, 7.931708], [93.332693, 7.924954], [93.323009, 7.924384], [93.31365, 7.929185], [93.306651, 7.938544], [93.305024, 7.949937], [93.313324, 7.989732], [93.322602, 8.002102], [93.344412, 8.012112], [93.370291, 8.01732], [93.391368, 8.01557], [93.394786, 8.006741], [93.396169, 7.997219], [93.396007, 7.976386], [93.401215, 7.967963], [93.424164, 7.959866], [93.429454, 7.955268]]], [[[93.565929, 7.999335], [93.570323, 7.989], [93.574229, 7.968817], [93.573985, 7.924872], [93.518321, 7.965806], [93.510753, 7.98017], [93.511485, 7.99551], [93.520763, 8.004381], [93.539317, 7.999335], [93.540294, 8.016181], [93.54835, 8.020087], [93.558604, 8.013617], [93.565929, 7.999335]]], [[[93.531912, 8.034735], [93.528005, 8.032416], [93.514903, 8.027655], [93.511974, 8.024156], [93.509776, 8.018541], [93.494965, 7.996283], [93.484386, 7.991889], [93.479259, 8.002916], [93.477306, 8.030992], [93.480805, 8.040513], [93.488536, 8.044379], [93.495616, 8.049384], [93.497813, 8.061998], [93.505219, 8.061998], [93.514822, 8.05622], [93.516938, 8.0633], [93.511485, 8.074856], [93.497813, 8.082506], [93.492198, 8.063625], [93.479747, 8.063218], [93.466319, 8.074408], [93.457367, 8.089993], [93.446625, 8.154364], [93.446544, 8.164496], [93.454845, 8.170111], [93.465099, 8.183295], [93.473643, 8.198188], [93.477306, 8.209133], [93.48585, 8.22012], [93.504649, 8.226996], [93.523448, 8.225816], [93.531668, 8.213202], [93.531016, 8.193752], [93.521495, 8.179104], [93.510509, 8.166571], [93.505219, 8.14704], [93.508556, 8.128811], [93.525157, 8.095038], [93.537608, 8.057318], [93.537364, 8.047797], [93.531912, 8.034735]]], [[[73.077159, 8.315253], [73.081798, 8.315823], [73.083018, 8.314358], [73.083018, 8.311672], [73.083995, 8.308498], [73.066091, 8.270453], [73.056651, 8.259996], [73.044688, 8.252427], [73.032725, 8.249498], [73.024099, 8.255439], [73.022472, 8.274359], [73.029307, 8.274319], [73.029307, 8.274115], [73.028982, 8.263414], [73.029307, 8.259996], [73.04835, 8.267564], [73.063162, 8.279486], [73.073009, 8.295396], [73.077159, 8.315253]]], [[[93.134532, 8.24018], [93.160818, 8.228746], [93.173025, 8.221259], [93.17628, 8.212877], [93.167328, 8.204535], [93.153494, 8.205512], [93.124685, 8.212877], [93.10255, 8.221503], [93.081554, 8.242377], [93.065766, 8.268134], [93.059581, 8.291409], [93.06129, 8.320746], [93.064301, 8.336127], [93.069835, 8.346625], [93.083751, 8.355902], [93.095063, 8.35517], [93.10141, 8.345771], [93.100759, 8.299262], [93.104015, 8.27558], [93.114106, 8.256415], [93.134532, 8.24018]]], [[[93.607677, 8.548041], [93.621755, 8.520087], [93.626801, 8.505845], [93.628184, 8.486558], [93.625173, 8.473619], [93.618337, 8.453925], [93.609711, 8.438422], [93.60141, 8.438178], [93.600352, 8.450344], [93.607107, 8.49079], [93.607677, 8.507066], [93.595551, 8.541734], [93.594981, 8.55801], [93.607677, 8.569159], [93.607677, 8.548041]]], [[[92.811373, 9.23117], [92.833849, 9.202359], [92.831981, 9.145025], [92.799565, 9.110154], [92.731212, 9.130845], [92.720225, 9.147121], [92.711681, 9.175116], [92.708507, 9.201606], [92.713878, 9.213446], [92.729259, 9.217841], [92.745128, 9.229071], [92.754405, 9.244452], [92.750987, 9.261217], [92.763031, 9.264146], [92.772716, 9.259223], [92.795272, 9.240818], [92.811373, 9.23117]]], [[[92.585092, 10.771269], [92.586986, 10.736294], [92.598059, 10.709302], [92.595908, 10.678364], [92.574492, 10.655484], [92.56124, 10.642555], [92.551056, 10.634603], [92.539835, 10.620669], [92.54176, 10.59172], [92.555964, 10.588675], [92.574213, 10.580624], [92.546675, 10.547781], [92.535423, 10.524861], [92.513027, 10.507969], [92.479558, 10.516073], [92.463372, 10.531103], [92.444139, 10.545143], [92.421788, 10.546203], [92.406466, 10.529266], [92.384111, 10.529328], [92.378057, 10.541329], [92.402557, 10.575206], [92.413855, 10.606117], [92.417003, 10.629059], [92.408917, 10.647055], [92.390674, 10.663085], [92.379486, 10.672122], [92.378586, 10.718071], [92.378671, 10.750037], [92.37568, 10.775031], [92.376721, 10.78402], [92.385878, 10.781981], [92.407265, 10.789891], [92.41542, 10.798863], [92.422598, 10.822836], [92.428966, 10.850816], [92.482796, 10.885622], [92.527592, 10.895466], [92.54888, 10.874373], [92.56506, 10.849308], [92.567981, 10.815301], [92.582116, 10.790261], [92.585092, 10.771269]]], [[[92.676524, 11.412543], [92.685069, 11.399359], [92.696788, 11.391791], [92.69988, 11.384426], [92.682791, 11.371527], [92.673188, 11.367336], [92.646007, 11.359076], [92.63502, 11.357896], [92.61964, 11.360745], [92.61085, 11.366156], [92.60377, 11.372504], [92.593516, 11.378363], [92.598969, 11.401313], [92.600841, 11.406317], [92.608165, 11.411322], [92.623871, 11.412502], [92.631196, 11.416246], [92.637218, 11.430162], [92.635265, 11.447008], [92.627615, 11.477362], [92.63559, 11.508043], [92.654145, 11.508124], [92.675059, 11.490424], [92.689708, 11.46776], [92.6963, 11.454413], [92.689708, 11.442532], [92.679698, 11.429389], [92.676524, 11.412543]]], [[[92.266775, 11.589545], [92.278087, 11.583808], [92.286876, 11.563178], [92.285004, 11.544013], [92.273204, 11.531073], [92.252126, 11.529242], [92.234548, 11.540351], [92.230235, 11.577338], [92.217296, 11.591254], [92.217296, 11.59809], [92.228689, 11.597724], [92.254893, 11.593817], [92.266775, 11.589545]]], [[[93.042491, 11.961168], [93.048676, 11.952786], [93.066417, 11.899115], [93.049327, 11.896145], [93.035411, 11.911933], [93.026541, 11.931627], [93.024913, 11.940131], [93.015391, 11.94599], [93.001313, 11.949205], [92.990571, 11.954413], [92.980235, 11.96369], [92.974457, 11.972235], [92.96754, 11.978583], [92.953136, 11.981106], [92.947032, 11.989691], [92.957205, 12.008694], [92.972179, 12.027737], [92.980724, 12.036322], [93.014659, 12.037177], [93.025401, 12.031562], [93.018565, 12.015815], [93.040375, 11.969062], [93.042491, 11.961168]]], [[[93.103282, 12.190497], [93.10377, 12.187934], [93.103852, 12.185289], [93.103282, 12.18008], [93.096202, 12.158759], [93.094981, 12.15176], [93.102306, 12.119534], [93.103282, 12.102037], [93.094249, 12.090969], [93.08546, 12.095852], [93.074229, 12.110338], [93.064301, 12.127143], [93.059581, 12.138739], [93.059337, 12.153551], [93.061534, 12.172309], [93.066254, 12.189846], [93.073253, 12.200832], [93.082042, 12.204779], [93.091075, 12.20425], [93.098481, 12.199408], [93.103282, 12.190497]]], [[[92.733084, 12.97248], [92.737478, 12.961737], [92.732188, 12.945705], [92.729991, 12.895209], [92.724376, 12.872382], [92.717621, 12.862209], [92.701508, 12.844428], [92.697113, 12.838202], [92.695079, 12.8251], [92.696544, 12.8133], [92.694672, 12.803778], [92.682791, 12.797309], [92.670258, 12.867662], [92.672862, 12.878607], [92.677745, 12.890367], [92.684906, 12.945054], [92.682791, 12.961737], [92.695079, 12.973456], [92.702403, 12.983059], [92.708344, 12.988674], [92.71697, 12.988471], [92.722504, 12.985785], [92.727061, 12.982001], [92.730724, 12.977484], [92.733084, 12.97248]]], [[[93.052745, 13.283881], [93.075694, 13.26911], [93.079438, 13.259833], [93.056895, 13.224799], [93.052582, 13.196967], [93.052745, 13.133043], [93.048106, 13.103705], [93.036794, 13.062161], [93.019542, 13.033637], [92.998057, 13.043687], [92.990571, 13.043687], [92.986827, 13.033677], [92.97934, 13.029527], [92.970876, 13.030748], [92.964041, 13.036851], [92.961436, 13.048163], [92.966563, 13.07217], [92.964041, 13.084662], [92.956554, 13.084662], [92.956554, 13.064154], [92.937022, 13.067084], [92.928884, 13.050198], [92.928396, 13.027493], [92.932628, 13.013007], [92.950694, 12.993638], [92.956228, 12.981676], [92.952647, 12.970771], [92.942882, 12.954291], [92.936046, 12.954291], [92.934744, 12.973049], [92.927419, 12.981024], [92.916515, 12.982652], [92.905284, 12.982245], [92.902599, 12.974026], [92.880707, 12.930406], [92.87908, 12.921942], [92.876313, 12.916653], [92.876801, 12.911526], [92.884532, 12.903469], [92.896332, 12.899726], [92.905528, 12.906073], [92.915538, 12.919582], [92.92449, 12.914374], [92.928233, 12.905341], [92.929861, 12.896674], [92.932628, 12.89289], [92.940684, 12.889309], [92.964041, 12.864936], [92.960704, 12.847154], [92.965831, 12.825629], [92.968028, 12.803168], [92.956554, 12.783026], [92.973155, 12.76435], [92.984386, 12.745103], [92.990489, 12.724311], [92.990571, 12.701117], [92.976817, 12.654934], [92.972911, 12.628119], [92.989024, 12.592108], [92.991547, 12.567084], [92.990571, 12.519232], [92.9817, 12.506049], [92.964854, 12.491034], [92.955903, 12.477444], [92.970225, 12.468329], [92.962413, 12.454413], [92.949718, 12.446967], [92.93865, 12.438137], [92.936046, 12.419908], [92.92921, 12.419908], [92.923839, 12.430976], [92.915863, 12.440863], [92.906016, 12.448961], [92.89503, 12.454657], [92.897146, 12.449937], [92.901866, 12.433539], [92.873302, 12.431383], [92.84669, 12.433539], [92.84669, 12.427395], [92.853526, 12.416815], [92.863617, 12.385484], [92.871104, 12.378892], [92.881847, 12.375922], [92.894054, 12.368109], [92.902517, 12.357123], [92.901866, 12.344794], [92.91505, 12.338365], [92.912771, 12.333645], [92.903168, 12.329088], [92.89503, 12.323676], [92.886404, 12.315741], [92.881114, 12.314602], [92.879242, 12.312974], [92.880707, 12.30386], [92.885753, 12.295559], [92.893403, 12.290351], [92.900157, 12.283189], [92.901866, 12.269029], [92.896251, 12.261705], [92.886404, 12.253363], [92.879893, 12.243598], [92.884532, 12.231838], [92.888682, 12.225002], [92.892589, 12.214545], [92.894216, 12.205024], [92.891612, 12.200832], [92.872569, 12.201483], [92.874522, 12.183743], [92.864024, 12.171536], [92.842947, 12.156195], [92.827403, 12.140123], [92.833669, 12.125678], [92.833669, 12.118232], [92.825938, 12.115668], [92.806407, 12.10456], [92.80893, 12.095771], [92.799327, 12.089545], [92.783946, 12.085883], [92.768809, 12.084703], [92.757498, 12.07746], [92.767263, 12.061225], [92.792654, 12.036322], [92.781016, 12.037787], [92.771251, 12.036078], [92.763682, 12.030951], [92.758556, 12.02204], [92.765391, 12.02204], [92.745616, 11.996649], [92.740571, 11.983629], [92.737478, 11.961168], [92.737478, 11.878648], [92.744884, 11.878648], [92.749034, 11.912258], [92.753754, 11.931586], [92.761974, 11.940131], [92.776378, 11.939276], [92.787934, 11.934516], [92.795909, 11.922187], [92.79949, 11.899115], [92.796886, 11.865139], [92.76002, 11.708686], [92.754731, 11.701117], [92.734548, 11.699286], [92.722911, 11.693793], [92.689952, 11.660061], [92.68393, 11.649888], [92.682628, 11.636949], [92.682791, 11.61518], [92.687022, 11.611558], [92.696788, 11.611233], [92.706391, 11.614488], [92.710704, 11.621731], [92.710704, 11.659573], [92.715343, 11.65762], [92.726736, 11.65469], [92.731212, 11.652737], [92.730235, 11.671454], [92.736013, 11.68244], [92.746104, 11.683743], [92.758556, 11.673245], [92.761485, 11.663642], [92.765636, 11.629584], [92.765391, 11.618598], [92.760509, 11.610338], [92.747569, 11.597846], [92.744884, 11.587592], [92.744884, 11.557115], [92.735118, 11.523871], [92.726736, 11.506171], [92.71697, 11.494452], [92.713145, 11.50731], [92.707042, 11.511054], [92.698985, 11.511786], [92.689708, 11.51557], [92.658946, 11.546291], [92.654796, 11.555894], [92.65561, 11.565985], [92.654633, 11.573798], [92.645274, 11.576972], [92.633556, 11.582587], [92.625662, 11.595404], [92.621593, 11.609361], [92.621349, 11.618598], [92.62794, 11.625149], [92.63738, 11.627143], [92.645681, 11.629828], [92.648692, 11.638495], [92.642833, 11.64525], [92.631114, 11.647447], [92.61964, 11.651313], [92.614513, 11.662991], [92.613455, 11.674221], [92.608165, 11.693264], [92.607107, 11.704535], [92.60377, 11.718899], [92.595958, 11.72016], [92.586762, 11.71601], [92.579845, 11.714179], [92.563243, 11.725572], [92.560313, 11.74136], [92.566661, 11.7862], [92.566661, 11.827135], [92.563243, 11.83393], [92.554942, 11.837836], [92.545665, 11.840806], [92.534353, 11.846869], [92.530935, 11.845893], [92.528087, 11.845893], [92.525157, 11.851304], [92.524913, 11.857489], [92.527354, 11.862006], [92.530528, 11.864651], [92.531993, 11.864976], [92.539724, 11.894436], [92.562999, 11.930162], [92.570567, 11.932603], [92.577159, 11.927395], [92.592133, 11.905748], [92.594737, 11.898749], [92.600271, 11.895494], [92.614513, 11.899115], [92.607677, 11.882799], [92.61085, 11.87051], [92.617686, 11.8664], [92.621349, 11.874945], [92.624522, 11.930162], [92.629405, 11.943752], [92.627289, 11.95893], [92.623057, 11.97427], [92.621349, 11.987942], [92.625011, 12.003119], [92.63738, 12.025295], [92.641856, 12.036322], [92.640147, 12.136135], [92.645274, 12.156073], [92.668224, 12.200995], [92.681814, 12.219631], [92.689708, 12.207668], [92.697113, 12.207668], [92.703868, 12.225165], [92.713064, 12.232571], [92.720958, 12.226549], [92.724376, 12.20425], [92.719086, 12.184516], [92.719493, 12.176256], [92.727794, 12.172838], [92.733735, 12.174018], [92.736339, 12.176581], [92.737804, 12.179145], [92.741059, 12.180325], [92.760997, 12.179877], [92.765391, 12.180325], [92.784028, 12.189358], [92.788829, 12.195543], [92.792654, 12.207668], [92.7824, 12.248603], [92.775564, 12.250637], [92.767751, 12.255439], [92.761241, 12.261176], [92.758556, 12.265937], [92.7588, 12.276557], [92.760427, 12.276842], [92.764496, 12.274319], [92.781016, 12.279242], [92.789399, 12.279486], [92.7942, 12.283189], [92.792654, 12.296373], [92.786957, 12.306301], [92.778494, 12.310126], [92.754731, 12.310045], [92.733653, 12.318061], [92.722341, 12.337795], [92.717784, 12.362494], [92.72169, 12.599433], [92.726817, 12.612982], [92.744802, 12.63349], [92.750173, 12.644273], [92.754731, 12.649563], [92.76002, 12.651109], [92.775238, 12.65176], [92.778982, 12.652655], [92.783214, 12.664984], [92.777354, 12.669338], [92.766287, 12.671536], [92.754731, 12.677191], [92.747732, 12.692694], [92.744884, 12.762519], [92.737478, 12.807196], [92.740896, 12.819648], [92.748871, 12.828111], [92.758149, 12.835679], [92.765391, 12.84512], [92.785655, 12.840033], [92.800304, 12.857815], [92.809337, 12.884508], [92.813243, 12.90648], [92.813243, 12.958075], [92.828136, 12.985175], [92.834727, 13.002021], [92.830251, 13.009508], [92.807953, 13.020087], [92.809255, 13.044745], [92.8296, 13.103461], [92.848399, 13.136705], [92.853038, 13.142727], [92.854177, 13.146064], [92.851085, 13.152045], [92.837657, 13.16706], [92.833669, 13.174018], [92.844737, 13.186998], [92.842052, 13.20775], [92.842784, 13.226996], [92.864024, 13.2355], [92.863943, 13.244696], [92.849864, 13.289008], [92.854177, 13.304389], [92.854177, 13.310614], [92.841156, 13.324897], [92.847423, 13.344916], [92.886892, 13.393459], [92.891368, 13.400946], [92.89503, 13.413031], [92.895681, 13.422838], [92.894542, 13.447252], [92.89503, 13.454576], [92.901866, 13.468248], [92.903331, 13.46776], [92.915538, 13.475735], [92.937999, 13.50137], [92.942882, 13.509223], [92.945486, 13.519721], [92.945486, 13.536933], [92.949718, 13.544623], [92.958507, 13.550238], [92.977224, 13.553778], [92.984386, 13.557603], [92.99171, 13.550035], [92.997325, 13.546698], [93.003184, 13.547065], [93.011729, 13.550767], [93.006114, 13.556627], [93.004568, 13.562201], [93.006521, 13.568793], [93.011729, 13.578111], [93.016287, 13.567369], [93.022797, 13.566107], [93.031586, 13.569078], [93.042491, 13.571234], [93.049978, 13.566392], [93.066417, 13.537095], [93.055024, 13.503079], [93.053722, 13.471422], [93.059581, 13.406806], [93.064138, 13.408637], [93.07545, 13.411119], [93.080089, 13.413031], [93.07838, 13.397121], [93.057872, 13.38288], [93.052745, 13.368883], [93.046886, 13.357408], [93.033458, 13.348578], [93.017345, 13.340481], [93.004242, 13.331041], [93.003673, 13.348619], [92.995128, 13.357367], [92.984386, 13.356431], [92.977061, 13.345364], [92.97934, 13.329779], [92.990489, 13.317816], [93.004568, 13.311835], [93.025564, 13.316148], [93.035655, 13.306952], [93.052745, 13.283881]]], [[[93.039073, 13.667467], [93.042817, 13.662055], [93.052745, 13.640204], [93.019542, 13.6494], [93.000499, 13.657701], [92.990571, 13.667467], [93.019542, 13.680121], [93.033214, 13.680243], [93.039073, 13.667467]]], [[[88.872895, 21.595526], [88.88852, 21.589545], [88.902029, 21.580268], [88.911306, 21.566881], [88.91391, 21.548407], [88.90797, 21.530707], [88.895763, 21.528469], [88.879731, 21.529975], [88.862478, 21.523586], [88.838715, 21.531928], [88.830821, 21.577379], [88.841319, 21.614], [88.872895, 21.595526]]], [[[88.050548, 21.70539], [88.090017, 21.808661], [88.119314, 21.859524], [88.146739, 21.869859], [88.167003, 21.765448], [88.163422, 21.705471], [88.132579, 21.684963], [88.144379, 21.657782], [88.146007, 21.643744], [88.139415, 21.630316], [88.130056, 21.625922], [88.1185, 21.627672], [88.091563, 21.636542], [88.063487, 21.636664], [88.051768, 21.641588], [88.043142, 21.65762], [88.042247, 21.668158], [88.043956, 21.680854], [88.050548, 21.70539]]], [[[88.636241, 21.921373], [88.650157, 21.927436], [88.663259, 21.921291], [88.670584, 21.907416], [88.667003, 21.890326], [88.655284, 21.866604], [88.649181, 21.842353], [88.646332, 21.787909], [88.636241, 21.79267], [88.59962, 21.803656], [88.591807, 21.804389], [88.590505, 21.812893], [88.584972, 21.826606], [88.584321, 21.835761], [88.586274, 21.843004], [88.598643, 21.869859], [88.616466, 21.897935], [88.636241, 21.921373]]], [[[88.080903, 21.849351], [88.072276, 21.856757], [88.071056, 21.873603], [88.073904, 21.892157], [88.077322, 21.904608], [88.084727, 21.921576], [88.094249, 21.937893], [88.105724, 21.947577], [88.118907, 21.945014], [88.132579, 21.920478], [88.125011, 21.888658], [88.104991, 21.861151], [88.080903, 21.849351]]], [[[72.172699, 10.809231], [72.172618, 10.829983], [72.179047, 10.8487], [72.185151, 10.858303], [72.189301, 10.864936], [72.195649, 10.872707], [72.200043, 10.878079], [72.200043, 10.864447], [72.188162, 10.836127], [72.172699, 10.809231]]], [[[72.789073, 11.261664], [72.789236, 11.261664], [72.795909, 11.261664], [72.795584, 11.257229], [72.793956, 11.254299], [72.791515, 11.251654], [72.789073, 11.248033], [72.787934, 11.232245], [72.784923, 11.216376], [72.775483, 11.186754], [72.775401, 11.186591], [72.771169, 11.201117], [72.770356, 11.20364], [72.774181, 11.222886], [72.789073, 11.261664]]]]}}]}"
  },
  {
    "path": "static/js/api.js",
    "content": "/**\r\n * api.js - Backend API Communication Module for India LIMS\r\n * All fetch calls to the Flask REST API are centralized here.\r\n */\r\n\r\nconst API_BASE = '';  // Same origin; empty string for relative paths\r\n\r\n// Shared fetch options to include session cookies\r\nconst FETCH_OPTS = {\r\n    credentials: 'include'\r\n};\r\n\r\n// --- Authentication Endpoints ---\r\nasync function verifyCaptcha(answer, token) {\r\n    const res = await fetch(`${API_BASE}/api/verify-captcha`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ answer: answer, token: token })\r\n    });\r\n    return res.json();\r\n}\r\n\r\nasync function getCaptcha() {\r\n    const res = await fetch(`${API_BASE}/api/captcha`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    return res.json();\r\n}\r\n\r\nasync function adminLogin(username, password) {\r\n    const res = await fetch(`${API_BASE}/api/login`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ username, password })\r\n    });\r\n    return res.json();\r\n}\r\n\r\nasync function logout() {\r\n    const res = await fetch(`${API_BASE}/api/logout`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' }\r\n    });\r\n    return res.json();\r\n}\r\n\r\nasync function getSessionInfo() {\r\n    const res = await fetch(`${API_BASE}/api/session-info`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    return res.json();\r\n}\r\n\r\nasync function forgotPassword() {\r\n    const res = await fetch(`${API_BASE}/api/forgot`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    return res.json();\r\n}\r\n\r\n// --- Records Endpoints ---\r\nasync function fetchRecords() {\r\n    const res = await fetch(`${API_BASE}/api/records`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to fetch records');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function fetchRecord(recordId) {\r\n    const res = await fetch(`${API_BASE}/api/records/${recordId}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Record not found');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function searchRecords(query) {\r\n    const res = await fetch(`${API_BASE}/api/records/search?q=${encodeURIComponent(query)}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Search failed');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function fetchLocationCatalog() {\r\n    const res = await fetch(`${API_BASE}/api/location-catalog`, FETCH_OPTS);\r\n    if (!res.ok) throw new Error('Failed to fetch location catalog');\r\n    return await res.json();\r\n}\r\n\r\nasync function createRecord(recordData) {\r\n    const res = await fetch(`${API_BASE}/api/records`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify(recordData)\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to create record');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function updateRecord(recordId, updateData) {\r\n    const res = await fetch(`${API_BASE}/api/records/${recordId}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'PUT',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify(updateData)\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to update record');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function deleteRecord(recordId) {\r\n    const res = await fetch(`${API_BASE}/api/records/${recordId}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'DELETE'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to delete record');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function restoreRecord(recordId) {\r\n    const res = await fetch(`${API_BASE}/api/records/${recordId}/restore`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to restore record');\r\n    }\r\n    return res.json();\r\n}\r\n\r\n// --- GIS Processing Endpoints ---\r\nasync function calculateArea(geometry) {\r\n    const res = await fetch(`${API_BASE}/api/calculate-area`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ geometry })\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Area calculation failed');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function validateGeometry(geometry) {\r\n    const res = await fetch(`${API_BASE}/api/validate-geometry`, {\r\n        ...FETCH_OPTS,\r\n        method: 'POST',\r\n        headers: { 'Content-Type': 'application/json' },\r\n        body: JSON.stringify({ geometry })\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Validation failed');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function fetchLocationFromCoordinates(lat, lng) {\r\n    const res = await fetch(`${API_BASE}/api/location-from-coords?lat=${encodeURIComponent(lat)}&lng=${encodeURIComponent(lng)}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Reverse geocoding failed');\r\n    }\r\n\r\n    return res.json();\r\n}\r\n\r\n// --- Server-Side Filtering & Analytics (Python-heavy) ---\r\n/**\r\n * Server-side filtering of records.\r\n * @param {Object} filters - { state, district, village, land_use, search }\r\n * @returns {Promise<Array>} - Filtered records\r\n */\r\nasync function fetchFilteredRecords(filters = {}) {\r\n    const params = new URLSearchParams();\r\n    if (filters.state) params.set('state', filters.state);\r\n    if (filters.district) params.set('district', filters.district);\r\n    if (filters.village) params.set('village', filters.village);\r\n    if (filters.land_use) params.set('land_use', filters.land_use);\r\n    if (filters.search) params.set('search', filters.search);\r\n    \r\n    const res = await fetch(`${API_BASE}/api/records/filter?${params}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Filter failed');\r\n    }\r\n    return res.json();\r\n}\r\n\r\n/**\r\n * Pre-computed dashboard analytics from server.\r\n * @param {Object} filters - Same as fetchFilteredRecords\r\n * @returns {Promise<Object>} - { kpis, land_use_distribution, district_overview, top_parcel, recent_mutations }\r\n */\r\nasync function fetchDashboardAnalytics(filters = {}) {\r\n    const params = new URLSearchParams();\r\n    if (filters.state) params.set('state', filters.state);\r\n    if (filters.district) params.set('district', filters.district);\r\n    if (filters.village) params.set('village', filters.village);\r\n    if (filters.land_use) params.set('land_use', filters.land_use);\r\n    if (filters.search) params.set('search', filters.search);\r\n    \r\n    const res = await fetch(`${API_BASE}/api/dashboard?${params}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Dashboard analytics failed');\r\n    }\r\n    return res.json();\r\n}\r\n\r\n\r\n/**\r\n * Get application config (land use colors, options, etc.).\r\n * @returns {Promise<Object>} - { land_use_options, land_use_colors, mutation_types }\r\n */\r\nasync function fetchAppConfig() {\r\n    const res = await fetch(`${API_BASE}/api/config`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to fetch app config');\r\n    }\r\n    return res.json();\r\n}\r\n\r\nasync function fetchAudit(limit = 50) {\r\n    const res = await fetch(`${API_BASE}/api/audit?limit=${encodeURIComponent(limit)}`, {\r\n        ...FETCH_OPTS,\r\n        method: 'GET'\r\n    });\r\n    if (!res.ok) {\r\n        const err = await res.json();\r\n        throw new Error(err.error || 'Failed to fetch audit log');\r\n    }\r\n    return res.json();\r\n}\r\n\r\n// --- Document Generation Endpoints ---\r\nfunction getPropertyCardUrl(ulpin) { return `${API_BASE}/api/print-card/${ulpin}`; }\r\n\r\nfunction getVillageExcelUrl(village) {\r\n    const params = village ? `?village=${encodeURIComponent(village)}` : '';\r\n    return `${API_BASE}/api/export-village${params}`;\r\n}\r\n\r\nfunction downloadFile(url, filename) {\r\n    const a = document.createElement('a');\r\n    a.href = url;\r\n    a.download = filename || 'download';\r\n    document.body.appendChild(a);\r\n    a.click();\r\n    document.body.removeChild(a);\r\n}\r\n\r\n// --- Utility ---\r\nfunction showToast(message, type = 'info', duration = 3000) {\r\n    const toast = document.getElementById('toast');\r\n    const msgEl = document.getElementById('toast-msg');\r\n    const iconEl = document.getElementById('toast-icon');\r\n\r\n    if (!toast || !msgEl) return;\r\n\r\n    const icons = {\r\n        success: '<svg class=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 20 20\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"/></svg>',\r\n        error: '<svg class=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 20 20\"><path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z\" clip-rule=\"evenodd\"/></svg>',\r\n        info: '<svg class=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 20 20\"><path fill-rule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\"/></svg>',\r\n        warning: '<svg class=\"w-4 h-4\" fill=\"currentColor\" viewBox=\"0 0 20 20\"><path fill-rule=\"evenodd\" d=\"M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z\" clip-rule=\"evenodd\"/></svg>'\r\n    };\r\n\r\n    if (iconEl) iconEl.innerHTML = icons[type] || icons.info;\r\n    msgEl.textContent = message;\r\n\r\n    toast.classList.add('show');\r\n    toast.className = toast.className.replace(/success|error|info|warning/g, '').trim();\r\n    toast.classList.add(type);\r\n\r\n    setTimeout(() => {\r\n        toast.classList.remove('show');\r\n    }, duration);\r\n}\r\n\r\n// --- Real-Time Clock ---\r\nfunction updateRealTimeClock() {\r\n    const timeEl = document.getElementById('rtc-time');\r\n    const dateEl = document.getElementById('rtc-date');\r\n    if (!timeEl || !dateEl) return;\r\n\r\n    const now = new Date();\r\n    \r\n    // Time formatted as HH:MM:SS AM/PM\r\n    const timeString = now.toLocaleTimeString('en-US', {\r\n        hour: '2-digit', \r\n        minute: '2-digit', \r\n        second: '2-digit', \r\n        hour12: true \r\n    });\r\n\r\n    // Date formatted as DD MMM YYYY (e.g. 14 Apr 2026)\r\n    const dateString = now.toLocaleDateString('en-GB', {\r\n        day: '2-digit',\r\n        month: 'short',\r\n        year: 'numeric'\r\n    }).replace(/ /g, ' ');\r\n\r\n    timeEl.textContent = timeString;\r\n    dateEl.textContent = dateString;\r\n}\r\n\r\n// Initialize clock if elements exist\r\ndocument.addEventListener('DOMContentLoaded', () => {\r\n    if (document.getElementById('rtc-time')) {\r\n        updateRealTimeClock();\r\n        setInterval(updateRealTimeClock, 1000);\r\n    }\r\n});\r\n"
  },
  {
    "path": "static/js/auth.js",
    "content": "/**\r\n * auth.js - Login and CAPTCHA Handling for India LIMS\r\n * Handles the login page forms: CAPTCHA verification and Admin login.\r\n */\r\n\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n\r\n    // --- Only run session check on login page ---\r\n    const isLoginPage = window.location.pathname === '/login' || window.location.pathname === '/';\r\n    if (isLoginPage) {\r\n        checkSessionAndRedirect();\r\n    }\r\n\r\n    // --- Tab Navigation Handling ---\r\n    const tabPublic = document.getElementById('tab-public');\r\n    const tabAdmin = document.getElementById('tab-admin');\r\n    const panelPublic = document.getElementById('panel-public');\r\n    const panelAdmin = document.getElementById('panel-admin');\r\n\r\n    if (tabPublic && tabAdmin && panelPublic && panelAdmin) {\r\n        tabPublic.addEventListener('click', () => {\r\n            panelPublic.classList.remove('hidden');\r\n            panelAdmin.classList.add('hidden');\r\n            \r\n            // Active Public Tab\r\n            tabPublic.classList.add('text-emerald-700', 'border-emerald-500', 'bg-emerald-50/50');\r\n            tabPublic.classList.remove('text-gray-400', 'border-transparent', 'hover:text-orange-600', 'hover:bg-orange-50/30');\r\n            \r\n            // Inactive Admin Tab\r\n            tabAdmin.classList.add('text-gray-400', 'border-transparent', 'hover:text-orange-600', 'hover:bg-orange-50/30');\r\n            tabAdmin.classList.remove('text-orange-700', 'border-orange-500', 'bg-orange-50/50');\r\n        });\r\n\r\n        tabAdmin.addEventListener('click', () => {\r\n            panelAdmin.classList.remove('hidden');\r\n            panelPublic.classList.add('hidden');\r\n            \r\n            // Active Admin Tab\r\n            tabAdmin.classList.add('text-orange-700', 'border-orange-500', 'bg-orange-50/50');\r\n            tabAdmin.classList.remove('text-gray-400', 'border-transparent', 'hover:text-orange-600', 'hover:bg-orange-50/30');\r\n            \r\n            // Inactive Public Tab\r\n            tabPublic.classList.add('text-gray-400', 'border-transparent', 'hover:text-emerald-600', 'hover:bg-emerald-50/30');\r\n            tabPublic.classList.remove('text-emerald-700', 'border-emerald-500', 'bg-emerald-50/50');\r\n        });\r\n    }\r\n\r\n    // --- CAPTCHA Form Handling ---\r\n    const captchaForm = document.getElementById('captcha-form');\r\n    const captchaAnswer = document.getElementById('captcha-answer');\r\n    const captchaToken = document.getElementById('captcha-token');\r\n    const captchaError = document.getElementById('captcha-error');\r\n    const captchaQuestionEl = document.getElementById('captcha-question');\r\n\r\n    if (captchaForm) {\r\n        captchaForm.addEventListener('submit', async function(e) {\r\n            e.preventDefault();\r\n\r\n            const answer = captchaAnswer.value.trim();\r\n            const token = captchaToken.value.trim();\r\n            if (!answer) {\r\n                showCaptchaError('Please enter the answer.');\r\n                return;\r\n            }\r\n\r\n            // Disable button while processing\r\n            const submitBtn = captchaForm.querySelector('button[type=\"submit\"]');\r\n            const originalText = submitBtn.innerHTML;\r\n            submitBtn.disabled = true;\r\n            submitBtn.innerHTML = '<span class=\"spinner\"></span> Verifying...';\r\n\r\n            try {\r\n                const result = await verifyCaptcha(answer, token);\r\n\r\n                if (result.success) {\r\n                    // Replace current history entry so back button doesn't return to login\r\n                    window.location.replace(result.redirect || '/viewer');\r\n                } else {\r\n                    showCaptchaError(result.message || 'Incorrect answer. Please try again.');\r\n                    // Update CAPTCHA question if a new one was provided\r\n                    if (result.new_question && captchaQuestionEl) {\r\n                        captchaQuestionEl.textContent = result.new_question;\r\n                    }\r\n                    if (result.new_token && captchaToken) {\r\n                        captchaToken.value = result.new_token;\r\n                    }\r\n                    captchaAnswer.value = '';\r\n                    captchaAnswer.focus();\r\n                }\r\n            } catch (err) {\r\n                showCaptchaError('An error occurred. Please try again.');\r\n            } finally {\r\n                submitBtn.disabled = false;\r\n                submitBtn.innerHTML = originalText;\r\n            }\r\n        });\r\n    }\r\n\r\n    const refreshCaptchaBtn = document.getElementById('refresh-captcha');\r\n    if (refreshCaptchaBtn && captchaQuestionEl && captchaToken) {\r\n        refreshCaptchaBtn.addEventListener('click', async function() {\r\n            try {\r\n                // Optionally show a spinning state\r\n                const originalIcon = refreshCaptchaBtn.innerHTML;\r\n                refreshCaptchaBtn.innerHTML = '<span class=\"spinner w-4 h-4 mr-1\"></span> Regenerating...';\r\n                refreshCaptchaBtn.disabled = true;\r\n\r\n                const result = await getCaptcha();\r\n                if (result.question && result.token) {\r\n                    captchaQuestionEl.textContent = result.question;\r\n                    captchaToken.value = result.token;\r\n                    captchaAnswer.value = '';\r\n                    captchaAnswer.focus();\r\n                } else {\r\n                    showCaptchaError('Failed to get new CAPTCHA.');\r\n                }\r\n                \r\n                refreshCaptchaBtn.innerHTML = originalIcon;\r\n                refreshCaptchaBtn.disabled = false;\r\n            } catch (err) {\r\n                showCaptchaError('Error refreshing CAPTCHA.');\r\n                refreshCaptchaBtn.disabled = false;\r\n            }\r\n        });\r\n    }\r\n\r\n    function showCaptchaError(msg) {\r\n        if (captchaError) {\r\n            captchaError.textContent = msg;\r\n            captchaError.classList.remove('hidden');\r\n            setTimeout(() => captchaError.classList.add('hidden'), 5000);\r\n        }\r\n    }\r\n\r\n    // --- Admin Login Form Handling ---\r\n    const loginForm = document.getElementById('login-form');\r\n    const usernameInput = document.getElementById('username');\r\n    const passwordInput = document.getElementById('password');\r\n    const loginError = document.getElementById('login-error');\r\n\r\n    if (loginForm) {\r\n        loginForm.addEventListener('submit', async function(e) {\r\n            e.preventDefault();\r\n\r\n            const username = usernameInput.value.trim();\r\n            const password = passwordInput.value;\r\n\r\n            if (!username || !password) {\r\n                showLoginError('Please enter both username and password.');\r\n                return;\r\n            }\r\n\r\n            // Disable button while processing\r\n            const submitBtn = loginForm.querySelector('button[type=\"submit\"]');\r\n            const originalText = submitBtn.innerHTML;\r\n            submitBtn.disabled = true;\r\n            submitBtn.innerHTML = '<span class=\"spinner\"></span> Signing in...';\r\n\r\n            try {\r\n                const result = await adminLogin(username, password);\r\n\r\n                if (result.success) {\r\n                    // Redirect to admin dashboard\r\n                    window.location.replace(result.redirect || '/admin');\r\n                } else {\r\n                    showLoginError(result.error || 'Invalid credentials.');\r\n                    passwordInput.value = '';\r\n                    passwordInput.focus();\r\n                }\r\n            } catch (err) {\r\n                showLoginError('An error occurred during login. Please try again.');\r\n            } finally {\r\n                submitBtn.disabled = false;\r\n                submitBtn.innerHTML = originalText;\r\n            }\r\n        });\r\n    }\r\n\r\n    const forgotBtn = document.getElementById('btn-forgot-password');\r\n    if (forgotBtn) {\r\n        forgotBtn.addEventListener('click', async () => {\r\n            try {\r\n                const data = await forgotPassword();\r\n                // We use showConfirmModal just as a styled alert here\r\n                showConfirmModal(data.instructions || 'Please contact your administrator.', null);\r\n            } catch (err) {\r\n                showLoginError('Could not fetch recovery instructions.');\r\n            }\r\n        });\r\n    }\r\n\r\n    function showLoginError(msg) {\r\n        if (loginError) {\r\n            loginError.textContent = msg;\r\n            loginError.classList.remove('hidden');\r\n            setTimeout(() => loginError.classList.add('hidden'), 5000);\r\n        }\r\n    }\r\n\r\n    // --- Logout Button (present on admin & viewer pages) ---\r\n    // Logout logic should be handled by specific pages to avoid conflicts\r\n});\r\n\r\n// --- Session Check Helper ---\r\nasync function checkSessionAndRedirect() {\r\n    // Check sessionStorage cache first (5-minute TTL)\r\n    const CACHE_KEY = 'lims_session_cache';\r\n    const CACHE_TTL = 5 * 60 * 1000; // 5 minutes\r\n    \r\n    try {\r\n        const cached = sessionStorage.getItem(CACHE_KEY);\r\n        if (cached) {\r\n            const { timestamp, sessionInfo } = JSON.parse(cached);\r\n            if (Date.now() - timestamp < CACHE_TTL) {\r\n                // Use cached session\r\n                if (sessionInfo.is_authenticated) {\r\n                    if (sessionInfo.role === 'admin') {\r\n                        window.location.replace('/admin');\r\n                    } else if (sessionInfo.role === 'viewer') {\r\n                        window.location.replace('/viewer');\r\n                    }\r\n                }\r\n                return;\r\n            }\r\n        }\r\n    } catch (err) {\r\n        // Cache invalid, continue to API call\r\n    }\r\n    \r\n    try {\r\n        const sessionInfo = await getSessionInfo();\r\n        \r\n        // Update cache\r\n        try {\r\n            sessionStorage.setItem(CACHE_KEY, JSON.stringify({\r\n                timestamp: Date.now(),\r\n                sessionInfo\r\n            }));\r\n        } catch (err) {\r\n            // sessionStorage not available, ignore\r\n        }\r\n        \r\n        if (sessionInfo.is_authenticated) {\r\n            // User is already logged in, redirect to their dashboard\r\n            if (sessionInfo.role === 'admin') {\r\n                window.location.replace('/admin');\r\n            } else if (sessionInfo.role === 'viewer') {\r\n                window.location.replace('/viewer');\r\n            }\r\n        }\r\n    } catch (err) {\r\n        // Session check failed, stay on login page\r\n        console.log('No active session found');\r\n    }\r\n}\r\n"
  },
  {
    "path": "static/js/map.js",
    "content": "/**\n * map.js - Entry point for India LIMS client-side logic.\n * Loads modules and initializes the application.\n */\n\n// --- Global State is now in modules/state.js ---\n\n// --- Global Event Listeners & Initialization ---\ndocument.addEventListener('DOMContentLoaded', async function() {\n    try {\n        // 1. Identify mode\n        const mapEl = document.getElementById('map');\n        const adminMode = mapEl ? mapEl.dataset.mode === 'admin' : false;\n        \n        // 2. Initialize Core Map\n        if (mapEl) {\n            initMap(adminMode);\n        }\n\n        // 3. Admin-only initializations\n        if (adminMode) {\n            isAdmin = true;\n            \n            // Load essential data\n            try {\n                const catalog = await fetchLocationCatalog();\n                locationCatalog = catalog;\n                console.log('Location catalog loaded:', Object.keys(catalog).length, 'states');\n            } catch (err) {\n                console.warn('Could not load location catalog:', err);\n            }\n\n            loadProfile();\n            initializeLocationFilters();\n            initializeRecordFilters();\n            \n            // Tab switching logic\n            setupTabSwitching();\n            switchMainTab('dashboard');\n            \n            // Search handler\n            const searchBtn = document.getElementById('btn-search');\n            if (searchBtn) {\n                searchBtn.addEventListener('click', performAdminSearch);\n            }\n            \n            // Form submission\n            const recordForm = document.getElementById('record-form');\n            if (recordForm) {\n                recordForm.addEventListener('submit', function(e) {\n                    e.preventDefault();\n                    handleFormSubmit();\n                });\n            }\n\n            // Reset form button\n            const resetBtn = document.getElementById('btn-reset-form');\n            if (resetBtn) resetBtn.addEventListener('click', resetForm);\n\n            // Logout is handled by admin.js with confirmation\n\n            // Refresh feedback\n            const refreshFeedbackBtn = document.getElementById('btn-refresh-feedback');\n            if (refreshFeedbackBtn) {\n                refreshFeedbackBtn.addEventListener('click', loadFeedback);\n            }\n        }\n        \n    } catch (e) {\n        console.error('Initialization error:', e);\n    }\n});\n\n// --- Tab Switching Logic ---\nfunction setupTabSwitching() {\n    // Main Tabs (Dashboard, Map, Records, Add Record, etc.)\n    document.querySelectorAll('.main-tab-btn').forEach(btn => {\n        btn.addEventListener('click', function() {\n            const tabId = this.dataset.tab;\n            if (tabId) switchMainTab(tabId);\n        });\n    });\n\n    // Form Tabs (Location, Parcel, Owner, Mutation)\n    document.querySelectorAll('.form-tab-btn').forEach(btn => {\n        btn.addEventListener('click', function() {\n            const tabId = this.dataset.formTab || this.dataset.tab;\n            if (tabId) switchFormTab(tabId);\n        });\n    });\n}\n\nfunction switchMainTab(tabId) {\n    // Hide all panels\n    document.querySelectorAll('.main-tab-panel').forEach(panel => {\n        panel.classList.add('hidden');\n    });\n    \n    // Show target panel\n    const target = document.getElementById('main-tab-' + tabId);\n    if (target) {\n        target.classList.remove('hidden');\n    }\n    \n    // Update button states\n    document.querySelectorAll('.main-tab-btn').forEach(btn => {\n        const active = btn.dataset.tab === tabId;\n        btn.classList.toggle('active', active);\n        // Tailwind active classes\n        if (active) {\n            btn.classList.add('bg-orange-50', 'text-orange-700', 'border-orange-500');\n            btn.classList.remove('text-gray-500', 'border-transparent');\n        } else {\n            btn.classList.remove('bg-orange-50', 'text-orange-700', 'border-orange-500');\n            btn.classList.add('text-gray-500', 'border-transparent');\n        }\n    });\n\n    // Special handling for maps when switching tabs\n    if (tabId === 'map' && map) {\n        setTimeout(() => map.invalidateSize(), 100);\n    } else if (tabId === 'add-record') {\n        if (!addRecordMap) initAddRecordMap();\n        else setTimeout(() => addRecordMap.invalidateSize(), 100);\n    } else if (tabId === 'users') {\n        loadUsers();\n    } else if (tabId === 'audit') {\n        fetch(`${API_BASE}/api/audit`, { credentials: 'include' })\n            .then(r => r.json())\n            .then(data => showAuditModal(data));\n    } else if (tabId === 'feedback') {\n        loadFeedback();\n    }\n}\n\nfunction switchFormTab(tabId) {\n    document.querySelectorAll('.form-tab-panel').forEach(content => {\n        content.classList.add('hidden');\n    });\n    \n    const target = document.getElementById('form-tab-' + tabId);\n    if (target) {\n        target.classList.remove('hidden');\n    }\n    \n    document.querySelectorAll('.form-tab-btn').forEach(btn => {\n        const active = (btn.dataset.formTab || btn.dataset.tab) === tabId;\n        btn.classList.toggle('active', active);\n        if (active) {\n            btn.classList.add('border-orange-500', 'text-orange-600');\n            btn.classList.remove('border-transparent', 'text-gray-500');\n        } else {\n            btn.classList.remove('border-orange-500', 'text-orange-600');\n            btn.classList.add('border-transparent', 'text-gray-500');\n        }\n    });\n\n    if (tabId === 'parcel' && addRecordMap) {\n        setTimeout(() => addRecordMap.invalidateSize(), 100);\n    }\n}\n\nasync function performAdminSearch() {\n    applyAdminFilters(true);\n}\n\nasync function viewRecordDetails(recordId) {\n    selectedRecordId = recordId;\n    selectedRecord = null; // Will be populated\n\n    try {\n        const record = await fetchRecord(recordId);\n        selectedRecord = record;\n        \n        // Switch to view record tab\n        switchMainTab('view-record');\n        \n        // Populate details\n        const loc = record.location || {};\n        const attrs = record.attributes || {};\n        const owner = record.owner || {};\n        const mutations = record.mutation_history || [];\n        \n        document.getElementById('view-khasra').textContent = record.khasra_no || 'N/A';\n        document.getElementById('view-ulpin').textContent = 'ULPIN: ' + (record.ulpin || 'N/A');\n        document.getElementById('view-land-use-badge').textContent = attrs.land_use || 'Unknown';\n        document.getElementById('view-land-use-badge').className = 'land-use-badge badge-' + (attrs.land_use || '').toLowerCase();\n        document.getElementById('view-state').textContent = loc.state || 'N/A';\n        document.getElementById('view-district').textContent = loc.district || 'N/A';\n        document.getElementById('view-village').textContent = loc.village || 'N/A';\n        document.getElementById('view-khata').textContent = record.khata_no || 'N/A';\n        document.getElementById('view-area').textContent = attrs.area_ha ? attrs.area_ha + ' Ha' : 'N/A';\n        document.getElementById('view-rate').textContent = attrs.circle_rate_inr ? 'Rs. ' + Number(attrs.circle_rate_inr).toLocaleString() + '/ha' : 'N/A';\n        \n        const value = calculateValuation(attrs.area_ha, attrs.circle_rate_inr, attrs.land_use || '');\n        if (value > 0) {\n            document.getElementById('view-value').textContent = 'Rs. ' + formatInr(value);\n        } else {\n            document.getElementById('view-value').textContent = 'Rs. 0';\n        }\n        \n        document.getElementById('view-owner').textContent = owner.name || 'N/A';\n        document.getElementById('view-share').textContent = owner.share_pct ? owner.share_pct + '%' : 'N/A';\n        document.getElementById('view-aadhaar').textContent = owner.aadhaar_mask || 'N/A';\n        \n        // Owner document logic\n        const ownerDocContainer = document.getElementById('view-owner-doc-container');\n        const ownerDocLink = document.getElementById('view-owner-doc-link');\n        if (owner.proof_doc_b64) {\n            ownerDocContainer.classList.remove('hidden');\n            ownerDocLink.href = owner.proof_doc_b64;\n        } else {\n            ownerDocContainer.classList.add('hidden');\n        }\n\n        // Mutation history\n        const mutationsEl = document.getElementById('view-mutations');\n        if (mutations.length > 0) {\n            mutationsEl.innerHTML = mutations.map(m => {\n                const docLinkHTML = m.proof_doc_b64 ? `<a href=\"${m.proof_doc_b64}\" download=\"Mutation_Proof\" class=\"text-blue-600 hover:text-blue-800 font-medium underline flex items-center gap-1 mt-2 text-xs\"><svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg> Download Proof</a>` : '';\n                return `\n                <div class=\"bg-amber-50 border-l-4 border-amber-400 rounded-r-lg p-3 shadow-sm mb-2\">\n                    <div class=\"flex items-center justify-between mb-1\">\n                        <span class=\"text-sm font-bold text-gray-800\">${m.previous_owner}</span>\n                        <span class=\"text-[10px] font-bold uppercase bg-amber-100 text-amber-700 px-1.5 py-0.5 rounded\">${m.mutation_type}</span>\n                    </div>\n                    <div class=\"text-[11px] text-gray-600 space-y-0.5\">\n                        <div class=\"flex justify-between\"><span>Share: ${m.previous_share_pct}%</span><span>Aadhaar: ${m.previous_aadhaar_mask || 'N/A'}</span></div>\n                        <div class=\"flex justify-between\"><span>Date: ${m.mutation_date}</span><span class=\"font-mono\">Ref: ${m.mutation_ref || 'N/A'}</span></div>\n                    </div>\n                    ${docLinkHTML}\n                </div>\n            `;\n            }).join('');\n        } else {\n            mutationsEl.innerHTML = '<p class=\"text-sm text-gray-500 italic\">No historical mutations found for this parcel.</p>';\n        }\n        \n        // Initialize small map after tab switch\n        setTimeout(() => {\n            if (typeof initViewRecordMap === 'function') {\n                initViewRecordMap(record);\n            }\n        }, 200);\n\n        // Wire buttons (if not already wired)\n        const backBtn = document.getElementById('btn-back-to-records');\n        if (backBtn) backBtn.onclick = () => switchMainTab('records');\n\n        const editBtn = document.getElementById('btn-view-edit');\n        if (editBtn) editBtn.onclick = () => { if (typeof editRecord === 'function') editRecord(record._id); };\n\n        const printBtn = document.getElementById('btn-view-print');\n        if (printBtn) printBtn.onclick = () => { if (typeof printCard === 'function') printCard(record.ulpin); };\n\n        const delBtn = document.getElementById('btn-view-delete');\n        if (delBtn) delBtn.onclick = () => { if (typeof confirmDelete === 'function') confirmDelete(record._id, record.khasra_no); };\n        \n    } catch (_err) {\n        if (typeof showToast === 'function') {\n            showToast('Failed to load record details.', 'error');\n        }\n    }\n}\n"
  },
  {
    "path": "static/js/map_OLD_BAK.js",
    "content": "/**\r\n * map.js - Leaflet map and parcel workflow logic for India LIMS.\r\n * Handles map initialization, polygon drawing, record rendering,\r\n * location filtering, and admin form interactions.\r\n */\r\n\r\nlet map = null;\r\nlet viewRecordMap = null; // Small map in view record tab\r\nlet addRecordMap = null; // Map in add record tab\r\nlet addRecordDrawnItems = null; // Drawing layer for add record\r\nlet baseLayers = {};\r\nlet currentBaseLayer = null;\r\nlet parcelLayers = [];\r\nlet drawnItems = null;\r\nlet currentSketchLayer = null;\r\nlet isAdmin = false;\r\nlet selectedRecordId = null;\r\nlet selectedRecord = null;\r\nlet reverseGeocodeTimer = null;\r\nlet lastGeocodeKey = '';\r\nlet lastAutoLocation = { state: '', district: '', village: '' };\r\nlet lastGpsDetectedLocation = { state: '', district: '', village: '' };\r\nlet allRecordsCache = [];\r\nlet filteredRecordsCache = [];\r\nlet recordsViewMode = 'cards';\r\n\r\nconst DEFAULT_SNAP_DISTANCE = 20;\r\nlet locationCatalog = {};\r\n\r\nfunction showConfirmModal(message, onConfirm) {\r\n    const overlay = document.createElement('div');\r\n    overlay.className = 'fixed inset-0 bg-black bg-opacity-50 z-[9999] flex items-center justify-center p-4';\r\n    overlay.style.backdropFilter = 'blur(2px)';\r\n    \r\n    const modal = document.createElement('div');\r\n    modal.className = 'bg-white rounded-lg shadow-xl max-w-sm w-full overflow-hidden fade-in';\r\n    \r\n    const content = document.createElement('div');\r\n    content.className = 'p-6';\r\n    \r\n    const iconContainer = document.createElement('div');\r\n    iconContainer.className = 'mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4';\r\n    iconContainer.innerHTML = '<svg class=\"h-6 w-6 text-red-600\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>';\r\n    \r\n    const textContainer = document.createElement('div');\r\n    textContainer.className = 'text-center';\r\n    \r\n    const title = document.createElement('h3');\r\n    title.className = 'text-lg leading-6 font-medium text-gray-900 mb-2';\r\n    title.textContent = 'Confirm Action';\r\n    \r\n    const messageEl = document.createElement('p');\r\n    messageEl.className = 'text-sm text-gray-500 whitespace-pre-line';\r\n    messageEl.textContent = message;\r\n    \r\n    textContainer.appendChild(title);\r\n    textContainer.appendChild(messageEl);\r\n    content.appendChild(iconContainer);\r\n    content.appendChild(textContainer);\r\n    \r\n    const buttonsContainer = document.createElement('div');\r\n    buttonsContainer.className = 'bg-gray-50 px-4 py-3 sm:px-6 flex flex-row-reverse gap-2';\r\n    \r\n    const confirmBtn = document.createElement('button');\r\n    confirmBtn.className = 'w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none sm:w-auto sm:text-sm transition-colors cursor-pointer';\r\n    confirmBtn.textContent = 'Confirm';\r\n    \r\n    const cancelBtn = document.createElement('button');\r\n    cancelBtn.className = 'w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:w-auto sm:text-sm transition-colors cursor-pointer';\r\n    cancelBtn.textContent = 'Cancel';\r\n    \r\n    buttonsContainer.appendChild(confirmBtn);\r\n    buttonsContainer.appendChild(cancelBtn);\r\n    modal.appendChild(content);\r\n    modal.appendChild(buttonsContainer);\r\n    overlay.appendChild(modal);\r\n    document.body.appendChild(overlay);\r\n    \r\n    const close = () => document.body.removeChild(overlay);\r\n    confirmBtn.addEventListener('click', () => { close(); if (onConfirm) onConfirm(); });\r\n    cancelBtn.addEventListener('click', close);\r\n    overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });\r\n}\r\n\r\nfunction sortedValues(values) {\r\n    return [...new Set(values)].sort((a, b) => a.localeCompare(b));\r\n}\r\n\r\nfunction asNumber(value) {\r\n    const parsed = parseFloat(value);\r\n    return Number.isFinite(parsed) ? parsed : 0;\r\n}\r\n\r\nfunction formatInr(value) {\r\n    return Math.round(value).toLocaleString('en-IN');\r\n}\r\n\r\nfunction escapeHtml(value) {\r\n    return String(value || '')\r\n        .replace(/&/g, '&amp;')\r\n        .replace(/</g, '&lt;')\r\n        .replace(/>/g, '&gt;')\r\n        .replace(/\"/g, '&quot;')\r\n        .replace(/'/g, '&#039;');\r\n}\r\n\r\n// Utility: simple debounce implementation\r\nfunction debounce(fn, wait = 200) {\r\n    let t = null;\r\n    return function(...args) {\r\n        const ctx = this;\r\n        clearTimeout(t);\r\n        t = setTimeout(() => fn.apply(ctx, args), wait);\r\n    };\r\n}\r\n\r\nfunction ensureLocationInCatalog(state, district, village) {\r\n    if (!state || !district || !village) return;\r\n\r\n    if (!locationCatalog[state]) {\r\n        locationCatalog[state] = {};\r\n    }\r\n    if (!locationCatalog[state][district]) {\r\n        locationCatalog[state][district] = [];\r\n    }\r\n    if (!locationCatalog[state][district].includes(village)) {\r\n        locationCatalog[state][district].push(village);\r\n        locationCatalog[state][district] = sortedValues(locationCatalog[state][district]);\r\n    }\r\n}\r\n\r\nfunction syncLocationCatalogWithRecords(records) {\r\n    records.forEach(rec => {\r\n        const loc = rec.location || {};\r\n        ensureLocationInCatalog(loc.state, loc.district, loc.village);\r\n    });\r\n}\r\n\r\nfunction populateStateFilter(records) {\r\n    const stateFilterEl = document.getElementById('state-filter');\r\n    if (!stateFilterEl) return;\r\n\r\n    const selected = stateFilterEl.value || '';\r\n    const states = sortedValues(records\r\n        .map(rec => (rec.location && rec.location.state) || '')\r\n        .filter(Boolean));\r\n\r\n    stateFilterEl.innerHTML = '<option value=\"\">All States</option>';\r\n    states.forEach(state => {\r\n        const option = document.createElement('option');\r\n        option.value = state;\r\n        option.textContent = state;\r\n        stateFilterEl.appendChild(option);\r\n    });\r\n\r\n    if (selected && states.includes(selected)) {\r\n        stateFilterEl.value = selected;\r\n    }\r\n}\r\n\r\nfunction populateDistrictFilter(records) {\r\n    const districtFilterEl = document.getElementById('district-filter');\r\n    if (!districtFilterEl) return;\r\n\r\n    const selected = districtFilterEl.value || '';\r\n    const districts = sortedValues(records\r\n        .map(rec => (rec.location && rec.location.district) || '')\r\n        .filter(Boolean));\r\n\r\n    districtFilterEl.innerHTML = '<option value=\"\">All Districts</option>';\r\n    districts.forEach(district => {\r\n        const option = document.createElement('option');\r\n        option.value = district;\r\n        option.textContent = district;\r\n        districtFilterEl.appendChild(option);\r\n    });\r\n\r\n    if (selected && districts.includes(selected)) {\r\n        districtFilterEl.value = selected;\r\n    }\r\n}\r\n\r\nfunction getAdminFilterState() {\r\n    const query = (document.getElementById('search-input') || {}).value || '';\r\n    const landUse = (document.getElementById('land-use-filter') || {}).value || '';\r\n    const district = (document.getElementById('district-filter') || {}).value || '';\r\n    const state = (document.getElementById('state-filter') || {}).value || '';\r\n\r\n    return {\r\n        query: query.trim().toLowerCase(),\r\n        landUse,\r\n        district,\r\n        state\r\n    };\r\n}\r\n\r\nfunction filterRecordsByState(records, filterState) {\r\n    return records.filter(rec => {\r\n        const attrs = rec.attributes || {};\r\n        const loc = rec.location || {};\r\n        const owner = rec.owner || {};\r\n        const searchText = [\r\n            rec.khasra_no,\r\n            rec.ulpin,\r\n            rec.khata_no,\r\n            loc.village,\r\n            loc.district,\r\n            loc.state,\r\n            owner.name,\r\n            attrs.land_use\r\n        ].join(' ').toLowerCase();\r\n\r\n        const queryMatch = !filterState.query || searchText.includes(filterState.query);\r\n        const landUseMatch = !filterState.landUse || attrs.land_use === filterState.landUse;\r\n        const stateMatch = !filterState.state || loc.state === filterState.state;\r\n        const districtMatch = !filterState.district || loc.district === filterState.district;\r\n\r\n        return queryMatch && landUseMatch && stateMatch && districtMatch;\r\n    });\r\n}\r\n\r\nfunction renderKpiCards(records) {\r\n    const totalParcelsEl = document.getElementById('kpi-total-parcels');\r\n    const totalAreaEl = document.getElementById('kpi-total-area');\r\n    const estimatedValueEl = document.getElementById('kpi-estimated-value');\r\n    const mutationEl = document.getElementById('kpi-mutations');\r\n\r\n    if (!totalParcelsEl || !totalAreaEl || !estimatedValueEl || !mutationEl) return;\r\n\r\n    let totalArea = 0;\r\n    let totalValue = 0;\r\n    let totalMutations = 0;\r\n\r\n    records.forEach(rec => {\r\n        const attrs = rec.attributes || {};\r\n        const area = asNumber(attrs.area_ha);\r\n        const rate = asNumber(attrs.circle_rate_inr);\r\n\r\n        totalArea += area;\r\n        totalValue += area * rate;\r\n        totalMutations += (rec.mutation_history || []).length;\r\n    });\r\n\r\n    totalParcelsEl.textContent = String(records.length);\r\n    totalAreaEl.textContent = totalArea.toFixed(2);\r\n    estimatedValueEl.textContent = formatInr(totalValue);\r\n    mutationEl.textContent = String(totalMutations);\r\n}\r\n\r\nfunction buildDonutChart(slices, colors) {\r\n    // slices: [{label, value, extra}]\r\n    const total = slices.reduce((s, x) => s + x.value, 0);\r\n    if (total === 0) return '<p style=\"color:#9ca3af;font-size:12px;\">No data</p>';\r\n    const R = 40, CX = 50, CY = 50, stroke = 18;\r\n    let cumAngle = -90;\r\n    const arcs = slices.map((s, i) => {\r\n        const pct = s.value / total;\r\n        const angle = pct * 360;\r\n        const r1 = (cumAngle * Math.PI) / 180;\r\n        const r2 = ((cumAngle + angle) * Math.PI) / 180;\r\n        const x1 = CX + R * Math.cos(r1), y1 = CY + R * Math.sin(r1);\r\n        const x2 = CX + R * Math.cos(r2), y2 = CY + R * Math.sin(r2);\r\n        const large = angle > 180 ? 1 : 0;\r\n        const d = `M ${x1} ${y1} A ${R} ${R} 0 ${large} 1 ${x2} ${y2}`;\r\n        cumAngle += angle;\r\n        return `<path d=\"${d}\" fill=\"none\" stroke=\"${colors[i % colors.length]}\" stroke-width=\"${stroke}\" stroke-linecap=\"butt\"/>`;\r\n    }).join('');\r\n    return `\r\n        <div style=\"display:flex;align-items:center;gap:16px;\">\r\n            <svg viewBox=\"0 0 100 100\" style=\"width:90px;height:90px;flex-shrink:0;\">\r\n                <circle cx=\"${CX}\" cy=\"${CY}\" r=\"${R}\" fill=\"none\" stroke=\"currentColor\" stroke-opacity=\"0.1\" stroke-width=\"${stroke}\"/>\r\n                ${arcs}\r\n                <text x=\"${CX}\" y=\"${CY}\" text-anchor=\"middle\" dominant-baseline=\"central\" style=\"font-size:11px;font-weight:700;fill:currentColor;\">${total}</text>\r\n                <text x=\"${CX}\" y=\"${CY+12}\" text-anchor=\"middle\" dominant-baseline=\"central\" style=\"font-size:7px;fill:currentColor;opacity:0.6;\">parcels</text>\r\n            </svg>\r\n            <div style=\"flex:1;min-width:0;\">\r\n                ${slices.map((s, i) => `\r\n                    <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:6px;\">\r\n                        <div style=\"width:10px;height:10px;border-radius:2px;background:${colors[i % colors.length]};flex-shrink:0;\"></div>\r\n                        <span style=\"font-size:11px;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100px;\" title=\"${escapeHtml(s.label)}\">${escapeHtml(s.label)}</span>\r\n                        <span style=\"margin-left:auto;font-size:11px;font-weight:700;color:inherit;\">${s.value}</span>\r\n                        <span style=\"font-size:10px;color:inherit;opacity:0.6;\">(${(s.value/total*100).toFixed(0)}%)</span>\r\n                    </div>`).join('')}\r\n            </div>\r\n        </div>`;\r\n}\r\n\r\nfunction buildRankedList(items, colors) {\r\n    // items: [{label, value, sublabel}]\r\n    const max = Math.max(...items.map(x => x.value), 1);\r\n    return items.map((item, i) => {\r\n        const pct = Math.max((item.value / max) * 100, 4);\r\n        const color = colors[i % colors.length];\r\n        return `\r\n            <div style=\"margin-bottom:10px;\">\r\n                <div style=\"display:flex;justify-content:space-between;align-items:baseline;margin-bottom:3px;\">\r\n                    <span style=\"font-size:11px;font-weight:600;color:inherit;\">${escapeHtml(item.label)}</span>\r\n                    <span style=\"font-size:11px;font-weight:700;color:${color};white-space:nowrap;margin-left:8px;\">${item.sublabel}</span>\r\n                </div>\r\n                <div style=\"background:currentColor;background-opacity:0.1;background-color:rgba(0,0,0,0.05);border-radius:99px;height:6px;\">\r\n                    <div style=\"background:${color};width:${pct}%;height:100%;border-radius:99px;transition:width 0.5s ease;\"></div>\r\n                </div>\r\n            </div>`;\r\n    }).join('');\r\n}\r\n\r\nfunction renderLandUseDistribution(records) {\r\n    const target = document.getElementById('dashboard-land-use');\r\n    if (!target) return;\r\n    if (!records.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No records yet.</p>'; return; }\r\n    const metrics = {};\r\n    records.forEach(rec => {\r\n        const lu = (rec.attributes && rec.attributes.land_use) || 'Unknown';\r\n        if (!metrics[lu]) metrics[lu] = { count: 0, area: 0 };\r\n        metrics[lu].count++;\r\n        metrics[lu].area += asNumber(rec.attributes && rec.attributes.area_ha);\r\n    });\r\n    const COLORS = ['#f97316','#3b82f6','#22c55e','#a855f7','#ec4899','#14b8a6','#f59e0b','#ef4444'];\r\n    const slices = Object.entries(metrics).sort((a,b)=>b[1].count-a[1].count)\r\n        .map(([label, v]) => ({ label, value: v.count, extra: v.area.toFixed(1)+' Ha' }));\r\n    target.innerHTML = buildDonutChart(slices, COLORS);\r\n}\r\n\r\nfunction renderDistrictOverview(records) {\r\n    const target = document.getElementById('dashboard-districts');\r\n    if (!target) return;\r\n    if (!records.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No district data.</p>'; return; }\r\n    const districtMap = {};\r\n    records.forEach(rec => {\r\n        const d = (rec.location && rec.location.district) || 'Unknown';\r\n        if (!districtMap[d]) districtMap[d] = { count: 0, area: 0, value: 0 };\r\n        const area = asNumber(rec.attributes && rec.attributes.area_ha);\r\n        districtMap[d].count++;\r\n        districtMap[d].area += area;\r\n        districtMap[d].value += area * asNumber(rec.attributes && rec.attributes.circle_rate_inr);\r\n    });\r\n    const COLORS = ['#6366f1','#f97316','#10b981','#f59e0b','#f43f5e','#a855f7'];\r\n    const items = Object.entries(districtMap).sort((a,b)=>b[1].count-a[1].count).slice(0,6)\r\n        .map(([label, v]) => ({\r\n            label,\r\n            value: v.count,\r\n            sublabel: `${v.count} parcels · ${v.area.toFixed(1)} Ha`\r\n        }));\r\n    target.innerHTML = buildRankedList(items, COLORS);\r\n}\r\n\r\nfunction renderTopValueParcel(records) {\r\n    const target = document.getElementById('dashboard-top-parcel');\r\n    if (!target) return;\r\n\r\n    if (!records.length) {\r\n        target.textContent = 'No parcel data yet.';\r\n        return;\r\n    }\r\n\r\n    let topRecord = null;\r\n    let topValue = -1;\r\n\r\n    records.forEach(rec => {\r\n        const area = asNumber(rec.attributes && rec.attributes.area_ha);\r\n        const rate = asNumber(rec.attributes && rec.attributes.circle_rate_inr);\r\n        const value = area * rate;\r\n        if (value > topValue) {\r\n            topValue = value;\r\n            topRecord = rec;\r\n        }\r\n    });\r\n\r\n    if (!topRecord) {\r\n        target.textContent = 'No parcel data yet.';\r\n        return;\r\n    }\r\n\r\n    const loc = topRecord.location || {};\r\n    const attrs = topRecord.attributes || {};\r\n    target.innerHTML = `\r\n        <div class=\"text-sm font-semibold text-inherit\">${escapeHtml(topRecord.khasra_no || 'N/A')} (${escapeHtml(topRecord.ulpin || 'N/A')})</div>\r\n        <div class=\"mt-1 text-xs text-inherit opacity-70\">${escapeHtml(loc.village || 'N/A')}, ${escapeHtml(loc.district || 'N/A')} | ${escapeHtml(attrs.land_use || 'N/A')}</div>\r\n        <div class=\"mt-1 text-xs text-inherit opacity-60\">Area: ${asNumber(attrs.area_ha).toFixed(2)} Ha | Estimated: Rs. ${formatInr(topValue)}</div>\r\n    `;\r\n}\r\n\r\nfunction renderRecentMutations(records) {\r\n    const target = document.getElementById('dashboard-mutations');\r\n    if (!target) return;\r\n\r\n    const entries = [];\r\n    records.forEach(rec => {\r\n        (rec.mutation_history || []).forEach(item => {\r\n            entries.push({\r\n                khasraNo: rec.khasra_no || 'N/A',\r\n                district: (rec.location && rec.location.district) || 'N/A',\r\n                previousOwner: item.previous_owner || 'N/A',\r\n                mutationType: item.mutation_type || 'N/A',\r\n                mutationDate: item.mutation_date || 'N/A',\r\n                mutationRef: item.mutation_ref || 'N/A'\r\n            });\r\n        });\r\n    });\r\n\r\n    entries.sort((a, b) => String(b.mutationDate).localeCompare(String(a.mutationDate)));\r\n\r\n    if (!entries.length) {\r\n        target.innerHTML = '<div class=\"dashboard-row text-xs text-gray-500\">No mutation history available for selected filters.</div>';\r\n        return;\r\n    }\r\n\r\n    target.innerHTML = entries.slice(0, 6).map(item => `\r\n        <div class=\"dashboard-row text-xs border-b border-gray-100 p-2\">\r\n            <div class=\"font-semibold text-inherit\">${escapeHtml(item.khasraNo)} | ${escapeHtml(item.mutationType)}</div>\r\n            <div class=\"mt-1 text-inherit opacity-70\">${escapeHtml(item.previousOwner)} -> ${escapeHtml(item.mutationDate)}</div>\r\n            <div class=\"text-inherit opacity-50\">${escapeHtml(item.district)} | Ref: ${escapeHtml(item.mutationRef)}</div>\r\n        </div>\r\n    `).join('');\r\n}\r\n\r\nfunction renderDashboardAnalytics(filteredRecords) {\r\n    renderKpiCards(filteredRecords);\r\n    renderLandUseDistribution(filteredRecords);\r\n    renderDistrictOverview(filteredRecords);\r\n    renderTopValueParcel(filteredRecords);\r\n    renderRecentMutations(filteredRecords);\r\n}\r\n\r\nfunction applyAdminFilters(notify) {\r\n    if (!isAdmin) return;\r\n\r\n    const filterState = getAdminFilterState();\r\n    \r\n    // Use server-side filtering\r\n    fetchFilteredRecords({\r\n        state: filterState.query ? '' : filterState.state,\r\n        district: filterState.query ? '' : filterState.district,\r\n        village: filterState.query ? '' : filterState.village,\r\n        land_use: filterState.landUse,\r\n        search: filterState.query\r\n    }).then(filtered => {\r\n        filteredRecordsCache = filtered;\r\n        \r\n        // Update map\r\n        clearMapLayers();\r\n        addRecordsToMap(filtered);\r\n        \r\n        // Update records list\r\n        renderRecordsList(filtered);\r\n        \r\n        // Update dashboard from server\r\n        fetchDashboardAnalytics({\r\n            state: filterState.state,\r\n            land_use: filterState.landUse,\r\n            district: filterState.district,\r\n            search: filterState.query\r\n        }).then(analytics => {\r\n            renderKpiCardsFromServer(analytics.kpis);\r\n            renderLandUseDistributionFromServer(analytics.land_use_distribution);\r\n            renderDistrictOverviewFromServer(analytics.district_overview);\r\n            renderTopValueParcelFromServer(analytics.top_parcel);\r\n            renderRecentMutationsFromServer(analytics.recent_mutations);\r\n        }).catch(err => {\r\n            console.error('Dashboard analytics failed:', err);\r\n        });\r\n\r\n        if (notify) {\r\n            showToast(`Showing ${filtered.length} record(s).`, 'info');\r\n        }\r\n    }).catch(err => {\r\n        console.error('Server-side filtering failed:', err);\r\n        // Fallback to client-side\r\n        const filtered = filterRecordsByState(allRecordsCache, filterState);\r\n        filteredRecordsCache = filtered;\r\n        clearMapLayers();\r\n        addRecordsToMap(filtered);\r\n        renderRecordsList(filtered);\r\n        renderDashboardAnalytics(filtered);\r\n        if (notify) showToast(`Showing ${filtered.length} record(s).`, 'info');\r\n    });\r\n}\r\n\r\n// Server-side rendering helpers\r\nfunction renderKpiCardsFromServer(kpis) {\r\n    const totalParcelsEl = document.getElementById('kpi-total-parcels');\r\n    const totalAreaEl = document.getElementById('kpi-total-area');\r\n    const estimatedValueEl = document.getElementById('kpi-estimated-value');\r\n    const mutationEl = document.getElementById('kpi-mutations');\r\n    \r\n    if (totalParcelsEl) totalParcelsEl.textContent = String(kpis.total_parcels || 0);\r\n    if (totalAreaEl) totalAreaEl.textContent = (kpis.total_area || 0).toFixed(2);\r\n    if (estimatedValueEl) estimatedValueEl.textContent = formatInr(kpis.estimated_value || 0);\r\n    if (mutationEl) mutationEl.textContent = String(kpis.total_mutations || 0);\r\n}\r\n\r\nfunction renderLandUseDistributionFromServer(stats) {\r\n    const target = document.getElementById('dashboard-land-use');\r\n    if (!target) return;\r\n    const entries = Object.entries(stats || {});\r\n    if (!entries.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No records yet.</p>'; return; }\r\n    const COLORS = ['#f97316','#3b82f6','#22c55e','#a855f7','#ec4899','#14b8a6','#f59e0b','#ef4444'];\r\n    const slices = entries.sort((a,b)=>b[1].count-a[1].count)\r\n        .map(([label, s]) => ({ label, value: s.count, extra: s.area.toFixed(1)+' Ha' }));\r\n    target.innerHTML = buildDonutChart(slices, COLORS);\r\n}\r\n\r\nfunction renderDistrictOverviewFromServer(districts) {\r\n    const target = document.getElementById('dashboard-districts');\r\n    if (!target) return;\r\n    if (!districts || !districts.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No district data.</p>'; return; }\r\n    const COLORS = ['#6366f1','#f97316','#10b981','#f59e0b','#f43f5e','#a855f7'];\r\n    const items = districts.map(d => ({\r\n        label: d.name,\r\n        value: d.count,\r\n        sublabel: `${d.count} parcels · ${d.area.toFixed(1)} Ha`\r\n    }));\r\n    target.innerHTML = buildRankedList(items, COLORS);\r\n}\r\n\r\nfunction renderTopValueParcelFromServer(parcel) {\r\n    const target = document.getElementById('dashboard-top-parcel');\r\n    if (!target) return;\r\n    \r\n    if (!parcel) {\r\n        target.textContent = 'No parcel data yet.';\r\n        return;\r\n    }\r\n    \r\n    target.innerHTML = `\r\n        <div class=\"text-sm font-semibold text-inherit\">${escapeHtml(parcel.khasra_no)} (${escapeHtml(parcel.ulpin)})</div>\r\n        <div class=\"mt-1 text-xs text-inherit opacity-70\">${escapeHtml(parcel.village)}, ${escapeHtml(parcel.district)} | ${escapeHtml(parcel.land_use)}</div>\r\n        <div class=\"mt-1 text-xs text-inherit opacity-60\">Area: ${parcel.area_ha} Ha | Estimated: Rs. ${formatInr(parcel.estimated_value)}</div>\r\n    `;\r\n}\r\n\r\nfunction renderRecentMutationsFromServer(mutations) {\r\n    const target = document.getElementById('dashboard-mutations');\r\n    if (!target) return;\r\n    \r\n    if (!mutations || !mutations.length) {\r\n        target.innerHTML = '<div class=\"dashboard-row text-xs text-gray-500\">No mutation history available for selected filters.</div>';\r\n        return;\r\n    }\r\n    \r\n    target.innerHTML = mutations.slice(0, 6).map(m => `\r\n        <div class=\"dashboard-row text-xs border-b border-gray-100 p-2\">\r\n            <div class=\"font-semibold text-inherit\">${escapeHtml(m.khasra_no)} | ${escapeHtml(m.mutation_type)}</div>\r\n            <div class=\"mt-1 text-inherit opacity-70\">${escapeHtml(m.previous_owner)} -> ${escapeHtml(m.mutation_date)}</div>\r\n        </div>\r\n    `).join('');\r\n}\r\n\r\nfunction getFormElements() {\r\n    return {\r\n        stateEl: document.getElementById('form-state'),\r\n        districtEl: document.getElementById('form-district'),\r\n        villageEl: document.getElementById('form-village'),\r\n        manualOverrideEl: document.getElementById('location-manual-override'),\r\n        manualFieldsEl: document.getElementById('location-manual-fields'),\r\n        stateManualEl: document.getElementById('form-state-manual'),\r\n        districtManualEl: document.getElementById('form-district-manual'),\r\n        villageManualEl: document.getElementById('form-village-manual'),\r\n        sourceEl: document.getElementById('form-location-source')\r\n    };\r\n}\r\n\r\nfunction populateSelect(selectEl, values, placeholder, selectedValue) {\r\n    if (!selectEl) return;\r\n\r\n    const current = selectedValue || '';\r\n    selectEl.innerHTML = '';\r\n\r\n    const placeholderOption = document.createElement('option');\r\n    placeholderOption.value = '';\r\n    placeholderOption.textContent = placeholder;\r\n    selectEl.appendChild(placeholderOption);\r\n\r\n    values.forEach(value => {\r\n        const option = document.createElement('option');\r\n        option.value = value;\r\n        option.textContent = value;\r\n        selectEl.appendChild(option);\r\n    });\r\n\r\n    if (current && !values.includes(current)) {\r\n        const customOption = document.createElement('option');\r\n        customOption.value = current;\r\n        customOption.textContent = current;\r\n        selectEl.appendChild(customOption);\r\n    }\r\n\r\n    selectEl.value = current;\r\n}\r\n\r\nfunction refreshStateOptions(selectedState) {\r\n    const { stateEl } = getFormElements();\r\n    if (!stateEl) return;\r\n    const states = sortedValues(Object.keys(locationCatalog));\r\n    populateSelect(stateEl, states, 'Select State', selectedState || '');\r\n}\r\n\r\nfunction refreshDistrictOptions(selectedDistrict) {\r\n    const { stateEl, districtEl } = getFormElements();\r\n    if (!stateEl || !districtEl) return;\r\n\r\n    const state = stateEl.value;\r\n    const districts = state && locationCatalog[state] ? sortedValues(Object.keys(locationCatalog[state])) : [];\r\n    populateSelect(districtEl, districts, 'Select District', selectedDistrict || '');\r\n}\r\n\r\nfunction refreshVillageOptions(selectedVillage) {\r\n    const { stateEl, districtEl, villageEl } = getFormElements();\r\n    if (!stateEl || !districtEl || !villageEl) return;\r\n\r\n    const state = stateEl.value;\r\n    const district = districtEl.value;\r\n    const villages = state && district && locationCatalog[state] && locationCatalog[state][district]\r\n        ? sortedValues(locationCatalog[state][district])\r\n        : [];\r\n\r\n    populateSelect(villageEl, villages, 'Select Village / Ward', selectedVillage || '');\r\n}\r\n\r\nfunction setLocationValues(state, district, village) {\r\n    ensureLocationInCatalog(state, district, village);\r\n\r\n    refreshStateOptions(state || '');\r\n    refreshDistrictOptions(district || '');\r\n    refreshVillageOptions(village || '');\r\n\r\n    const { stateManualEl, districtManualEl, villageManualEl } = getFormElements();\r\n    if (stateManualEl && !stateManualEl.value.trim()) {\r\n        stateManualEl.value = state || '';\r\n    }\r\n    if (districtManualEl && !districtManualEl.value.trim()) {\r\n        districtManualEl.value = district || '';\r\n    }\r\n    if (villageManualEl && !villageManualEl.value.trim()) {\r\n        villageManualEl.value = village || '';\r\n    }\r\n}\r\n\r\nfunction setLocationSource(text) {\r\n    const { sourceEl } = getFormElements();\r\n    if (sourceEl) {\r\n        sourceEl.textContent = text;\r\n    }\r\n}\r\n\r\nfunction isManualLocationOverrideEnabled() {\r\n    const { manualOverrideEl } = getFormElements();\r\n    return !!(manualOverrideEl && manualOverrideEl.checked);\r\n}\r\n\r\nfunction toggleManualLocationOverride(enabled) {\r\n    const {\r\n        stateEl,\r\n        districtEl,\r\n        villageEl,\r\n        manualFieldsEl,\r\n        stateManualEl,\r\n        districtManualEl,\r\n        villageManualEl\r\n    } = getFormElements();\r\n\r\n    if (!stateEl || !districtEl || !villageEl || !manualFieldsEl || !stateManualEl || !districtManualEl || !villageManualEl) {\r\n        return;\r\n    }\r\n\r\n    manualFieldsEl.classList.toggle('hidden', !enabled);\r\n    stateEl.disabled = enabled;\r\n    districtEl.disabled = enabled;\r\n    villageEl.disabled = enabled;\r\n\r\n    stateEl.required = !enabled;\r\n    districtEl.required = !enabled;\r\n    villageEl.required = !enabled;\r\n\r\n    stateManualEl.required = enabled;\r\n    districtManualEl.required = enabled;\r\n    villageManualEl.required = enabled;\r\n\r\n    if (enabled) {\r\n        stateManualEl.value = stateEl.value || stateManualEl.value;\r\n        districtManualEl.value = districtEl.value || districtManualEl.value;\r\n        villageManualEl.value = villageEl.value || villageManualEl.value;\r\n        setLocationSource('Source: manual override enabled');\r\n    } else {\r\n        setLocationSource('Source: map auto-fill enabled');\r\n    }\r\n}\r\n\r\nfunction getEffectiveLocationValues() {\r\n    const {\r\n        stateEl,\r\n        districtEl,\r\n        villageEl,\r\n        stateManualEl,\r\n        districtManualEl,\r\n        villageManualEl\r\n    } = getFormElements();\r\n\r\n    if (isManualLocationOverrideEnabled()) {\r\n        return {\r\n            state: (stateManualEl ? stateManualEl.value : '').trim(),\r\n            district: (districtManualEl ? districtManualEl.value : '').trim(),\r\n            village: (villageManualEl ? villageManualEl.value : '').trim()\r\n        };\r\n    }\r\n\r\n    return {\r\n        state: stateEl ? stateEl.value : '',\r\n        district: districtEl ? districtEl.value : '',\r\n        village: villageEl ? villageEl.value : ''\r\n    };\r\n}\r\n\r\nfunction initializeLocationFilters() {\r\n    const {\r\n        stateEl,\r\n        districtEl,\r\n        villageEl,\r\n        manualOverrideEl,\r\n        stateManualEl,\r\n        districtManualEl,\r\n        villageManualEl\r\n    } = getFormElements();\r\n\r\n    if (!stateEl || !districtEl || !villageEl) return;\r\n\r\n    locationCatalog = {};\r\n\r\n    refreshStateOptions('');\r\n    refreshDistrictOptions('');\r\n    refreshVillageOptions('');\r\n\r\n    stateEl.addEventListener('change', function() {\r\n        lastAutoLocation = { state: '', district: '', village: '' };\r\n        refreshDistrictOptions('');\r\n        refreshVillageOptions('');\r\n    });\r\n\r\n    districtEl.addEventListener('change', function() {\r\n        lastAutoLocation = { state: '', district: '', village: '' };\r\n        refreshVillageOptions('');\r\n    });\r\n\r\n    if (manualOverrideEl) {\r\n        manualOverrideEl.addEventListener('change', function() {\r\n            toggleManualLocationOverride(this.checked);\r\n        });\r\n    }\r\n\r\n    if (stateManualEl && districtManualEl && villageManualEl) {\r\n        const onManualEntry = function() {\r\n            if (!isManualLocationOverrideEnabled()) return;\r\n            lastAutoLocation = {\r\n                state: stateManualEl.value.trim(),\r\n                district: districtManualEl.value.trim(),\r\n                village: villageManualEl.value.trim()\r\n            };\r\n        };\r\n\r\n        stateManualEl.addEventListener('input', onManualEntry);\r\n        districtManualEl.addEventListener('input', onManualEntry);\r\n        villageManualEl.addEventListener('input', onManualEntry);\r\n    }\r\n\r\n    toggleManualLocationOverride(false);\r\n}\r\n\r\nfunction updateAutofillStatus(message, isError) {\r\n    const statusEl = document.getElementById('map-autofill-status');\r\n    if (!statusEl) return;\r\n\r\n    statusEl.textContent = message || '';\r\n    statusEl.classList.toggle('text-red-600', !!isError);\r\n    statusEl.classList.toggle('text-gray-500', !isError);\r\n}\r\n\r\nfunction isMapAutofillEnabled() {\r\n    const enabledEl = document.getElementById('map-autofill-enabled');\r\n    return enabledEl ? enabledEl.checked : false;\r\n}\r\n\r\nfunction shouldApplyLocationUpdate(nextLocation, forceUpdate) {\r\n    const { stateEl, districtEl, villageEl } = getFormElements();\r\n    if (!stateEl || !districtEl || !villageEl) return false;\r\n\r\n    if (isManualLocationOverrideEnabled()) return false;\r\n\r\n    if (forceUpdate) return true;\r\n\r\n    const current = {\r\n        state: stateEl.value || '',\r\n        district: districtEl.value || '',\r\n        village: villageEl.value || ''\r\n    };\r\n\r\n    const currentIsEmpty = !current.state && !current.district && !current.village;\r\n    const currentIsPreviousAuto =\r\n        current.state === (lastAutoLocation.state || '') &&\r\n        current.district === (lastAutoLocation.district || '') &&\r\n        current.village === (lastAutoLocation.village || '');\r\n\r\n    const nextLooksUseful = nextLocation.state || nextLocation.district || nextLocation.village;\r\n    return !!nextLooksUseful && (currentIsEmpty || currentIsPreviousAuto);\r\n}\r\n\r\nfunction applyResolvedLocation(locationData, forceUpdate) {\r\n    const nextLocation = {\r\n        state: locationData.state || '',\r\n        district: locationData.district || '',\r\n        village: locationData.village || ''\r\n    };\r\n\r\n    if (!shouldApplyLocationUpdate(nextLocation, forceUpdate)) {\r\n        updateAutofillStatus('Map location detected. Location fields kept as manually selected.', false);\r\n        return;\r\n    }\r\n\r\n    setLocationValues(nextLocation.state, nextLocation.district, nextLocation.village);\r\n    lastAutoLocation = nextLocation;\r\n    setLocationSource(`Source: ${locationData.display_name || 'Map reverse geocoding'}`);\r\n    updateAutofillStatus(`Auto-filled: ${nextLocation.district || 'District N/A'}, ${nextLocation.state || 'State N/A'}`, false);\r\n}\r\n\r\nfunction scheduleMapLocationLookup(lat, lng, forceUpdate) {\r\n    if (!isAdmin || !isMapAutofillEnabled()) return;\r\n    if (typeof lat !== 'number' || typeof lng !== 'number') return;\r\n\r\n    const geocodeKey = `${lat.toFixed(4)},${lng.toFixed(4)}`;\r\n    if (!forceUpdate && geocodeKey === lastGeocodeKey) return;\r\n    lastGeocodeKey = geocodeKey;\r\n\r\n    if (reverseGeocodeTimer) {\r\n        clearTimeout(reverseGeocodeTimer);\r\n    }\r\n\r\n    reverseGeocodeTimer = setTimeout(async function() {\r\n        updateAutofillStatus('Detecting state and district from map...', false);\r\n        try {\r\n            const locationData = await fetchLocationFromCoordinates(lat, lng);\r\n            applyResolvedLocation(locationData, forceUpdate);\r\n        } catch (err) {\r\n            updateAutofillStatus(err.message || 'Map location detection failed.', true);\r\n        }\r\n    }, 650);\r\n}\r\n\r\nfunction initializeFormTabs() {\r\n    const tabButtons = document.querySelectorAll('.form-tab-btn');\r\n    if (!tabButtons.length) return;\r\n\r\n    tabButtons.forEach(btn => {\r\n        btn.addEventListener('click', function() {\r\n            switchFormTab(this.dataset.formTab);\r\n        });\r\n    });\r\n\r\n    switchFormTab('location');\r\n}\r\n\r\nfunction switchFormTab(tabName) {\r\n    const tabButtons = document.querySelectorAll('.form-tab-btn');\r\n    const panels = document.querySelectorAll('.form-tab-panel');\r\n\r\n    tabButtons.forEach(btn => {\r\n        const isActive = btn.dataset.formTab === tabName;\r\n        btn.classList.toggle('active', isActive);\r\n    });\r\n\r\n    panels.forEach(panel => {\r\n        panel.classList.toggle('hidden', panel.id !== `form-tab-${tabName}`);\r\n    });\r\n}\r\n\r\nfunction setMutationMode(enabled) {\r\n    const mutationSection = document.getElementById('mutation-section');\r\n    const mutationTabBtn = document.getElementById('form-mutation-tab-btn');\r\n\r\n    if (!mutationSection || !mutationTabBtn) return;\r\n\r\n    mutationSection.classList.toggle('hidden', !enabled);\r\n    mutationTabBtn.classList.toggle('hidden', !enabled);\r\n}\r\n\r\nfunction updateSnapDistanceLabel() {\r\n    const slider = document.getElementById('snap-distance');\r\n    const valueLabel = document.getElementById('snap-distance-value');\r\n    if (!slider || !valueLabel) return;\r\n\r\n    valueLabel.textContent = `${slider.value}px`;\r\n}\r\n\r\nfunction getDrawSettings() {\r\n    const snapEnabledEl = document.getElementById('snap-enabled');\r\n    const snapDistanceEl = document.getElementById('snap-distance');\r\n\r\n    return {\r\n        snappable: snapEnabledEl ? snapEnabledEl.checked : true,\r\n        snapDistance: snapDistanceEl ? parseInt(snapDistanceEl.value, 10) || DEFAULT_SNAP_DISTANCE : DEFAULT_SNAP_DISTANCE,\r\n        continueDrawing: false,\r\n        allowSelfIntersection: false\r\n    };\r\n}\r\n\r\nfunction clearMetricsUI() {\r\n    const areaInput = document.getElementById('form-area');\r\n    const areaAuto = document.getElementById('area-auto');\r\n    const areaEquivalents = document.getElementById('area-equivalents');\r\n    const geometryMetrics = document.getElementById('geometry-metrics');\r\n    const perimeterEl = document.getElementById('metric-perimeter');\r\n    const centroidEl = document.getElementById('metric-centroid');\r\n\r\n    if (areaInput) areaInput.value = '';\r\n    if (areaAuto) areaAuto.textContent = '';\r\n    if (areaEquivalents) {\r\n        areaEquivalents.classList.add('hidden');\r\n        areaEquivalents.innerHTML = '';\r\n    }\r\n    if (geometryMetrics) {\r\n        geometryMetrics.classList.add('hidden');\r\n    }\r\n    if (perimeterEl) perimeterEl.textContent = '--';\r\n    if (centroidEl) centroidEl.textContent = '--';\r\n}\r\n\r\nfunction clearGeometrySelection(alsoDisableDraw) {\r\n    const geometryInput = document.getElementById('form-geometry');\r\n    if (geometryInput) {\r\n        geometryInput.value = '';\r\n    }\r\n\r\n    clearMetricsUI();\r\n\r\n    if (drawnItems) {\r\n        drawnItems.clearLayers();\r\n    }\r\n    currentSketchLayer = null;\r\n\r\n    if (alsoDisableDraw && map) {\r\n        map.pm.disableDraw();\r\n    }\r\n}\r\n\r\nfunction startPolygonDraw() {\r\n    if (!isAdmin || !map) return;\r\n\r\n    const drawSettings = getDrawSettings();\r\n    map.pm.disableDraw();\r\n    map.pm.enableDraw('Polygon', drawSettings);\r\n\r\n    showToast('Drawing started. Click points on map and double-click to finish.', 'info', 5000);\r\n}\r\n\r\nfunction finishPolygonDraw() {\r\n    if (!isAdmin || !map) return;\r\n    map.pm.disableDraw();\r\n    showToast('Drawing mode closed.', 'info');\r\n}\r\n\r\nfunction cancelPolygonSelection() {\r\n    if (!isAdmin) return;\r\n    clearGeometrySelection(true);\r\n    showToast('Polygon selection canceled.', 'warning');\r\n}\r\n\r\nfunction clearPolygonSelection() {\r\n    if (!isAdmin) return;\r\n    clearGeometrySelection(false);\r\n    showToast('Selection cleared.', 'info');\r\n}\r\n\r\nfunction initializeDrawSettingsPanel() {\r\n    const panel = document.getElementById('draw-settings-panel');\r\n    if (!panel) return;\r\n\r\n    const startBtn = document.getElementById('btn-start-draw');\r\n    const finishBtn = document.getElementById('btn-finish-draw');\r\n    const cancelBtn = document.getElementById('btn-cancel-selection');\r\n    const clearBtn = document.getElementById('btn-clear-selection');\r\n    const snapDistance = document.getElementById('snap-distance');\r\n    const mapAutofillEnabled = document.getElementById('map-autofill-enabled');\r\n\r\n    if (startBtn) {\r\n        startBtn.addEventListener('click', startPolygonDraw);\r\n    }\r\n    if (finishBtn) {\r\n        finishBtn.addEventListener('click', finishPolygonDraw);\r\n    }\r\n    if (cancelBtn) {\r\n        cancelBtn.addEventListener('click', cancelPolygonSelection);\r\n    }\r\n    if (clearBtn) {\r\n        clearBtn.addEventListener('click', clearPolygonSelection);\r\n    }\r\n\r\n    if (snapDistance) {\r\n        snapDistance.addEventListener('input', updateSnapDistanceLabel);\r\n        updateSnapDistanceLabel();\r\n    }\r\n\r\n    if (mapAutofillEnabled) {\r\n        mapAutofillEnabled.addEventListener('change', function() {\r\n            if (this.checked && map) {\r\n                const center = map.getCenter();\r\n                scheduleMapLocationLookup(center.lat, center.lng, false);\r\n                updateAutofillStatus('Auto-fill enabled. Move map to update location.', false);\r\n            } else {\r\n                updateAutofillStatus('Auto-fill paused.', false);\r\n            }\r\n        });\r\n    }\r\n\r\n    updateAutofillStatus('Move map or draw parcel to auto-detect location.', false);\r\n}\r\n\r\nasync function updateGeometryMetrics(geometry, options) {\r\n    const shouldRefreshMetrics = options && options.shouldRefreshMetrics;\r\n\r\n    if (!geometry) return;\r\n\r\n    const geometryInput = document.getElementById('form-geometry');\r\n    if (geometryInput) {\r\n        geometryInput.value = JSON.stringify(geometry);\r\n    }\r\n\r\n    if (!shouldRefreshMetrics) {\r\n        return;\r\n    }\r\n\r\n    try {\r\n        const result = await calculateArea(geometry);\r\n\r\n        if (!result.area) {\r\n            return;\r\n        }\r\n\r\n        const area = result.area;\r\n        const perimeter = result.perimeter || {};\r\n        const centroid = result.centroid || {};\r\n\r\n        const areaInput = document.getElementById('form-area');\r\n        const areaAuto = document.getElementById('area-auto');\r\n        const areaEquivalents = document.getElementById('area-equivalents');\r\n        const geometryMetrics = document.getElementById('geometry-metrics');\r\n        const perimeterEl = document.getElementById('metric-perimeter');\r\n        const centroidEl = document.getElementById('metric-centroid');\r\n\r\n        if (areaInput) {\r\n            areaInput.value = area.area_ha;\r\n        }\r\n        if (areaAuto) {\r\n            areaAuto.textContent = '(auto)';\r\n        }\r\n\r\n        if (areaEquivalents) {\r\n            areaEquivalents.classList.remove('hidden');\r\n            areaEquivalents.innerHTML = `\r\n                <span><strong>${area.area_acres}</strong> Acres</span>\r\n                <span><strong>${area.area_guntha}</strong> Guntha</span>\r\n                <span><strong>${area.area_bigha_mp}</strong> Bigha</span>\r\n            `;\r\n        }\r\n\r\n        if (geometryMetrics) {\r\n            geometryMetrics.classList.remove('hidden');\r\n        }\r\n\r\n        if (perimeterEl) {\r\n            perimeterEl.textContent = perimeter.perimeter_m\r\n                ? `${perimeter.perimeter_m} m (${perimeter.perimeter_km || 0} km)`\r\n                : '--';\r\n        }\r\n\r\n        if (centroidEl) {\r\n            centroidEl.textContent = typeof centroid.lat === 'number' && typeof centroid.lng === 'number'\r\n                ? `${centroid.lat.toFixed(6)}, ${centroid.lng.toFixed(6)}`\r\n                : '--';\r\n        }\r\n\r\n        if (typeof centroid.lat === 'number' && typeof centroid.lng === 'number') {\r\n            scheduleMapLocationLookup(centroid.lat, centroid.lng, true);\r\n        }\r\n    } catch (err) {\r\n        console.error('Area calculation failed:', err);\r\n        showToast('Area metrics could not be calculated.', 'warning');\r\n    }\r\n}\r\n\r\nfunction switchBaseLayer(layerName) {\r\n    if (!baseLayers[layerName] || layerName === currentBaseLayer) return;\r\n\r\n    map.removeLayer(baseLayers[currentBaseLayer]);\r\n    baseLayers[layerName].addTo(map);\r\n    currentBaseLayer = layerName;\r\n}\r\n\r\n// Add India boundary mask to dim everything outside India\r\nasync function addIndiaMask(mapInstance) {\r\n    try {\r\n        const response = await fetch('/api/boundary');\r\n        if (!response.ok) return;\r\n        \r\n        const indiaGeoJSON = await response.json();\r\n        \r\n        // Create a mask: large rectangle with India cut out as a hole\r\n        const worldBounds = [\r\n            [-180, -90],\r\n            [180, -90],\r\n            [180, 90],\r\n            [-180, 90],\r\n            [-180, -90]\r\n        ];\r\n        \r\n        let maskCoordinates = [worldBounds];\r\n        const geometry = indiaGeoJSON.features[0].geometry;\r\n        \r\n        if (geometry.type === 'MultiPolygon') {\r\n            geometry.coordinates.forEach(polygon => {\r\n                maskCoordinates.push(polygon[0]); // Add exterior ring of each polygon as a hole\r\n            });\r\n        } else if (geometry.type === 'Polygon') {\r\n            maskCoordinates.push(geometry.coordinates[0]);\r\n        }\r\n        \r\n        // Create a GeoJSON with the mask (world minus India)\r\n        const maskGeoJSON = {\r\n            type: 'Feature',\r\n            geometry: {\r\n                type: 'Polygon',\r\n                coordinates: maskCoordinates  // Exterior ring is world, holes are India's parts\r\n            }\r\n        };\r\n        \r\n        // Add the mask layer\r\n        const maskLayer = L.geoJSON(maskGeoJSON, {\r\n            style: {\r\n                color: '#1f2937',\r\n                weight: 2,\r\n                fillColor: '#1f2937',\r\n                fillOpacity: 0.7\r\n            },\r\n            interactive: false  // Allow clicks to pass through\r\n        });\r\n        \r\n        maskLayer.addTo(mapInstance);\r\n        \r\n        // Add India border outline for clarity\r\n        const indiaBorder = L.geoJSON(indiaGeoJSON, {\r\n            style: {\r\n                color: '#f97316',  // Orange border\r\n                weight: 3,\r\n                fillColor: 'transparent',\r\n                fillOpacity: 0,\r\n                opacity: 0.9\r\n            },\r\n            interactive: false\r\n        });\r\n        \r\n        indiaBorder.addTo(mapInstance);\r\n        \r\n    } catch (err) {\r\n        console.warn('Failed to load India boundary mask:', err);\r\n    }\r\n}\r\n\r\nfunction initMap(adminMode) {\r\n    isAdmin = adminMode || false;\r\n\r\n    // Expanded India bounding box with generous margins\r\n    const indiaBounds = L.latLngBounds(\r\n        L.latLng(4.0, 60.0),   // Southwest (extended into ocean)\r\n        L.latLng(40.0, 105.0)  // Northeast (extended into China/Myanmar)\r\n    );\r\n\r\n    map = L.map('map', {\r\n        center: [23.5, 77.5],\r\n        zoom: 7,\r\n        minZoom: 4,\r\n        maxZoom: 18,\r\n        maxBounds: indiaBounds,\r\n        maxBoundsViscosity: 1.0,\r\n        zoomControl: true,\r\n        attributionControl: true\r\n    });\r\n\r\n    baseLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\r\n        attribution: '&copy; OpenStreetMap contributors',\r\n        maxZoom: 19,\r\n        crossOrigin: true\r\n    });\r\n\r\n    baseLayers.google = L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {\r\n        attribution: '&copy; Google Maps',\r\n        maxZoom: 20,\r\n        crossOrigin: true\r\n    });\r\n\r\n    baseLayers.esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {\r\n        attribution: '&copy; Esri',\r\n        maxZoom: 18,\r\n        crossOrigin: true\r\n    });\r\n\r\n    baseLayers.osm.addTo(map);\r\n    currentBaseLayer = 'osm';\r\n\r\n    document.querySelectorAll('input[name=\"basemap\"]').forEach(radio => {\r\n        radio.addEventListener('change', function() {\r\n            switchBaseLayer(this.value);\r\n        });\r\n    });\r\n\r\n    // Add India boundary mask\r\n    addIndiaMask(map);\r\n\r\n    drawnItems = new L.FeatureGroup();\r\n    map.addLayer(drawnItems);\r\n\r\n    if (isAdmin) {\r\n        // For Map View tab, disable all drawing controls\r\n        map.pm.addControls({\r\n            position: 'topleft',\r\n            drawPolygon: false,\r\n            drawMarker: false,\r\n            drawCircleMarker: false,\r\n            drawPolyline: false,\r\n            drawRectangle: false,\r\n            drawCircle: false,\r\n            editMode: false,\r\n            dragMode: false,\r\n            cutPolygon: false,\r\n            removalMode: false,\r\n            rotateMode: false\r\n        });\r\n\r\n        // Hide Geoman toolbar by default (only shown in Add Record tab)\r\n        setTimeout(() => {\r\n            const geomanToolbar = document.querySelector('.leaflet-pm-toolbar');\r\n            if (geomanToolbar) {\r\n                geomanToolbar.style.display = 'none';\r\n            }\r\n        }, 100);\r\n\r\n        map.pm.setPathOptions({\r\n            color: '#ea580c',\r\n            fillColor: '#fed7aa',\r\n            fillOpacity: 0.3,\r\n            weight: 3\r\n        });\r\n\r\n        map.on('pm:create', async function(e) {\r\n            const layer = e.layer;\r\n\r\n            drawnItems.clearLayers();\r\n            drawnItems.addLayer(layer);\r\n            currentSketchLayer = layer;\r\n\r\n            map.pm.disableDraw();\r\n\r\n            const geometry = layer.toGeoJSON().geometry;\r\n            await updateGeometryMetrics(geometry, {\r\n                shouldRefreshMetrics: true\r\n            });\r\n\r\n            switchMainTab('add-record');\r\n            switchFormTab('parcel');\r\n\r\n            const drawInstruction = document.getElementById('draw-instruction');\r\n            if (drawInstruction) {\r\n                drawInstruction.style.display = 'none';\r\n            }\r\n\r\n            showToast('Polygon ready. Review parcel details and save.', 'success');\r\n        });\r\n\r\n        map.on('pm:edit', async function(e) {\r\n            const layer = e.layer;\r\n            if (!layer) return;\r\n\r\n            currentSketchLayer = layer;\r\n            const geometry = layer.toGeoJSON().geometry;\r\n            await updateGeometryMetrics(geometry, {\r\n                shouldRefreshMetrics: true\r\n            });\r\n        });\r\n\r\n        map.on('pm:remove', function() {\r\n            clearGeometrySelection(false);\r\n        });\r\n    }\r\n\r\n    map.on('mousemove', function(e) {\r\n        const coordsEl = document.getElementById('cursor-coords');\r\n        if (coordsEl) {\r\n            coordsEl.textContent = `Lat: ${e.latlng.lat.toFixed(6)}, Lng: ${e.latlng.lng.toFixed(6)}`;\r\n        }\r\n    });\r\n\r\n    if (isAdmin) {\r\n        map.on('moveend', function() {\r\n            const center = map.getCenter();\r\n            scheduleMapLocationLookup(center.lat, center.lng, false);\r\n        });\r\n\r\n        map.on('click', function(e) {\r\n            if (map.pm && typeof map.pm.globalDrawModeEnabled === 'function' && map.pm.globalDrawModeEnabled()) {\r\n                return;\r\n            }\r\n            scheduleMapLocationLookup(e.latlng.lat, e.latlng.lng, true);\r\n        });\r\n    }\r\n\r\n    loadRecordsOnMap();\r\n}\r\n\r\nasync function loadRecordsOnMap() {\r\n    try {\r\n        const records = await fetchRecords();\r\n        allRecordsCache = records;\r\n\r\n        if (isAdmin) {\r\n            syncLocationCatalogWithRecords(records);\r\n            populateStateFilter(records);\r\n            populateDistrictFilter(records);\r\n\r\n            const { stateEl, districtEl, villageEl } = getFormElements();\r\n            const selectedState = stateEl ? stateEl.value : '';\r\n            const selectedDistrict = districtEl ? districtEl.value : '';\r\n            const selectedVillage = villageEl ? villageEl.value : '';\r\n\r\n            refreshStateOptions(selectedState);\r\n            refreshDistrictOptions(selectedDistrict);\r\n            refreshVillageOptions(selectedVillage);\r\n\r\n            applyAdminFilters(false);\r\n            return;\r\n        }\r\n\r\n        clearMapLayers();\r\n        addRecordsToMap(records);\r\n    } catch (err) {\r\n        console.error('Failed to load records:', err);\r\n        showToast('Failed to load land records.', 'error');\r\n    }\r\n}\r\n\r\nfunction clearMapLayers() {\r\n    parcelLayers.forEach(layer => {\r\n        map.removeLayer(layer);\r\n    });\r\n    parcelLayers = [];\r\n}\r\n\r\nfunction addRecordsToMap(records) {\r\n    records.forEach(record => {\r\n        if (!record.geometry || record.geometry.type !== 'Polygon') return;\r\n\r\n        const landUse = (record.attributes && record.attributes.land_use) || 'agricultural';\r\n        const color = getLandUseColor(landUse);\r\n\r\n        const geoJsonLayer = L.geoJSON(record.geometry, {\r\n            style: function() {\r\n                return {\r\n                    color: color,\r\n                    fillColor: color,\r\n                    fillOpacity: 0.25,\r\n                    weight: 2.5,\r\n                    opacity: 0.9\r\n                };\r\n            },\r\n            onEachFeature: function(_feature, layer) {\r\n                layer.on('click', function() {\r\n                    onParcelClick(record, layer);\r\n                });\r\n\r\n                layer.on('mouseover', function() {\r\n                    this.setStyle({ fillOpacity: 0.45, weight: 3.5 });\r\n                });\r\n                layer.on('mouseout', function() {\r\n                    this.setStyle({ fillOpacity: 0.25, weight: 2.5 });\r\n                });\r\n\r\n                const attrs = record.attributes || {};\r\n                const owner = record.owner || {};\r\n                const loc = record.location || {};\r\n\r\n                layer.bindTooltip(\r\n                    `<div class=\"tooltip-content\">\r\n                        <div class=\"tooltip-title\">${record.khasra_no || 'N/A'}</div>\r\n                        <div class=\"tooltip-row\">\r\n                            <span class=\"tooltip-label\">ULPIN:</span>\r\n                            <span class=\"tooltip-value\">${record.ulpin || 'N/A'}</span>\r\n                        </div>\r\n                        <div class=\"tooltip-row\">\r\n                            <span class=\"tooltip-label\">Land Use:</span>\r\n                            <span class=\"tooltip-value\">${landUse}</span>\r\n                        </div>\r\n                        <div class=\"tooltip-row\">\r\n                            <span class=\"tooltip-label\">Area:</span>\r\n                            <span class=\"tooltip-value\">${attrs.area_ha || '?'} Ha</span>\r\n                        </div>\r\n                        <div class=\"tooltip-divider\"></div>\r\n                        <div class=\"tooltip-row\">\r\n                            <span class=\"tooltip-label\">Owner:</span>\r\n                            <span class=\"tooltip-value\">${owner.name || 'N/A'}</span>\r\n                        </div>\r\n                        <div class=\"tooltip-row\">\r\n                            <span class=\"tooltip-label\">Location:</span>\r\n                            <span class=\"tooltip-value\">${loc.village || '?'}, ${loc.district || '?'}</span>\r\n                        </div>\r\n                        <div class=\"tooltip-hint\">Click to view details →</div>\r\n                    </div>`,\r\n                    { sticky: true, className: 'parcel-tooltip', direction: 'top', offset: [0, -10] }\r\n                );\r\n            }\r\n        });\r\n\r\n        geoJsonLayer.addTo(map);\r\n        geoJsonLayer._recordId = record._id;\r\n        parcelLayers.push(geoJsonLayer);\r\n    });\r\n\r\n    if (isAdmin) {\r\n        renderRecordsList(records);\r\n    }\r\n}\r\n\r\nfunction getLandUseColor(landUse) {\r\n    const colors = {\r\n        Agricultural: '#22c55e',\r\n        Residential: '#3b82f6',\r\n        Commercial: '#f59e0b',\r\n        Industrial: '#8b5cf6',\r\n        Government: '#ef4444',\r\n        Forest: '#065f46',\r\n        Wasteland: '#9ca3af'\r\n    };\r\n    return colors[landUse] || '#6b7280';\r\n}\r\n\r\nfunction onParcelClick(record) {\r\n    if (isAdmin) {\r\n        showAdminDetails(record);\r\n        flyToRecord(record);\r\n    } else if (typeof showViewerInfo === 'function') {\r\n        showViewerInfo(record);\r\n    }\r\n}\r\n\r\nfunction flyToRecord(record) {\r\n    if (!record.geometry) return;\r\n    const geoJsonLayer = L.geoJSON(record.geometry);\r\n    const bounds = geoJsonLayer.getBounds();\r\n    map.fitBounds(bounds, { padding: [60, 60], maxZoom: 16 });\r\n}\r\n\r\nfunction switchRecordsView(mode) {\r\n    const cardsContainer = document.getElementById('records-list-cards');\r\n    const tableContainer = document.getElementById('records-list-table');\r\n\r\n    if (!cardsContainer || !tableContainer) return;\r\n\r\n    recordsViewMode = mode === 'table' ? 'table' : 'cards';\r\n\r\n    cardsContainer.classList.toggle('hidden', recordsViewMode !== 'cards');\r\n    tableContainer.classList.toggle('hidden', recordsViewMode !== 'table');\r\n\r\n    document.querySelectorAll('.records-view-tab').forEach(tab => {\r\n        tab.classList.toggle('active', tab.dataset.view === recordsViewMode);\r\n    });\r\n}\r\n\r\nfunction renderRecordsList(records) {\r\n    const cardsEl = document.getElementById('records-list-cards');\r\n    const tableEl = document.getElementById('records-list-table');\r\n    const noRecordsEl = document.getElementById('no-records');\r\n    const countLabel = document.getElementById('records-count-label');\r\n\r\n    if (!cardsEl || !tableEl) return;\r\n\r\n    if (countLabel) {\r\n        countLabel.textContent = String(records.length);\r\n    }\r\n\r\n    if (records.length === 0) {\r\n        cardsEl.innerHTML = '';\r\n        tableEl.innerHTML = '';\r\n        if (noRecordsEl) noRecordsEl.classList.remove('hidden');\r\n        return;\r\n    }\r\n\r\n    if (noRecordsEl) noRecordsEl.classList.add('hidden');\r\n\r\n    cardsEl.innerHTML = records.map(rec => {\r\n        const attrs = rec.attributes || {};\r\n        const owner = rec.owner || {};\r\n        const loc = rec.location || {};\r\n        const landUse = attrs.land_use || 'Unknown';\r\n        const badgeClass = 'badge-' + landUse.toLowerCase();\r\n\r\n        return `\r\n            <div class=\"record-card fade-in ${selectedRecordId === rec._id ? 'active' : ''}\"\r\n                 data-id=\"${rec._id}\">\r\n                <div class=\"flex items-center justify-between mb-1\">\r\n                    <div class=\"flex items-center gap-2\">\r\n                        <span class=\"font-semibold text-gray-800 text-sm\">${rec.khasra_no || 'N/A'}</span>\r\n                        ${rec.deleted ? `<span class=\"badge-deleted\">Deleted</span>` : ''}\r\n                    </div>\r\n                    <span class=\"land-use-badge ${badgeClass}\">${landUse}</span>\r\n                </div>\r\n                <div class=\"text-xs text-gray-500 space-y-0.5\">\r\n                    <div>ULPIN: <span class=\"font-mono\">${rec.ulpin || 'N/A'}</span></div>\r\n                    <div>${owner.name || 'No Owner'} | ${attrs.area_ha || '?'} Ha</div>\r\n                    <div>${loc.village || ''}, ${loc.district || ''}</div>\r\n                </div>\r\n                <div class=\"flex items-center justify-between mt-2\">\r\n                    <div class=\"text-xs text-gray-500\">\r\n                        <div>${loc.state || ''} • ${loc.district || ''}</div>\r\n                    </div>\r\n                    <button type=\"button\" class=\"view-record-btn bg-orange-600 hover:bg-orange-700 text-white text-xs px-4 py-1.5 rounded-lg transition font-medium\">\r\n                        View\r\n                    </button>\r\n                </div>\r\n            </div>\r\n        `;\r\n    }).join('');\r\n\r\n    // Attach click handlers for view buttons\r\n    cardsEl.querySelectorAll('.view-record-btn').forEach((btn, index) => {\r\n        btn.addEventListener('click', function(e) {\r\n            e.stopPropagation();\r\n            const card = this.closest('.record-card');\r\n            if (card) {\r\n                viewRecordDetails(records[index]._id);\r\n            }\r\n        });\r\n    });\r\n\r\n    tableEl.innerHTML = records.map((rec, index) => {\r\n        const attrs = rec.attributes || {};\r\n        const loc = rec.location || {};\r\n        const landUse = attrs.land_use || 'Unknown';\r\n        const recordValue = asNumber(attrs.area_ha) * asNumber(attrs.circle_rate_inr);\r\n\r\n        return `\r\n            <div class=\"record-table-row ${selectedRecordId === rec._id ? 'active' : ''} ${rec.deleted ? 'deleted' : ''}\" data-id=\"${rec._id}\">\r\n                <div class=\"flex items-center justify-between gap-2\">\r\n                    <div class=\"min-w-0\">\r\n                        <p class=\"text-sm font-semibold text-gray-800 truncate\">${escapeHtml(rec.khasra_no || 'N/A')} ${rec.deleted ? '<span class=\"badge-deleted ml-2\">Deleted</span>' : ''}</p>\r\n                        <p class=\"text-[11px] text-gray-500 truncate\">${escapeHtml(rec.ulpin || 'N/A')} | ${escapeHtml(loc.district || 'N/A')}</p>\r\n                    </div>\r\n                    <button type=\"button\" class=\"view-record-btn-table bg-orange-600 hover:bg-orange-700 text-white text-[11px] px-2 py-1 rounded\">View</button>\r\n                </div>\r\n                <div class=\"mt-2 grid grid-cols-3 gap-2 text-[11px] text-gray-600\">\r\n                    <span>${escapeHtml(landUse)}</span>\r\n                    <span>${asNumber(attrs.area_ha).toFixed(2)} Ha</span>\r\n                    <span>Rs. ${formatInr(recordValue)}</span>\r\n                </div>\r\n            </div>\r\n        `;\r\n    }).join('');\r\n\r\n    // Attach click handlers for table view buttons\r\n    tableEl.querySelectorAll('.view-record-btn-table').forEach((btn, index) => {\r\n        btn.addEventListener('click', function(e) {\r\n            e.stopPropagation();\r\n            const row = this.closest('.record-table-row');\r\n            if (row) {\r\n                viewRecordDetails(records[index]._id);\r\n            }\r\n        });\r\n    });\r\n\r\n    switchRecordsView(recordsViewMode);\r\n}\r\n\r\nasync function selectRecord(recordId) {\r\n    selectedRecordId = recordId;\r\n\r\n    document.querySelectorAll('.record-card').forEach(el => {\r\n        el.classList.toggle('active', el.dataset.id === recordId);\r\n    });\r\n\r\n    document.querySelectorAll('.record-table-row').forEach(el => {\r\n        el.classList.toggle('active', el.dataset.id === recordId);\r\n    });\r\n\r\n    try {\r\n        const record = await fetchRecord(recordId);\r\n        flyToRecord(record);\r\n        showAdminDetails(record);\r\n    } catch (_err) {\r\n        showToast('Failed to load record details.', 'error');\r\n    }\r\n}\r\n\r\n// View record details in full screen tab\r\nasync function viewRecordDetails(recordId) {\r\n    selectedRecordId = recordId;\r\n    selectedRecord = null; // Will be populated\r\n\r\n    try {\r\n        const record = await fetchRecord(recordId);\r\n        selectedRecord = record;\r\n        \r\n        // Switch to view record tab\r\n        switchMainTab('view-record');\r\n        \r\n        // Populate details\r\n        const loc = record.location || {};\r\n        const attrs = record.attributes || {};\r\n        const owner = record.owner || {};\r\n        const mutations = record.mutation_history || [];\r\n        \r\n        document.getElementById('view-khasra').textContent = record.khasra_no || 'N/A';\r\n        document.getElementById('view-ulpin').textContent = 'ULPIN: ' + (record.ulpin || 'N/A');\r\n        document.getElementById('view-land-use-badge').textContent = attrs.land_use || 'Unknown';\r\n        document.getElementById('view-land-use-badge').className = 'land-use-badge badge-' + (attrs.land_use || '').toLowerCase();\r\n        document.getElementById('view-state').textContent = loc.state || 'N/A';\r\n        document.getElementById('view-district').textContent = loc.district || 'N/A';\r\n        document.getElementById('view-village').textContent = loc.village || 'N/A';\r\n        document.getElementById('view-khata').textContent = record.khata_no || 'N/A';\r\n        document.getElementById('view-area').textContent = attrs.area_ha ? attrs.area_ha + ' Ha' : 'N/A';\r\n        document.getElementById('view-rate').textContent = attrs.circle_rate_inr ? 'Rs. ' + Number(attrs.circle_rate_inr).toLocaleString() + '/ha' : 'N/A';\r\n        \r\n        const value = asNumber(attrs.area_ha) * asNumber(attrs.circle_rate_inr);\r\n        document.getElementById('view-value').textContent = 'Rs. ' + formatInr(value);\r\n        \r\n        document.getElementById('view-owner').textContent = owner.name || 'N/A';\r\n        document.getElementById('view-share').textContent = owner.share_pct ? owner.share_pct + '%' : 'N/A';\r\n        document.getElementById('view-aadhaar').textContent = owner.aadhaar_mask || 'N/A';\r\n        \r\n        // Owner document logic\r\n        const ownerDocContainer = document.getElementById('view-owner-doc-container');\r\n        const ownerDocLink = document.getElementById('view-owner-doc-link');\r\n        if (owner.proof_doc_b64) {\r\n            ownerDocContainer.classList.remove('hidden');\r\n            ownerDocLink.href = owner.proof_doc_b64;\r\n        } else {\r\n            ownerDocContainer.classList.add('hidden');\r\n        }\r\n\r\n        // Mutation history\r\n        const mutationsEl = document.getElementById('view-mutations');\r\n        if (mutations.length > 0) {\r\n            mutationsEl.innerHTML = mutations.map(m => {\r\n                const docLinkHTML = m.proof_doc_b64 ? `<a href=\"${m.proof_doc_b64}\" download=\"Mutation_Proof\" class=\"text-blue-600 hover:text-blue-800 font-medium underline flex items-center gap-1 mt-2 text-xs\"><svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg> Download Proof</a>` : '';\r\n                return `\r\n                <div class=\"bg-yellow-50 border border-yellow-200 rounded-lg p-3\">\r\n                    <div class=\"flex items-center justify-between\">\r\n                        <span class=\"text-sm font-medium text-gray-800\">${m.previous_owner} (${m.previous_share_pct}%)</span>\r\n                        <span class=\"text-xs text-gray-500\">${m.mutation_type}</span>\r\n                    </div>\r\n                    <p class=\"text-xs text-gray-600 mt-1\">Date: ${m.mutation_date} | Ref: ${m.mutation_ref || 'N/A'}</p>\r\n                    ${docLinkHTML}\r\n                </div>\r\n            `;\r\n            }).join('');\r\n        } else {\r\n            mutationsEl.innerHTML = '<p class=\"text-sm text-gray-500\">No mutation history</p>';\r\n        }\r\n        \r\n        // Initialize small map after tab switch\r\n        setTimeout(() => initViewRecordMap(record), 200);\r\n        \r\n    } catch (_err) {\r\n        showToast('Failed to load record details.', 'error');\r\n    }\r\n}\r\n\r\n// Initialize small map for view record tab\r\nfunction initViewRecordMap(record) {\r\n    if (!document.getElementById('view-record-map')) return;\r\n    \r\n    // Destroy existing map if any\r\n    if (viewRecordMap) {\r\n        viewRecordMap.remove();\r\n        viewRecordMap = null;\r\n    }\r\n    \r\n    // Expanded India bounding box with generous margins\r\n    const indiaBounds = L.latLngBounds(\r\n        L.latLng(4.0, 60.0),   // Southwest (extended into ocean)\r\n        L.latLng(40.0, 105.0)  // Northeast (extended into China/Myanmar)\r\n    );\r\n\r\n    viewRecordMap = L.map('view-record-map', {\r\n        center: [23.5, 77.5],\r\n        zoom: 7,\r\n        minZoom: 4,\r\n        maxZoom: 18,\r\n        maxBounds: indiaBounds,\r\n        maxBoundsViscosity: 1.0,\r\n        zoomControl: true\r\n    });\r\n    \r\n    // Add basemap layers for view record map\r\n    const viewBaseLayers = {};\r\n    viewBaseLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\r\n        attribution: '&copy; OpenStreetMap contributors',\r\n        maxZoom: 19,\r\n        crossOrigin: true\r\n    });\r\n    viewBaseLayers.google = L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {\r\n        attribution: '&copy; Google Maps',\r\n        maxZoom: 20,\r\n        crossOrigin: true\r\n    });\r\n    viewBaseLayers.esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {\r\n        attribution: '&copy; Esri',\r\n        maxZoom: 18,\r\n        crossOrigin: true\r\n    });\r\n    \r\n    viewBaseLayers.osm.addTo(viewRecordMap);\r\n    let currentViewBaseLayer = 'osm';\r\n    \r\n    // Handle layer switching for view record map\r\n    document.querySelectorAll('input[name=\"view-basemap\"]').forEach(radio => {\r\n        radio.addEventListener('change', function() {\r\n            if (viewBaseLayers[this.value] && this.value !== currentViewBaseLayer) {\r\n                viewRecordMap.removeLayer(viewBaseLayers[currentViewBaseLayer]);\r\n                viewBaseLayers[this.value].addTo(viewRecordMap);\r\n                currentViewBaseLayer = this.value;\r\n            }\r\n        });\r\n    });\r\n    \r\n    // Coordinate display\r\n    viewRecordMap.on('mousemove', function(e) {\r\n        const coordsEl = document.getElementById('view-cursor-coords');\r\n        if (coordsEl) {\r\n            coordsEl.textContent = `Lat: ${e.latlng.lat.toFixed(5)}, Lng: ${e.latlng.lng.toFixed(5)}`;\r\n        }\r\n    });\r\n    \r\n    // Add parcel geometry\r\n    if (record.geometry) {\r\n        const landUse = (record.attributes && record.attributes.land_use) || '';\r\n        const color = getLandUseColor(landUse);\r\n        \r\n        const geoJsonLayer = L.geoJSON(record.geometry, {\r\n            style: {\r\n                color: color || '#ea580c',\r\n                fillColor: color || '#ea580c',\r\n                fillOpacity: 0.35,\r\n                weight: 3\r\n            }\r\n        });\r\n        \r\n        geoJsonLayer.addTo(viewRecordMap);\r\n        \r\n        const bounds = geoJsonLayer.getBounds();\r\n        viewRecordMap.fitBounds(bounds, { padding: [40, 40] });\r\n    }\r\n    \r\n    setTimeout(() => {\r\n        viewRecordMap.invalidateSize();\r\n        // Add India mask to view record map\r\n        addIndiaMask(viewRecordMap);\r\n    }, 200);\r\n}\r\n\r\n// Render and show audit modal\r\nfunction showAuditModal(entries) {\r\n    const container = document.getElementById('audit-entries');\r\n    if (!container) return;\r\n\r\n    if (!Array.isArray(entries)) entries = [];\r\n\r\n    // Prepare internal state for client-side filtering/pagination\r\n    container.__auditEntries = entries.slice();\r\n    container.__auditPage = 1;\r\n    container.__auditPageSize = 10;\r\n\r\n    function getActionBadge(action) {\r\n        const a = (action || '').toLowerCase();\r\n        if (a.includes('delete')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800\">Delete</span>';\r\n        if (a.includes('create') || a.includes('add')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800\">Create</span>';\r\n        if (a.includes('update') || a.includes('edit')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800\">Update</span>';\r\n        if (a.includes('restore')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800\">Restore</span>';\r\n        return `<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800\">${escapeHtml(action)}</span>`;\r\n    }\r\n\r\n    function renderAuditList(page = 1, pageSize = 10, filter = {}) {\r\n        const all = container.__auditEntries || [];\r\n        let filtered = all.filter(e => {\r\n            if (filter.q) {\r\n                const q = filter.q.toLowerCase();\r\n                const hay = [e.action, e.performed_by, e.user, e.record_id, JSON.stringify(e.details || {})].join(' ').toLowerCase();\r\n                if (!hay.includes(q)) return false;\r\n            }\r\n            if (filter.action && filter.action !== 'all') {\r\n                if (!String(e.action || '').toLowerCase().includes(filter.action)) return false;\r\n            }\r\n            if (filter.user && filter.user !== 'all') {\r\n                if (!String((e.performed_by||e.user||'')).toLowerCase().includes(filter.user)) return false;\r\n            }\r\n            return true;\r\n        });\r\n\r\n        const total = filtered.length;\r\n        const pages = Math.max(1, Math.ceil(total / pageSize));\r\n        if (page > pages) page = pages;\r\n        container.__auditPage = page;\r\n        container.__auditPageSize = pageSize;\r\n\r\n        const start = (page - 1) * pageSize;\r\n        const pageItems = filtered.slice(start, start + pageSize);\r\n\r\n        const controlsHtml = `\r\n            <div class=\"flex flex-col md:flex-row md:items-center justify-between mb-4 gap-4\">\r\n                <div class=\"flex flex-wrap items-center gap-3\">\r\n                    <input id=\"audit-search\" placeholder=\"Search logs...\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm w-64 focus:ring-orange-500 focus:border-orange-500\" value=\"${escapeHtml(filter.q || '')}\" />\r\n                    <select id=\"audit-action-filter\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\r\n                        <option value=\"all\">All Actions</option>\r\n                        <option value=\"create\" ${filter.action === 'create' ? 'selected' : ''}>Create</option>\r\n                        <option value=\"update\" ${filter.action === 'update' ? 'selected' : ''}>Update</option>\r\n                        <option value=\"delete\" ${filter.action === 'delete' ? 'selected' : ''}>Delete</option>\r\n                        <option value=\"restore\" ${filter.action === 'restore' ? 'selected' : ''}>Restore</option>\r\n                    </select>\r\n                    <select id=\"audit-user-filter\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\r\n                        <option value=\"all\">All Users</option>\r\n                    </select>\r\n                </div>\r\n                <div class=\"flex items-center gap-3\">\r\n                    <div class=\"text-sm font-medium text-gray-600\">${total} results</div>\r\n                    <select id=\"audit-page-size\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\r\n                        <option value=\"10\" ${pageSize === 10 ? 'selected' : ''}>10 per page</option>\r\n                        <option value=\"25\" ${pageSize === 25 ? 'selected' : ''}>25 per page</option>\r\n                        <option value=\"50\" ${pageSize === 50 ? 'selected' : ''}>50 per page</option>\r\n                    </select>\r\n                    <button id=\"audit-download\" class=\"px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 flex items-center gap-2\">\r\n                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg>\r\n                        Export\r\n                    </button>\r\n                </div>\r\n            </div>\r\n            <div class=\"overflow-x-auto bg-white border border-gray-200 rounded-lg shadow-sm\">\r\n                <table class=\"min-w-full divide-y divide-gray-200\">\r\n                    <thead class=\"bg-gray-50\">\r\n                        <tr>\r\n                            <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Timestamp</th>\r\n                            <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Action</th>\r\n                            <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">User</th>\r\n                            <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Record ID</th>\r\n                            <th scope=\"col\" class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Details</th>\r\n                        </tr>\r\n                    </thead>\r\n                    <tbody id=\"audit-list\" class=\"bg-white divide-y divide-gray-200\"></tbody>\r\n                </table>\r\n            </div>\r\n            <div id=\"audit-pager\" class=\"mt-4 flex items-center justify-between text-sm text-gray-700 bg-white p-3 rounded-lg border border-gray-200 shadow-sm\"></div>\r\n        `;\r\n\r\n        container.innerHTML = controlsHtml;\r\n\r\n        // populate user filter options\r\n        const users = Array.from(new Set(all.map(a => (a.performed_by||a.user||'').toLowerCase()).filter(Boolean))).sort();\r\n        const userFilter = document.getElementById('audit-user-filter');\r\n        users.forEach(u => {\r\n            const opt = document.createElement('option'); \r\n            opt.value = u; \r\n            opt.textContent = u; \r\n            if (filter.user === u) opt.selected = true;\r\n            userFilter.appendChild(opt);\r\n        });\r\n\r\n        const listEl = document.getElementById('audit-list');\r\n        if (pageItems.length === 0) {\r\n            listEl.innerHTML = '<tr><td colspan=\"5\" class=\"px-6 py-4 text-center text-sm text-gray-500\">No logs found matching your criteria.</td></tr>';\r\n        } else {\r\n            pageItems.forEach(e => {\r\n                const ts = e.timestamp || e.time || e.created_at || '';\r\n                const action = e.action || 'unknown';\r\n                const by = e.performed_by || e.user || 'system';\r\n                const rid = e.record_id || '-';\r\n                \r\n                let detailsStr = '';\r\n                if (e.details) {\r\n                    if (typeof e.details === 'string') {\r\n                        detailsStr = e.details;\r\n                    } else {\r\n                        // Extract meaningful details for table\r\n                        const parts = [];\r\n                        if (e.details.khasra_no) parts.push(`Khasra: ${e.details.khasra_no}`);\r\n                        if (e.details.ulpin) parts.push(`ULPIN: ${e.details.ulpin}`);\r\n                        if (e.details.changes) {\r\n                            const numChanges = Object.keys(e.details.changes).length;\r\n                            parts.push(`${numChanges} field(s) changed`);\r\n                        }\r\n                        if (parts.length > 0) {\r\n                            detailsStr = parts.join(' | ');\r\n                        } else {\r\n                            detailsStr = JSON.stringify(e.details).slice(0, 100);\r\n                        }\r\n                    }\r\n                }\r\n                const detailsRaw = e.details ? (typeof e.details === 'string' ? e.details : JSON.stringify(e.details, null, 2)) : '';\r\n\r\n                let dateStr = ts;\r\n                try {\r\n                    const d = new Date(ts);\r\n                    dateStr = d.toLocaleString();\r\n                } catch (_) {}\r\n\r\n                const tr = document.createElement('tr');\r\n                tr.className = 'hover:bg-gray-50 transition-colors';\r\n                tr.innerHTML = `\r\n                    <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${escapeHtml(dateStr)}</td>\r\n                    <td class=\"px-6 py-4 whitespace-nowrap text-sm\">${getActionBadge(action)}</td>\r\n                    <td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900\">${escapeHtml(by)}</td>\r\n                    <td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono\">${escapeHtml(rid)}</td>\r\n                    <td class=\"px-6 py-4 text-sm text-gray-600 max-w-md\">\r\n                        <div class=\"truncate cursor-pointer hover:text-gray-900\" title=\"${escapeHtml(detailsRaw)}\">${escapeHtml(detailsStr || 'No details')}</div>\r\n                    </td>\r\n                `;\r\n                listEl.appendChild(tr);\r\n            });\r\n        }\r\n\r\n        // pager\r\n        const pager = document.getElementById('audit-pager');\r\n        const prevDisabled = page <= 1 ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50';\r\n        const nextDisabled = page >= pages ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-50';\r\n        \r\n        pager.innerHTML = `\r\n            <div class=\"font-medium text-gray-500\">Page ${page} of ${pages}</div>\r\n            <div class=\"flex gap-2\">\r\n                <button id=\"audit-prev\" class=\"px-3 py-1.5 bg-white border border-gray-300 rounded-md text-sm font-medium text-gray-700 ${prevDisabled}\" ${page <= 1 ? 'disabled' : ''}>Previous</button>\r\n                <button id=\"audit-next\" class=\"px-3 py-1.5 bg-white border border-gray-300 rounded-md text-sm font-medium text-gray-700 ${nextDisabled}\" ${page >= pages ? 'disabled' : ''}>Next</button>\r\n            </div>\r\n        `;\r\n\r\n        const getFilterState = () => ({\r\n            q: document.getElementById('audit-search').value,\r\n            action: document.getElementById('audit-action-filter').value,\r\n            user: document.getElementById('audit-user-filter').value\r\n        });\r\n\r\n        // wire up controls\r\n        const searchInput = document.getElementById('audit-search');\r\n        searchInput.addEventListener('input', debounce(() => {\r\n            renderAuditList(1, container.__auditPageSize, getFilterState());\r\n        }, 300));\r\n        \r\n        // Ensure cursor is at the end of the text when re-rendering with search\r\n        if (document.activeElement.id === 'audit-search') {\r\n            setTimeout(() => {\r\n                const input = document.getElementById('audit-search');\r\n                if (input) {\r\n                    input.focus();\r\n                    const len = input.value.length;\r\n                    input.setSelectionRange(len, len);\r\n                }\r\n            }, 0);\r\n        }\r\n\r\n        document.getElementById('audit-action-filter').addEventListener('change', () => {\r\n            renderAuditList(1, container.__auditPageSize, getFilterState());\r\n        });\r\n        document.getElementById('audit-user-filter').addEventListener('change', () => {\r\n            renderAuditList(1, container.__auditPageSize, getFilterState());\r\n        });\r\n        document.getElementById('audit-page-size').addEventListener('change', () => {\r\n            const ps = parseInt(document.getElementById('audit-page-size').value, 10) || 10;\r\n            renderAuditList(1, ps, getFilterState());\r\n        });\r\n\r\n        document.getElementById('audit-prev').addEventListener('click', () => {\r\n            if (container.__auditPage > 1) renderAuditList(container.__auditPage - 1, container.__auditPageSize, getFilterState());\r\n        });\r\n        document.getElementById('audit-next').addEventListener('click', () => {\r\n            if (container.__auditPage < pages) renderAuditList(container.__auditPage + 1, container.__auditPageSize, getFilterState());\r\n        });\r\n\r\n        // download\r\n        document.getElementById('audit-download').addEventListener('click', () => {\r\n            try {\r\n                // If filtered, download filtered data, otherwise all\r\n                const dataToDownload = filtered.length > 0 ? filtered : container.__auditEntries;\r\n                const blob = new Blob([JSON.stringify(dataToDownload, null, 2)], { type: 'application/json' });\r\n                const url = URL.createObjectURL(blob);\r\n                const a = document.createElement('a'); \r\n                a.href = url; \r\n                a.download = `audit_log_${new Date().toISOString().split('T')[0]}.json`; \r\n                document.body.appendChild(a); \r\n                a.click(); \r\n                a.remove(); \r\n                URL.revokeObjectURL(url);\r\n            } catch (e) { \r\n                console.error('Audit download failed', e); \r\n                showToast('Failed to download audit JSON.', 'error'); \r\n            }\r\n        });\r\n    }\r\n\r\n    // initialize\r\n    renderAuditList(1, container.__auditPageSize, {});\r\n}\r\n\r\nfunction showAdminDetails(record) {\r\n    selectedRecordId = record._id;\r\n    selectedRecord = record; // Save for print/delete buttons\r\n\r\n    // Show details in the map overlay panel instead of sidebar\r\n    const detailsPanel = document.getElementById('map-details-panel');\r\n    const detailsActions = document.getElementById('map-details-actions');\r\n    if (detailsPanel) {\r\n        detailsPanel.classList.remove('hidden');\r\n    }\r\n    if (detailsActions) {\r\n        detailsActions.classList.remove('hidden');\r\n    }\r\n    \r\n    const content = document.getElementById('map-details-content');\r\n    if (!content) return;\r\n\r\n    const loc = record.location || {};\r\n    const attrs = record.attributes || {};\r\n    const owner = record.owner || {};\r\n    const mutations = record.mutation_history || [];\r\n\r\n    let mutationHtml = '';\r\n    if (mutations.length > 0) {\r\n        mutationHtml = `\r\n            <div class=\"mt-3\">\r\n                <p class=\"text-xs font-semibold text-gray-500 mb-1\">MUTATION HISTORY</p>\r\n                <div class=\"space-y-2\">\r\n                    ${mutations.map(m => `\r\n                        <div class=\"bg-yellow-50 p-2 rounded text-xs\">\r\n                            <div class=\"font-semibold\">${m.previous_owner} (${m.previous_share_pct}%)</div>\r\n                            <div class=\"text-gray-500\">${m.mutation_type} on ${m.mutation_date}</div>\r\n                            <div class=\"text-gray-400\">Ref: ${m.mutation_ref || 'N/A'}</div>\r\n                        </div>\r\n                    `).join('')}\r\n                </div>\r\n            </div>\r\n        `;\r\n    }\r\n    \r\n    content.innerHTML = `\r\n        <div class=\"fade-in\">\r\n            <div class=\"flex items-center justify-between mb-3\">\r\n                <div class=\"flex items-center gap-2\">\r\n                    <h3 class=\"text-sm font-bold text-gray-800\">${record.khasra_no || 'N/A'}</h3>\r\n                    ${record.deleted ? `<span class=\"text-xs bg-red-100 text-red-700 px-2 py-0.5 rounded-full font-semibold\">Soft-deleted</span>` : ''}\r\n                </div>\r\n                <span class=\"land-use-badge badge-${(attrs.land_use || '').toLowerCase()}\">${attrs.land_use || 'N/A'}</span>\r\n            </div>\r\n\r\n            <div class=\"space-y-2 text-xs\">\r\n                <div class=\"grid grid-cols-2 gap-x-3 gap-y-1\">\r\n                    <span class=\"text-gray-500\">ULPIN</span>\r\n                    <span class=\"font-mono\">${record.ulpin || 'N/A'}</span>\r\n                    <span class=\"text-gray-500\">Khata No.</span>\r\n                    <span>${record.khata_no || 'N/A'}</span>\r\n                    <span class=\"text-gray-500\">Area</span>\r\n                    <span>${attrs.area_ha || 'N/A'} Ha</span>\r\n                    <span class=\"text-gray-500\">Circle Rate</span>\r\n                    <span>Rs. ${(attrs.circle_rate_inr || 0).toLocaleString()}/ha</span>\r\n                    <span class=\"text-gray-500\">Village</span>\r\n                    <span>${loc.village || 'N/A'}</span>\r\n                    <span class=\"text-gray-500\">District</span>\r\n                    <span>${loc.district || 'N/A'}</span>\r\n                    <span class=\"text-gray-500\">State</span>\r\n                    <span>${loc.state || 'N/A'}</span>\r\n                </div>\r\n\r\n                <hr class=\"my-2\">\r\n                <p class=\"text-xs font-semibold text-gray-500\">OWNER</p>\r\n                <div class=\"grid grid-cols-2 gap-x-3 gap-y-1\">\r\n                    <span class=\"text-gray-500\">Name</span>\r\n                    <span class=\"font-medium\">${owner.name || 'N/A'}</span>\r\n                    <span class=\"text-gray-500\">Share</span>\r\n                    <span>${owner.share_pct || 'N/A'}%</span>\r\n                    <span class=\"text-gray-500\">Aadhaar</span>\r\n                    <span class=\"font-mono\">${owner.aadhaar_mask || 'N/A'}</span>\r\n                </div>\r\n\r\n                ${mutationHtml}\r\n                \r\n                ${record.deleted ? `\r\n                    <div class=\"mt-3 bg-red-50 border-l-4 border-red-500 rounded-r-lg p-3 text-xs text-red-800\">\r\n                        <p class=\"font-semibold\">Deletion details</p>\r\n                        <p>Deleted by: <span class=\"font-medium\">${record.deleted_by || 'Unknown'}</span></p>\r\n                        <p>Deleted at: <span class=\"font-medium\">${record.deleted_at || 'Unknown'}</span></p>\r\n                    </div>\r\n                ` : ''}\r\n            </div>\r\n\r\n            <div class=\"mt-4\">\r\n                <button id=\"btn-map-edit\"\r\n                    class=\"w-full bg-orange-600 hover:bg-orange-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">\r\n                    <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"/></svg>\r\n                    Edit / Mutate\r\n                </button>\r\n            </div>\r\n        </div>\r\n    `;\r\n    \r\n    // Attach edit button handler\r\n    const editBtn = document.getElementById('btn-map-edit');\r\n    if (editBtn) {\r\n        editBtn.addEventListener('click', function() {\r\n            editRecord(record._id);\r\n        });\r\n    }\r\n\r\n    // Rebuild details action buttons depending on deleted state\r\n    // detailsActions reference already obtained above\r\n    if (detailsActions) {\r\n        // If record is soft-deleted, show Restore and Hard Delete buttons prominently\r\n        if (record.deleted) {\r\n            detailsActions.innerHTML = `\r\n                <button id=\"btn-map-restore\" class=\"flex-1 bg-green-600 hover:bg-green-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">Restore</button>\r\n                <button id=\"btn-map-hard-delete\" class=\"flex-1 bg-red-600 hover:bg-red-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">Hard Delete</button>\r\n            `;\r\n\r\n            const restoreBtn = document.getElementById('btn-map-restore');\r\n            if (restoreBtn) {\r\n                restoreBtn.addEventListener('click', function() {\r\n                    if (!record || !record._id) return showToast('No record selected.', 'error');\r\n                    showConfirmModal(`Restore record \"${record.khasra_no || ''}\"?`, async () => {\r\n                        try {\r\n                            await restoreRecord(record._id);\r\n                            showToast('Record restored.', 'success');\r\n                            selectedRecordId = null;\r\n                            selectedRecord = null;\r\n                            // Refresh map/list\r\n                            loadRecordsOnMap();\r\n                            const panel = document.getElementById('map-details-panel'); if (panel) panel.classList.add('hidden');\r\n                        } catch (err) {\r\n                            showToast('Restore failed: ' + err.message, 'error');\r\n                        }\r\n                    });\r\n                });\r\n            }\r\n\r\n            const hardDeleteBtn = document.getElementById('btn-map-hard-delete');\r\n            if (hardDeleteBtn) {\r\n                hardDeleteBtn.addEventListener('click', function() {\r\n                    if (!record || !record._id) return showToast('No record selected.', 'error');\r\n                    showConfirmModal(`Permanently delete record \"${record.khasra_no || ''}\"? This cannot be undone.`, () => {\r\n                        deleteRecord(record._id).then(() => {\r\n                            showToast('Record permanently deleted.', 'success');\r\n                            selectedRecordId = null; selectedRecord = null;\r\n                            loadRecordsOnMap();\r\n                            const panel = document.getElementById('map-details-panel'); if (panel) panel.classList.add('hidden');\r\n                        }).catch(err => showToast('Delete failed: ' + err.message, 'error'));\r\n                    });\r\n                });\r\n            }\r\n        } else {\r\n            // Restore default actions: Edit / Print / Delete\r\n            detailsActions.innerHTML = `\r\n                <button id=\"btn-map-edit\" class=\"flex-1 bg-orange-600 hover:bg-orange-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">Edit</button>\r\n                <button id=\"btn-map-print\" class=\"flex-1 bg-blue-600 hover:bg-blue-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">Print</button>\r\n                <button id=\"btn-map-delete\" class=\"flex-1 bg-red-600 hover:bg-red-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">Delete</button>\r\n            `;\r\n\r\n            // Reattach handlers for newly created buttons\r\n            const mapPrintBtn = document.getElementById('btn-map-print');\r\n            if (mapPrintBtn) mapPrintBtn.addEventListener('click', function() { if (selectedRecord && selectedRecord.ulpin) printCard(selectedRecord.ulpin); else showToast('No ULPIN available.', 'error'); });\r\n            const mapDeleteBtn = document.getElementById('btn-map-delete');\r\n            if (mapDeleteBtn) mapDeleteBtn.addEventListener('click', function() { if (selectedRecord) confirmDelete(selectedRecord._id, selectedRecord.khasra_no || ''); else showToast('No record selected.', 'error'); });\r\n            const mapEditBtn2 = document.getElementById('btn-map-edit');\r\n            if (mapEditBtn2) mapEditBtn2.addEventListener('click', function() { if (selectedRecord) editRecord(selectedRecord._id); });\r\n        }\r\n    }\r\n}\r\n\r\nasync function editRecord(recordId) {\r\n    try {\r\n        const record = await fetchRecord(recordId);\r\n        switchMainTab('add-record');\r\n        \r\n        // Wait for tab to show\r\n        await new Promise(resolve => setTimeout(resolve, 200));\r\n\r\n        const { manualOverrideEl } = getFormElements();\r\n        if (manualOverrideEl) {\r\n            manualOverrideEl.checked = false;\r\n        }\r\n        toggleManualLocationOverride(false);\r\n\r\n        document.getElementById('form-record-id').value = record._id;\r\n        document.getElementById('form-title').textContent = 'Edit Record: ' + (record.khasra_no || '');\r\n        document.getElementById('form-khasra').value = record.khasra_no || '';\r\n        document.getElementById('form-khata').value = record.khata_no || '';\r\n        document.getElementById('form-ulpin').value = record.ulpin || '';\r\n        document.getElementById('form-land-use').value = (record.attributes && record.attributes.land_use) || '';\r\n        document.getElementById('form-area').value = (record.attributes && record.attributes.area_ha) || '';\r\n        document.getElementById('form-circle-rate').value = (record.attributes && record.attributes.circle_rate_inr) || 0;\r\n        document.getElementById('form-owner-name').value = (record.owner && record.owner.name) || '';\r\n        document.getElementById('form-share').value = (record.owner && record.owner.share_pct) || 100;\r\n        document.getElementById('form-aadhaar').value = (record.owner && record.owner.aadhaar_mask) || '';\r\n\r\n        const loc = record.location || {};\r\n        setLocationValues(loc.state || '', loc.district || '', loc.village || '');\r\n        setLocationSource('Source: loaded from existing record');\r\n\r\n        const geometry = record.geometry || null;\r\n        if (geometry) {\r\n            // Update geometry metrics\r\n            await updateGeometryMetricsForAddRecord(geometry);\r\n            \r\n            // Clear existing layers on add record map\r\n            if (addRecordDrawnItems) {\r\n                addRecordDrawnItems.clearLayers();\r\n            }\r\n            \r\n            // Add existing geometry to add record map\r\n            const editableLayer = L.geoJSON(geometry, {\r\n                style: {\r\n                    color: '#ea580c',\r\n                    fillColor: '#fed7aa',\r\n                    fillOpacity: 0.4,\r\n                    weight: 3\r\n                }\r\n            });\r\n\r\n            editableLayer.eachLayer(layer => {\r\n                if (addRecordDrawnItems) {\r\n                    addRecordDrawnItems.addLayer(layer);\r\n                }\r\n                currentSketchLayer = layer;\r\n            });\r\n            \r\n            // Enable editing on the layer\r\n            if (addRecordMap) {\r\n                // Fly to the parcel\r\n                const bounds = editableLayer.getBounds();\r\n                addRecordMap.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 });\r\n                \r\n                // Enable edit mode\r\n                setTimeout(() => {\r\n                    editableLayer.eachLayer(layer => {\r\n                        if (layer.pm) {\r\n                            layer.pm.enable();\r\n                        }\r\n                    });\r\n                }, 300);\r\n            }\r\n        }\r\n\r\n        setMutationMode(true);\r\n        document.getElementById('form-submit-btn').textContent = 'Update Record';\r\n\r\n        const drawInstruction = document.getElementById('draw-instruction');\r\n        if (drawInstruction) {\r\n            drawInstruction.style.display = 'none';\r\n        }\r\n\r\n        const drawStatus = document.getElementById('draw-status');\r\n        if (drawStatus) {\r\n            drawStatus.innerHTML = '<strong>Editing existing record.</strong> You can modify the polygon on the map.';\r\n            drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\r\n        }\r\n\r\n        switchFormTab('location');\r\n    } catch (_err) {\r\n        showToast('Failed to load record for editing.', 'error');\r\n    }\r\n}\r\n\r\nasync function capturePolygonMapForPdf(geometry) {\r\n    return new Promise((resolve) => {\r\n        const W = 800, H = 450;\r\n        const wrap = document.createElement('div');\r\n        // Must be in-viewport for tiles to load and html-to-image to work\r\n        // Use opacity near-zero so user never sees it\r\n        wrap.style.cssText = `position:fixed;left:0;top:0;width:${W}px;height:${H}px;z-index:99999;opacity:0.01;pointer-events:none;`;\r\n        document.body.appendChild(wrap);\r\n\r\n        const pdfMap = L.map(wrap, { zoomControl: false, attributionControl: false });\r\n        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(pdfMap);\r\n\r\n        if (geometry && geometry.coordinates && geometry.coordinates[0]) {\r\n            const latlngs = geometry.coordinates[0].map(c => [c[1], c[0]]);\r\n            const poly = L.polygon(latlngs, {\r\n                color: '#dc2626', weight: 3,\r\n                fillColor: '#fca5a5', fillOpacity: 0.35\r\n            }).addTo(pdfMap);\r\n            pdfMap.fitBounds(poly.getBounds(), { padding: [55, 55] });\r\n        } else {\r\n            pdfMap.setView([23.5, 77.5], 5);\r\n        }\r\n\r\n        setTimeout(async () => {\r\n            try {\r\n                // Briefly make fully visible for capture\r\n                wrap.style.opacity = '1';\r\n                const dataUrl = await window.htmlToImage.toPng(wrap, { pixelRatio: 1.5, width: W, height: H });\r\n                resolve(dataUrl);\r\n            } catch (e) {\r\n                console.warn('PDF map capture error:', e);\r\n                resolve(null);\r\n            } finally {\r\n                try { pdfMap.remove(); } catch (_) {}\r\n                try { document.body.removeChild(wrap); } catch (_) {}\r\n            }\r\n        }, 2500);\r\n    });\r\n}\r\n\r\nasync function printCard(ulpin) {\r\n    if (!ulpin) {\r\n        showToast('No ULPIN available for this record.', 'error');\r\n        return;\r\n    }\r\n\r\n    showToast('Generating PDF — capturing map, please wait...', 'info');\r\n\r\n    // Get geometry from selected record or cache\r\n    const record = selectedRecord || (allRecordsCache || []).find(r => r.ulpin === ulpin);\r\n    const geometry = record && record.geometry;\r\n\r\n    let mapImageBase64 = null;\r\n    if (geometry && typeof window.htmlToImage !== 'undefined') {\r\n        mapImageBase64 = await capturePolygonMapForPdf(geometry);\r\n    }\r\n\r\n    try {\r\n        const res = await fetch(getPropertyCardUrl(ulpin), {\r\n            method: 'POST',\r\n            headers: { 'Content-Type': 'application/json', ...FETCH_OPTS.headers },\r\n            body: JSON.stringify({ map_image: mapImageBase64 })\r\n        });\r\n        if (!res.ok) {\r\n            const err = await res.json();\r\n            throw new Error(err.error || 'Failed to generate property card');\r\n        }\r\n        const blob = await res.blob();\r\n        const url = window.URL.createObjectURL(blob);\r\n        downloadFile(url, `Property_Card_${ulpin}.pdf`);\r\n        window.URL.revokeObjectURL(url);\r\n    } catch (err) {\r\n        showToast(err.message, 'error');\r\n    }\r\n}\r\n\r\nfunction confirmDelete(recordId, khasraNo) {\r\n    showConfirmModal(`Are you sure you want to delete record \"${khasraNo}\"?\\n\\nThis action cannot be undone.`, () => {\r\n        deleteRecord(recordId).then(() => {\r\n            showToast(`Record \"${khasraNo}\" deleted successfully.`, 'success');\r\n            selectedRecordId = null;\r\n            selectedRecord = null;\r\n            \r\n            // Hide details panel\r\n            const detailsPanel = document.getElementById('map-details-panel');\r\n            const detailsActions = document.getElementById('map-details-actions');\r\n            if (detailsPanel) detailsPanel.classList.add('hidden');\r\n            if (detailsActions) detailsActions.classList.add('hidden');\r\n            \r\n            loadRecordsOnMap();\r\n            switchMainTab('records');\r\n        }).catch(err => {\r\n            showToast(`Delete failed: ${err.message}`, 'error');\r\n        });\r\n    });\r\n}\r\n\r\n// Initialize Add Record map (split view)\r\nfunction initAddRecordMap() {\r\n    if (!document.getElementById('add-record-map')) return;\r\n\r\n    // Expanded India bounding box with generous margins\r\n    const indiaBounds = L.latLngBounds(\r\n        L.latLng(4.0, 60.0),   // Southwest (extended into ocean)\r\n        L.latLng(40.0, 105.0)  // Northeast (extended into China/Myanmar)\r\n    );\r\n\r\n    addRecordMap = L.map('add-record-map', {\r\n        center: [23.5, 77.5],\r\n        zoom: 7,\r\n        minZoom: 4,\r\n        maxZoom: 18,\r\n        maxBounds: indiaBounds,\r\n        maxBoundsViscosity: 1.0,\r\n        zoomControl: true\r\n    });\r\n    \r\n    // Add basemap layers for add record map\r\n    const addBaseLayers = {};\r\n    addBaseLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\r\n        attribution: '&copy; OpenStreetMap contributors',\r\n        maxZoom: 19\r\n    });\r\n    addBaseLayers.google = L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {\r\n        attribution: '&copy; Google Maps',\r\n        maxZoom: 20\r\n    });\r\n    addBaseLayers.esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {\r\n        attribution: '&copy; Esri',\r\n        maxZoom: 18\r\n    });\r\n    \r\n    addBaseLayers.osm.addTo(addRecordMap);\r\n    let currentAddBaseLayer = 'osm';\r\n    \r\n    // Handle layer switching for add record map\r\n    document.querySelectorAll('input[name=\"add-basemap\"]').forEach(radio => {\r\n        radio.addEventListener('change', function() {\r\n            if (addBaseLayers[this.value] && this.value !== currentAddBaseLayer) {\r\n                addRecordMap.removeLayer(addBaseLayers[currentAddBaseLayer]);\r\n                addBaseLayers[this.value].addTo(addRecordMap);\r\n                currentAddBaseLayer = this.value;\r\n            }\r\n        });\r\n    });\r\n    \r\n    addRecordDrawnItems = new L.FeatureGroup();\r\n    addRecordMap.addLayer(addRecordDrawnItems);\r\n\r\n    // Add India mask to add record map\r\n    addIndiaMask(addRecordMap);\r\n\r\n    // Debug: verify PM is initialized\r\n    console.log('Add Record Map PM initialized:', !!addRecordMap.pm);\r\n    \r\n    // Handle polygon creation on add record map\r\n    addRecordMap.on('pm:create', async function(e) {\r\n        console.log('pm:create fired on add-record-map', e);\r\n        const layer = e.layer;\r\n        addRecordDrawnItems.clearLayers();\r\n        addRecordDrawnItems.addLayer(layer);\r\n        currentSketchLayer = layer;\r\n        \r\n        // Make polygon editable\r\n        if (layer.pm) {\r\n            layer.pm.enable();\r\n        }\r\n        \r\n        const geometry = layer.toGeoJSON().geometry;\r\n        \r\n        // Set geometry IMMEDIATELY\r\n        const geometryInput = document.getElementById('form-geometry');\r\n        if (geometryInput) {\r\n            geometryInput.value = JSON.stringify(geometry);\r\n            console.log('Geometry set to:', geometryInput.value.substring(0, 50) + '...');\r\n        }\r\n        \r\n        await updateGeometryMetricsForAddRecord(geometry);\r\n        \r\n        // Auto-fill location from drawn parcel centroid\r\n        const centroid = L.geoJSON(geometry).getBounds().getCenter();\r\n        lookupLocationForAddRecord(centroid.lat, centroid.lng);\r\n        \r\n        document.getElementById('draw-status').innerHTML = '<strong>Polygon ready!</strong> Location auto-filled. Fill in the details below.';\r\n        document.getElementById('draw-status').className = 'bg-green-50 border-l-4 border-green-500 rounded-r-lg p-3 text-sm text-green-800';\r\n        \r\n        showToast('Polygon drawn and saved. Fill in parcel details and save.', 'success');\r\n    });\r\n    \r\n    // Handle edits to existing polygons\r\n    addRecordMap.on('pm:edit', async function(e) {\r\n        const layer = e.layer;\r\n        if (!layer) return;\r\n        \r\n        currentSketchLayer = layer;\r\n        const geometry = layer.toGeoJSON().geometry;\r\n        await updateGeometryMetricsForAddRecord(geometry);\r\n    });\r\n    \r\n    // Handle removal\r\n    addRecordMap.on('pm:remove', function() {\r\n        document.getElementById('form-geometry').value = '';\r\n        clearMetricsUI();\r\n        document.getElementById('draw-status').innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\r\n        document.getElementById('draw-status').className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\r\n    });\r\n    \r\n    // Auto-fill location on map move\r\n    addRecordMap.on('moveend', function() {\r\n        const center = addRecordMap.getCenter();\r\n        lookupLocationForAddRecord(center.lat, center.lng);\r\n    });\r\n    \r\n    // Add draw button handler - start drawing polygon\r\n    const startDrawBtn = document.getElementById('btn-start-draw-new');\r\n    if (startDrawBtn) {\r\n        startDrawBtn.addEventListener('click', function() {\r\n            // Clear any existing shapes first\r\n            addRecordDrawnItems.clearLayers();\r\n            document.getElementById('form-geometry').value = '';\r\n            clearMetricsUI();\r\n            \r\n            // Start drawing mode\r\n            addRecordMap.pm.enableDraw('Polygon', {\r\n                snappable: true,\r\n                snapDistance: 20,\r\n                continueDrawing: false,\r\n                allowSelfIntersection: false,\r\n                finishOn: 'dblclick'\r\n            });\r\n            \r\n            document.getElementById('draw-status').innerHTML = '<strong>Drawing mode active.</strong> Click to add points. Double-click to finish.';\r\n            document.getElementById('draw-status').className = 'bg-yellow-50 border-l-4 border-yellow-500 rounded-r-lg p-3 text-sm text-yellow-800';\r\n            \r\n            showToast('Click points on the map to draw. Double-click to finish the polygon.', 'info', 5000);\r\n        });\r\n    }\r\n    \r\n    // Finish drawing button\r\n    const finishDrawBtn = document.getElementById('btn-finish-draw-new');\r\n    if (finishDrawBtn) {\r\n        finishDrawBtn.addEventListener('click', function() {\r\n            addRecordMap.pm.disableDraw();\r\n            showToast('Drawing mode closed.', 'info');\r\n        });\r\n    }\r\n    \r\n    // Cancel drawing button\r\n    const cancelDrawBtn = document.getElementById('btn-cancel-draw-new');\r\n    if (cancelDrawBtn) {\r\n        cancelDrawBtn.addEventListener('click', function() {\r\n            addRecordMap.pm.disableDraw();\r\n            addRecordDrawnItems.clearLayers();\r\n            currentSketchLayer = null;\r\n            document.getElementById('form-geometry').value = '';\r\n            clearMetricsUI();\r\n            document.getElementById('draw-status').innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\r\n            document.getElementById('draw-status').className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\r\n            showToast('Drawing cancelled.', 'warning');\r\n        });\r\n    }\r\n    \r\n    // Clear selection button\r\n    const clearDrawBtn = document.getElementById('btn-clear-draw-new');\r\n    if (clearDrawBtn) {\r\n        clearDrawBtn.addEventListener('click', function() {\r\n            addRecordMap.pm.disableDraw();\r\n            addRecordDrawnItems.clearLayers();\r\n            currentSketchLayer = null;\r\n            document.getElementById('form-geometry').value = '';\r\n            clearMetricsUI();\r\n            document.getElementById('draw-status').innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\r\n            document.getElementById('draw-status').className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\r\n            showToast('Drawing cleared.', 'info');\r\n        });\r\n    }\r\n    \r\n    setTimeout(() => addRecordMap.invalidateSize(), 200);\r\n}\r\n\r\n// Lookup location for Add Record map\r\nlet addRecordLookupTimer = null;\r\nfunction lookupLocationForAddRecord(lat, lng) {\r\n    if (typeof lat !== 'number' || typeof lng !== 'number') return;\r\n    \r\n    // Check if manual override is enabled\r\n    const manualOverride = document.getElementById('location-manual-override');\r\n    if (manualOverride && manualOverride.checked) return;\r\n    \r\n    if (addRecordLookupTimer) {\r\n        clearTimeout(addRecordLookupTimer);\r\n    }\r\n\r\n    // Show \"Detecting...\" in the GPS banner\r\n    const gpsBanner = document.getElementById('gps-detected-banner');\r\n    if (gpsBanner) {\r\n        gpsBanner.classList.remove('hidden');\r\n        gpsBanner.innerHTML = `\r\n            <div class=\"flex items-center gap-2\">\r\n                <svg class=\"w-4 h-4 text-green-600 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\"><circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle><path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"></path></svg>\r\n                <span class=\"text-xs font-semibold text-green-700\">Detecting location from GPS coordinates...</span>\r\n            </div>`;\r\n    }\r\n    \r\n    addRecordLookupTimer = setTimeout(async function() {\r\n        try {\r\n            const locationData = await fetchLocationFromCoordinates(lat, lng);\r\n            if (locationData && locationData.state && locationData.district && locationData.village) {\r\n                // Store what GPS detected\r\n                lastGpsDetectedLocation = {\r\n                    state: locationData.state,\r\n                    district: locationData.district,\r\n                    village: locationData.village\r\n                };\r\n\r\n                // Update form dropdowns if not in manual override mode\r\n                const manualOverrideNow = document.getElementById('location-manual-override');\r\n                if (!manualOverrideNow || !manualOverrideNow.checked) {\r\n                    setLocationValues(locationData.state, locationData.district, locationData.village);\r\n                    document.getElementById('form-location-source').textContent = `Source: ${locationData.display_name || 'Map reverse geocoding'}`;\r\n                }\r\n\r\n                // Update GPS banner with confirmed location\r\n                if (gpsBanner) {\r\n                    gpsBanner.innerHTML = `\r\n                        <div class=\"flex items-start gap-2\">\r\n                            <svg class=\"w-4 h-4 text-green-600 flex-shrink-0 mt-0.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\r\n                            <div>\r\n                                <p class=\"text-xs font-bold text-green-800\">GPS Detected Location</p>\r\n                                <p class=\"text-xs text-green-700 mt-0.5\">\r\n                                    <span class=\"font-semibold\">${escapeHtml(locationData.state)}</span> &rsaquo;\r\n                                    <span class=\"font-semibold\">${escapeHtml(locationData.district)}</span> &rsaquo;\r\n                                    ${escapeHtml(locationData.village)}\r\n                                </p>\r\n                            </div>\r\n                        </div>`;\r\n                }\r\n            } else {\r\n                lastGpsDetectedLocation = { state: '', district: '', village: '' };\r\n                if (gpsBanner) gpsBanner.classList.add('hidden');\r\n            }\r\n        } catch (err) {\r\n            console.error('Location lookup failed:', err);\r\n            if (gpsBanner) gpsBanner.classList.add('hidden');\r\n        }\r\n    }, 800);\r\n}\r\n\r\nasync function updateGeometryMetricsForAddRecord(geometry) {\r\n    const geometryInput = document.getElementById('form-geometry');\r\n    \r\n    // Set geometry IMMEDIATELY so form can save\r\n    if (geometryInput) geometryInput.value = JSON.stringify(geometry);\r\n    \r\n    // Now fetch area metrics\r\n    try {\r\n        const result = await calculateArea(geometry);\r\n        \r\n        // API returns: { area: { area_ha, area_acres, ... }, perimeter: {...}, centroid: {...} }\r\n        const areaData = result.area || result;\r\n        const perimeterData = result.perimeter || {};\r\n        const centroidData = result.centroid || {};\r\n        \r\n        const areaInput = document.getElementById('form-area');\r\n        const areaAuto = document.getElementById('area-auto');\r\n        const areaEquivalents = document.getElementById('area-equivalents');\r\n        const geometryMetrics = document.getElementById('geometry-metrics');\r\n        const perimeterEl = document.getElementById('metric-perimeter');\r\n        const centroidEl = document.getElementById('metric-centroid');\r\n        \r\n        if (areaInput) areaInput.value = areaData.area_ha || '';\r\n        if (areaAuto) areaAuto.textContent = `(Auto-calculated)`;\r\n        \r\n        if (areaEquivalents) {\r\n            areaEquivalents.classList.remove('hidden');\r\n            const bigha = areaData.area_bigha_assam || (areaData.area_ha * 7.4752).toFixed(2);\r\n            const lecha = areaData.area_lecha_assam || Math.round(areaData.area_ha * 747.52);\r\n            areaEquivalents.innerHTML = `\r\n                <div><strong>${areaData.area_ha || '?'}</strong> Ha</div>\r\n                <div><strong>${areaData.area_acres || '?'}</strong> Acres</div>\r\n                <div><strong>${areaData.area_guntha || '?'}</strong> Guntha</div>\r\n                <div style=\"color:#ea580c\"><strong>${bigha}</strong> Bigha</div>\r\n                <div style=\"color:#ea580c\"><strong>${lecha}</strong> Lecha</div>\r\n            `;\r\n        }\r\n        \r\n        if (geometryMetrics) {\r\n            geometryMetrics.classList.remove('hidden');\r\n            if (perimeterEl) perimeterEl.textContent = perimeterData.perimeter ? perimeterData.perimeter + ' m' : '--';\r\n            if (centroidEl) centroidEl.textContent = centroidData.lat ? centroidData.lat.toFixed(4) + ', ' + (centroidData.lng || centroidData.lon).toFixed(4) : '--';\r\n        }\r\n    } catch (err) {\r\n        console.error('Failed to calculate area:', err);\r\n        showToast('Area calculation failed but geometry is saved. You can still save the record.', 'warning');\r\n    }\r\n}\r\n\r\nfunction switchSidebarTab(tabName) {\r\n    // Legacy function - redirect to new main tab switching\r\n    switchMainTab(tabName);\r\n}\r\n\r\nfunction switchMainTab(tabName) {\r\n    const dashboardPanel = document.getElementById('main-tab-dashboard');\r\n    const recordsPanel = document.getElementById('main-tab-records');\r\n    const mapPanel = document.getElementById('main-tab-map');\r\n    const addRecordPanel = document.getElementById('main-tab-add-record');\r\n    const viewRecordPanel = document.getElementById('main-tab-view-record');\r\n    const profilePanel = document.getElementById('main-tab-profile');\r\n    const usersPanel = document.getElementById('main-tab-users');\r\n    const reportsPanel = document.getElementById('main-tab-reports');\r\n    const auditPanel = document.getElementById('main-tab-audit');\r\n\r\n    document.querySelectorAll('.main-tab-btn').forEach(tab => {\r\n        const isActive = tab.dataset.tab === tabName;\r\n        tab.classList.toggle('active', isActive);\r\n        if (isActive) tab.classList.add('bg-green-50', 'text-green-700');\r\n        else tab.classList.remove('bg-green-50', 'text-green-700');\r\n    });\r\n\r\n    if (dashboardPanel) dashboardPanel.classList.toggle('hidden', tabName !== 'dashboard');\r\n    if (recordsPanel) recordsPanel.classList.toggle('hidden', tabName !== 'records');\r\n    if (mapPanel) mapPanel.classList.toggle('hidden', tabName !== 'map');\r\n    if (addRecordPanel) addRecordPanel.classList.toggle('hidden', tabName !== 'add-record');\r\n    if (viewRecordPanel) viewRecordPanel.classList.toggle('hidden', tabName !== 'view-record');\r\n    if (profilePanel) profilePanel.classList.toggle('hidden', tabName !== 'profile');\r\n    if (usersPanel) usersPanel.classList.toggle('hidden', tabName !== 'users');\r\n    if (reportsPanel) reportsPanel.classList.toggle('hidden', tabName !== 'reports');\r\n    if (auditPanel) auditPanel.classList.toggle('hidden', tabName !== 'audit');\r\n\r\n    // Show/hide Geoman toolbar based on tab\r\n    const geomanToolbar = document.querySelector('.leaflet-pm-toolbar');\r\n    if (geomanToolbar) {\r\n        // Only show toolbar in Add Record tab (for drawing)\r\n        if (tabName === 'add-record') {\r\n            geomanToolbar.style.display = 'block';\r\n        } else {\r\n            geomanToolbar.style.display = 'none';\r\n        }\r\n    }\r\n\r\n    // Load users when switching to users tab\r\n    if (tabName === 'users') {\r\n        loadUsers();\r\n    }\r\n    \r\n    // Load feedback when switching to reports tab\r\n    if (tabName === 'reports') {\r\n        loadFeedback();\r\n    }\r\n\r\n    // Load audit when switching to audit tab\r\n    if (tabName === 'audit') {\r\n        fetchAudit(100).then(entries => showAuditModal(entries)).catch(err => showToast('Failed to load audit: ' + err.message, 'error'));\r\n    }\r\n\r\n    // Invalidate map size when switching to map tabs\r\n    if (tabName === 'map' && map) {\r\n        setTimeout(() => map.invalidateSize(), 100);\r\n    }\r\n    if (tabName === 'add-record' && addRecordMap) {\r\n        setTimeout(() => addRecordMap.invalidateSize(), 100);\r\n    }\r\n    if (tabName === 'view-record' && viewRecordMap) {\r\n        setTimeout(() => viewRecordMap.invalidateSize(), 100);\r\n    }\r\n}\r\n\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n    console.log('map.js loaded — initializing admin dashboard handlers');\r\n    try {\r\n        // Basic setup\r\n        const refreshBtn = document.getElementById('btn-refresh-feedback');\r\n        if (refreshBtn) {\r\n            refreshBtn.addEventListener('click', loadFeedback);\r\n        }\r\n\r\n        // Check if we're on the admin dashboard\r\n        if (!document.getElementById('map')) return;\r\n\r\n        // --- Prevent back button from exiting to login page ---\r\n        // Push a state when page loads so back button stays within the app\r\n        history.pushState({ page: 'admin', tab: 'records' }, '', window.location.href);\r\n\r\n        // Intercept back button\r\n        window.addEventListener('popstate', function(e) {\r\n            if (e.state && e.state.page === 'admin') {\r\n                history.pushState({ page: 'admin', tab: 'records' }, '', window.location.href);\r\n                switchMainTab('records');\r\n            }\r\n        });\r\n\r\n        initializeFormTabs();\r\n        initializeLocationFilters();\r\n\r\n        initMap(true);\r\n        initializeDrawSettingsPanel();\r\n        initAddRecordMap();\r\n\r\n        // Initialize main tab buttons\r\n        document.querySelectorAll('.main-tab-btn').forEach(tab => {\r\n            tab.addEventListener('click', function() {\r\n                switchMainTab(this.dataset.tab);\r\n            });\r\n        });\r\n        \r\n        // Start with dashboard tab active\r\n        switchMainTab('dashboard');\r\n        \r\n        // Back to records button\r\n        const backBtn = document.getElementById('btn-back-to-records');\r\n        if (backBtn) {\r\n            backBtn.addEventListener('click', function() {\r\n                switchMainTab('records');\r\n            });\r\n        }\r\n\r\n        // (removed redundant auditBtn listener)\r\n\r\n        // (rest of handler unchanged) — reattach the original listeners\r\n        // View record tab action buttons\r\n        const viewEditBtn = document.getElementById('btn-view-edit');\r\n        if (viewEditBtn) {\r\n            viewEditBtn.addEventListener('click', function() {\r\n                if (selectedRecord) editRecord(selectedRecord._id);\r\n            });\r\n        }\r\n        \r\n        const viewPrintBtn = document.getElementById('btn-view-print');\r\n        if (viewPrintBtn) {\r\n            viewPrintBtn.addEventListener('click', function() {\r\n                if (selectedRecord && selectedRecord.ulpin) printCard(selectedRecord.ulpin);\r\n                else showToast('No ULPIN available.', 'error');\r\n            });\r\n        }\r\n        \r\n        const viewDeleteBtn = document.getElementById('btn-view-delete');\r\n        if (viewDeleteBtn) {\r\n            viewDeleteBtn.addEventListener('click', function() {\r\n                if (selectedRecord) confirmDelete(selectedRecord._id, selectedRecord.khasra_no || '');\r\n                else showToast('No record selected.', 'error');\r\n            });\r\n        }\r\n\r\n        // Close map details button\r\n        const closeMapDetails = document.getElementById('close-map-details');\r\n        if (closeMapDetails) {\r\n            closeMapDetails.addEventListener('click', function() {\r\n                const detailsPanel = document.getElementById('map-details-panel');\r\n                if (detailsPanel) detailsPanel.classList.add('hidden');\r\n                const detailsActions = document.getElementById('map-details-actions');\r\n                if (detailsActions) detailsActions.classList.add('hidden');\r\n                selectedRecordId = null;\r\n                selectedRecord = null;\r\n            });\r\n        }\r\n\r\n        // Map details print/delete buttons\r\n        const printBtn = document.getElementById('btn-map-print');\r\n        if (printBtn) {\r\n            printBtn.addEventListener('click', function() {\r\n                if (selectedRecord && selectedRecord.ulpin) printCard(selectedRecord.ulpin);\r\n                else showToast('No ULPIN available.', 'error');\r\n            });\r\n        }\r\n\r\n        const mapDeleteBtn = document.getElementById('btn-map-delete');\r\n        if (mapDeleteBtn) {\r\n            mapDeleteBtn.addEventListener('click', function() {\r\n                if (selectedRecord) confirmDelete(selectedRecord._id, selectedRecord.khasra_no || '');\r\n                else showToast('No record selected.', 'error');\r\n            });\r\n        }\r\n        \r\n        const mapEditBtn = document.getElementById('btn-map-edit');\r\n        if (mapEditBtn) {\r\n            mapEditBtn.addEventListener('click', function() {\r\n                if (selectedRecord) editRecord(selectedRecord._id);\r\n            });\r\n        }\r\n\r\n        document.querySelectorAll('.records-view-tab').forEach(tab => {\r\n            tab.addEventListener('click', function() {\r\n                switchRecordsView(this.dataset.view);\r\n            });\r\n        });\r\n\r\n        const addRecordBtn = document.getElementById('btn-add-record-new');\r\n        if (addRecordBtn) {\r\n            addRecordBtn.addEventListener('click', function() {\r\n                resetForm();\r\n                switchMainTab('add-record');\r\n                switchFormTab('location');\r\n            });\r\n        }\r\n\r\n        const exportBtn = document.getElementById('btn-export-excel');\r\n        if (exportBtn) {\r\n            exportBtn.addEventListener('click', function() {\r\n                const url = getVillageExcelUrl();\r\n                downloadFile(url, 'Village_Ledger.xlsx');\r\n                showToast('Generating Excel ledger...', 'info');\r\n            });\r\n        }\r\n\r\n        const searchInput = document.getElementById('search-input');\r\n        if (searchInput) {\r\n            let searchDebounceTimer = null;\r\n            \r\n            searchInput.addEventListener('input', function() {\r\n                if (searchDebounceTimer) clearTimeout(searchDebounceTimer);\r\n                searchDebounceTimer = setTimeout(() => {\r\n                    applyAdminFilters(false);\r\n                }, 300);\r\n            });\r\n\r\n            searchInput.addEventListener('keypress', function(e) {\r\n                if (e.key === 'Enter') {\r\n                    if (searchDebounceTimer) clearTimeout(searchDebounceTimer);\r\n                    performAdminSearch();\r\n                }\r\n            });\r\n        }\r\n\r\n        const landUseFilter = document.getElementById('land-use-filter');\r\n        if (landUseFilter) {\r\n            landUseFilter.addEventListener('change', function() {\r\n                applyAdminFilters(false);\r\n            });\r\n        }\r\n\r\n        const districtFilter = document.getElementById('district-filter');\r\n        if (districtFilter) {\r\n            districtFilter.addEventListener('change', function() {\r\n                applyAdminFilters(false);\r\n            });\r\n        }\r\n\r\n        const stateFilter = document.getElementById('state-filter');\r\n        if (stateFilter) {\r\n            stateFilter.addEventListener('change', function() {\r\n                applyAdminFilters(false);\r\n            });\r\n        }\r\n\r\n        const clearFiltersBtn = document.getElementById('btn-clear-filters');\r\n        if (clearFiltersBtn) {\r\n            clearFiltersBtn.addEventListener('click', function() {\r\n                if (searchInput) searchInput.value = '';\r\n                if (landUseFilter) landUseFilter.value = '';\r\n                if (districtFilter) districtFilter.value = '';\r\n                if (stateFilter) stateFilter.value = '';\r\n                applyAdminFilters(true);\r\n            });\r\n        }\r\n\r\n        const recordForm = document.getElementById('record-form');\r\n        if (recordForm) {\r\n            recordForm.addEventListener('submit', async function(e) {\r\n                e.preventDefault();\r\n                await handleFormSubmit();\r\n            });\r\n        }\r\n\r\n        const cancelBtn = document.getElementById('form-cancel-btn');\r\n        if (cancelBtn) {\r\n            cancelBtn.addEventListener('click', function() {\r\n                resetForm();\r\n                switchMainTab('records');\r\n            });\r\n        }\r\n\r\n        const logoutBtn = document.getElementById('btn-logout');\r\n        if (logoutBtn) {\r\n            logoutBtn.addEventListener('click', async function() {\r\n                try {\r\n                    await logout();\r\n                } catch (_err) {}\r\n                window.location.href = '/login';\r\n            });\r\n        }\r\n\r\n        // --- Profile Tab Event Handlers ---\r\n        let profileLoaded = false;\r\n        const profileTabBtn = document.querySelector('.main-tab-btn[data-tab=\"profile\"]');\r\n        if (profileTabBtn) {\r\n            profileTabBtn.addEventListener('click', function() {\r\n                if (!profileLoaded) {\r\n                    loadProfile();\r\n                    loadUsers();\r\n                    getSessionUsername();\r\n                    profileLoaded = true;\r\n                }\r\n            });\r\n        }\r\n\r\n        const editProfileBtn = document.getElementById('btn-edit-profile');\r\n        const editProfileBtnInline = document.getElementById('btn-edit-profile-inline');\r\n        function showProfileEditForm() {\r\n            document.getElementById('edit-profile-form').classList.remove('hidden');\r\n            document.getElementById('profile-view').classList.add('hidden');\r\n            if (editProfileBtn) editProfileBtn.classList.add('hidden');\r\n            if (currentProfile) {\r\n                document.getElementById('edit-full-name').value = currentProfile.full_name || '';\r\n                document.getElementById('edit-email').value = currentProfile.email || '';\r\n                document.getElementById('edit-phone').value = currentProfile.phone || '';\r\n                document.getElementById('edit-designation').value = currentProfile.designation || '';\r\n                document.getElementById('edit-department').value = currentProfile.department || '';\r\n                document.getElementById('edit-office').value = currentProfile.office_location || '';\r\n            }\r\n        }\r\n        if (editProfileBtn) editProfileBtn.addEventListener('click', showProfileEditForm);\r\n        if (editProfileBtnInline) editProfileBtnInline.addEventListener('click', showProfileEditForm);\r\n\r\n        const cancelEditProfileBtn = document.getElementById('btn-cancel-edit-profile');\r\n        if (cancelEditProfileBtn) {\r\n            cancelEditProfileBtn.addEventListener('click', function() {\r\n                document.getElementById('edit-profile-form').classList.add('hidden');\r\n                document.getElementById('profile-view').classList.remove('hidden');\r\n                if (editProfileBtn) editProfileBtn.classList.remove('hidden');\r\n            });\r\n        }\r\n\r\n        const profileForm = document.getElementById('profile-form');\r\n        if (profileForm) {\r\n            profileForm.addEventListener('submit', async function(e) {\r\n                e.preventDefault();\r\n                const updateData = {\r\n                    full_name: document.getElementById('edit-full-name').value.trim(),\r\n                    email: document.getElementById('edit-email').value.trim(),\r\n                    phone: document.getElementById('edit-phone').value.trim(),\r\n                    designation: document.getElementById('edit-designation').value.trim(),\r\n                    department: document.getElementById('edit-department').value.trim(),\r\n                    office_location: document.getElementById('edit-office').value.trim()\r\n                };\r\n                const currentPass = document.getElementById('edit-current-password').value;\r\n                const newPass = document.getElementById('edit-new-password').value;\r\n                if (currentPass && newPass) { updateData.current_password = currentPass; updateData.new_password = newPass; }\r\n                try {\r\n                    const res = await fetch(`${API_BASE}/api/profile`, { credentials: 'include', method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updateData) });\r\n                    const data = await res.json(); if (!res.ok) throw new Error(data.error);\r\n                    showToast('Profile updated successfully.', 'success'); currentProfile = data.profile; displayProfile(currentProfile);\r\n                    document.getElementById('edit-profile-form').classList.add('hidden'); document.getElementById('profile-view').classList.remove('hidden'); if (editProfileBtn) editProfileBtn.classList.remove('hidden');\r\n                } catch (err) { showToast(`Failed to update profile: ${err.message}`, 'error'); }\r\n            });\r\n        }\r\n\r\n        const createUserBtn = document.getElementById('btn-create-user');\r\n        if (createUserBtn) createUserBtn.addEventListener('click', function() { document.getElementById('create-user-form').classList.remove('hidden'); });\r\n\r\n        const cancelCreateUserBtn = document.getElementById('btn-cancel-create-user');\r\n        const cancelCreateUserBtn2 = document.getElementById('btn-cancel-create-user-2');\r\n        function hideCreateUserForm() { document.getElementById('create-user-form').classList.add('hidden'); document.getElementById('user-create-form').reset(); }\r\n        if (cancelCreateUserBtn) cancelCreateUserBtn.addEventListener('click', hideCreateUserForm);\r\n        if (cancelCreateUserBtn2) cancelCreateUserBtn2.addEventListener('click', hideCreateUserForm);\r\n\r\n        const userCreateForm = document.getElementById('user-create-form');\r\n        if (userCreateForm) {\r\n            userCreateForm.addEventListener('submit', async function(e) {\r\n                e.preventDefault();\r\n                const userData = { username: document.getElementById('create-username').value.trim(), password: document.getElementById('create-password').value, full_name: document.getElementById('create-full-name').value.trim(), email: document.getElementById('create-email').value.trim(), phone: document.getElementById('create-phone').value.trim(), designation: document.getElementById('create-designation').value.trim(), department: document.getElementById('create-department').value.trim(), role: document.getElementById('create-role').value, office_location: document.getElementById('create-office').value.trim(), is_active: document.getElementById('create-active').checked };\r\n                try {\r\n                    const res = await fetch(`${API_BASE}/api/users`, { credentials: 'include', method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) });\r\n                    const data = await res.json(); if (!res.ok) throw new Error(data.error);\r\n                    showToast(`User \"${userData.username}\" created successfully.`, 'success'); document.getElementById('create-user-form').classList.add('hidden'); userCreateForm.reset(); loadUsers();\r\n                } catch (err) { showToast(`Failed to create user: ${err.message}`, 'error'); }\r\n            });\r\n        }\r\n    } catch (e) {\r\n        console.error('map.js initialization error:', e);\r\n        try { showToast('Client script error — check browser console', 'error'); } catch (_) {}\r\n    }\r\n});\r\n\r\nasync function performAdminSearch() {\r\n    applyAdminFilters(true);\r\n\r\n    if (filteredRecordsCache.length === 1) {\r\n        const record = filteredRecordsCache[0];\r\n        // If on map tab, fly to record\r\n        const mapPanel = document.getElementById('main-tab-map');\r\n        if (mapPanel && !mapPanel.classList.contains('hidden')) {\r\n            flyToRecord(record);\r\n        }\r\n    }\r\n}\r\n\r\nfunction fileToBase64(file) {\r\n    return new Promise((resolve, reject) => {\r\n        const reader = new FileReader();\r\n        reader.readAsDataURL(file);\r\n        reader.onload = () => resolve(reader.result);\r\n        reader.onerror = error => reject(error);\r\n    });\r\n}\r\n\r\nasync function handleFormSubmit() {\r\n    const recordId = document.getElementById('form-record-id').value;\r\n    const geometryStr = document.getElementById('form-geometry').value;\r\n\r\n    \r\n    const locationValues = {\r\n        state: document.getElementById('form-state-manual').value || document.getElementById('form-state').value,\r\n        district: document.getElementById('form-district-manual').value || document.getElementById('form-district').value,\r\n        village: document.getElementById('form-village-manual').value || document.getElementById('form-village').value\r\n    };\r\n\r\n    if (!locationValues.state || !locationValues.district || !locationValues.village) {\r\n        showToast('Location (State, District, Village) is required.', 'error');\r\n        switchFormTab('location');\r\n        return;\r\n    }\r\n\r\n    // GPS Mismatch Warning: Only check if we have GPS data AND manual override is active\r\n    const manualOverrideEl = document.getElementById('location-manual-override');\r\n    const isManualOverride = manualOverrideEl && manualOverrideEl.checked;\r\n    const hasGpsData = lastGpsDetectedLocation.state && lastGpsDetectedLocation.district;\r\n\r\n    if (isManualOverride && hasGpsData) {\r\n        const stateMatch = locationValues.state.trim().toLowerCase() === lastGpsDetectedLocation.state.trim().toLowerCase();\r\n        const districtMatch = locationValues.district.trim().toLowerCase() === lastGpsDetectedLocation.district.trim().toLowerCase();\r\n\r\n        if (!stateMatch || !districtMatch) {\r\n            // Show a blocking mismatch warning modal\r\n            const proceed = await new Promise(resolve => {\r\n                const overlay = document.createElement('div');\r\n                overlay.className = 'fixed inset-0 bg-black bg-opacity-60 z-[9999] flex items-center justify-center p-4';\r\n                overlay.style.backdropFilter = 'blur(3px)';\r\n\r\n                overlay.innerHTML = `\r\n                    <div class=\"bg-white rounded-xl shadow-2xl max-w-md w-full overflow-hidden\">\r\n                        <div class=\"bg-yellow-50 border-b-4 border-yellow-400 px-6 py-4 flex items-center gap-3\">\r\n                            <svg class=\"w-8 h-8 text-yellow-500 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>\r\n                            <h3 class=\"text-lg font-bold text-yellow-800\">Location Mismatch Warning</h3>\r\n                        </div>\r\n                        <div class=\"px-6 py-5\">\r\n                            <p class=\"text-sm text-gray-600 mb-4\">The location you entered does not match the GPS coordinates of the parcel you drew on the map.</p>\r\n                            <div class=\"grid grid-cols-2 gap-3 mb-4\">\r\n                                <div class=\"bg-green-50 rounded-lg p-3 border border-green-200\">\r\n                                    <p class=\"text-xs font-bold text-green-700 mb-1\">📡 GPS Detected</p>\r\n                                    <p class=\"text-sm font-semibold text-green-800\">${escapeHtml(lastGpsDetectedLocation.state)}</p>\r\n                                    <p class=\"text-xs text-green-600\">${escapeHtml(lastGpsDetectedLocation.district)}</p>\r\n                                </div>\r\n                                <div class=\"bg-red-50 rounded-lg p-3 border border-red-200\">\r\n                                    <p class=\"text-xs font-bold text-red-700 mb-1\">✏️ You Entered</p>\r\n                                    <p class=\"text-sm font-semibold text-red-800\">${escapeHtml(locationValues.state)}</p>\r\n                                    <p class=\"text-xs text-red-600\">${escapeHtml(locationValues.district)}</p>\r\n                                </div>\r\n                            </div>\r\n                            <p class=\"text-xs text-gray-500 mb-5\">Are you sure you want to continue with the manually entered location? This may cause incorrect revenue records.</p>\r\n                            <div class=\"flex gap-3\">\r\n                                <button id=\"mismatch-cancel\" class=\"flex-1 px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg text-sm font-medium transition\">← Use GPS Location</button>\r\n                                <button id=\"mismatch-force\" class=\"flex-1 px-4 py-2 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg text-sm font-medium transition\">Force Save Anyway</button>\r\n                            </div>\r\n                        </div>\r\n                    </div>`;\r\n\r\n                document.body.appendChild(overlay);\r\n                overlay.querySelector('#mismatch-cancel').addEventListener('click', () => {\r\n                    // Auto-fill with GPS-detected location\r\n                    const manualOvEl = document.getElementById('location-manual-override');\r\n                    if (manualOvEl) manualOvEl.checked = false;\r\n                    toggleManualLocationOverride(false);\r\n                    setLocationValues(lastGpsDetectedLocation.state, lastGpsDetectedLocation.district, lastGpsDetectedLocation.village);\r\n                    document.body.removeChild(overlay);\r\n                    resolve(false);\r\n                });\r\n                overlay.querySelector('#mismatch-force').addEventListener('click', () => {\r\n                    document.body.removeChild(overlay);\r\n                    resolve(true);\r\n                });\r\n            });\r\n\r\n            if (!proceed) return; // User chose to fix location — abort save\r\n        }\r\n    }\r\n\r\n    // Validate parcel fields\r\n    const khasra = document.getElementById('form-khasra').value.trim();\r\n    const khata = document.getElementById('form-khata').value.trim();\r\n    const landUse = document.getElementById('form-land-use').value;\r\n    \r\n    if (!khasra) {\r\n        showToast('Khasra No. is required.', 'error');\r\n        switchFormTab('parcel');\r\n        return;\r\n    }\r\n    if (!khata) {\r\n        showToast('Khata No. is required.', 'error');\r\n        switchFormTab('parcel');\r\n        return;\r\n    }\r\n    if (!landUse) {\r\n        showToast('Land Use is required.', 'error');\r\n        switchFormTab('parcel');\r\n        return;\r\n    }\r\n\r\n    // Validate geometry\r\n    if (!geometryStr) {\r\n        showToast('Please draw a polygon on the map first.', 'error');\r\n        switchFormTab('parcel');\r\n        return;\r\n    }\r\n\r\n    // Validate owner (only for new records)\r\n    if (!recordId) {\r\n        const ownerName = document.getElementById('form-owner-name').value.trim();\r\n        if (!ownerName) {\r\n            showToast('Owner Name is required.', 'error');\r\n            switchFormTab('owner');\r\n            return;\r\n        }\r\n    }\r\n\r\n    ensureLocationInCatalog(locationValues.state, locationValues.district, locationValues.village);\r\n\r\n    let geometry;\r\n    try {\r\n        geometry = JSON.parse(geometryStr);\r\n    } catch (_err) {\r\n        showToast('Invalid geometry data.', 'error');\r\n        return;\r\n    }\r\n\r\n    const submitBtn = document.getElementById('form-submit-btn');\r\n    const originalText = submitBtn.textContent;\r\n    submitBtn.disabled = true;\r\n    submitBtn.innerHTML = '<span class=\"spinner\"></span> Saving...';\r\n\r\n    try {\r\n        let ownerDocB64 = undefined;\r\n        const ownerDocFile = document.getElementById('form-owner-doc')?.files[0];\r\n        if (ownerDocFile) ownerDocB64 = await fileToBase64(ownerDocFile);\r\n\r\n        let mutationDocB64 = undefined;\r\n        const mutationDocFile = document.getElementById('form-mutation-doc')?.files[0];\r\n        if (mutationDocFile) mutationDocB64 = await fileToBase64(mutationDocFile);\r\n\r\n        if (recordId) {\r\n            const updateData = {\r\n                khasra_no: document.getElementById('form-khasra').value,\r\n                khata_no: document.getElementById('form-khata').value,\r\n                land_use: document.getElementById('form-land-use').value,\r\n                circle_rate_inr: parseFloat(document.getElementById('form-circle-rate').value) || 0,\r\n                share_pct: parseFloat(document.getElementById('form-share').value) || 100,\r\n                aadhaar_mask: document.getElementById('form-aadhaar').value,\r\n                geometry: geometry,\r\n                location: locationValues,\r\n                owner_proof_doc_b64: ownerDocB64\r\n            };\r\n\r\n            const newOwner = document.getElementById('form-new-owner').value.trim();\r\n            if (newOwner) {\r\n                updateData.mutation = true;\r\n                updateData.new_owner_name = newOwner;\r\n                updateData.new_share_pct = parseFloat(document.getElementById('form-new-share').value) || 100;\r\n                updateData.mutation_type = document.getElementById('form-mutation-type').value;\r\n                updateData.mutation_date = document.getElementById('form-mutation-date').value || new Date().toISOString().split('T')[0];\r\n                updateData.mutation_proof_doc_b64 = mutationDocB64;\r\n            }\r\n\r\n            await updateRecord(recordId, updateData);\r\n            showToast('Record updated successfully.', 'success');\r\n        } else {\r\n            const recordData = {\r\n                khasra_no: document.getElementById('form-khasra').value,\r\n                khata_no: document.getElementById('form-khata').value,\r\n                ulpin: document.getElementById('form-ulpin').value || undefined,\r\n                land_use: document.getElementById('form-land-use').value,\r\n                circle_rate_inr: parseFloat(document.getElementById('form-circle-rate').value) || 0,\r\n                owner_name: document.getElementById('form-owner-name').value,\r\n                share_pct: parseFloat(document.getElementById('form-share').value) || 100,\r\n                aadhaar_mask: document.getElementById('form-aadhaar').value || 'XXXX-XXXX-XXXX',\r\n                geometry: geometry,\r\n                location: locationValues,\r\n                owner_proof_doc_b64: ownerDocB64\r\n            };\r\n\r\n            const result = await createRecord(recordData);\r\n            if (result.area_details && result.area_details.area) {\r\n                showToast(`Record created. Area: ${result.area_details.area.area_ha} Ha (${result.area_details.area.area_acres} Acres)`, 'success', 5000);\r\n            } else {\r\n                showToast('Record created successfully.', 'success');\r\n            }\r\n        }\r\n\r\n        resetForm();\r\n        switchMainTab('records');\r\n        loadRecordsOnMap();\r\n    } catch (err) {\r\n        showToast(`Error: ${err.message}`, 'error');\r\n    } finally {\r\n        submitBtn.disabled = false;\r\n        submitBtn.textContent = originalText;\r\n    }\r\n}\r\n\r\nfunction resetForm() {\r\n    const form = document.getElementById('record-form');\r\n    if (form) {\r\n        form.reset();\r\n    }\r\n\r\n    const {\r\n        manualOverrideEl,\r\n        stateManualEl,\r\n        districtManualEl,\r\n        villageManualEl\r\n    } = getFormElements();\r\n\r\n    if (manualOverrideEl) {\r\n        manualOverrideEl.checked = false;\r\n    }\r\n    if (stateManualEl) stateManualEl.value = '';\r\n    if (districtManualEl) districtManualEl.value = '';\r\n    if (villageManualEl) villageManualEl.value = '';\r\n    toggleManualLocationOverride(false);\r\n\r\n    document.getElementById('form-record-id').value = '';\r\n    document.getElementById('form-title').textContent = 'Add New Land Record';\r\n\r\n    setMutationMode(false);\r\n    switchFormTab('location');\r\n\r\n    refreshStateOptions('');\r\n    refreshDistrictOptions('');\r\n    refreshVillageOptions('');\r\n\r\n    lastAutoLocation = { state: '', district: '', village: '' };\r\n    lastGeocodeKey = '';\r\n    updateAutofillStatus('Move map or draw parcel to auto-detect location.', false);\r\n\r\n    document.getElementById('form-submit-btn').textContent = 'Save Record';\r\n\r\n    const drawInstruction = document.getElementById('draw-instruction');\r\n    if (drawInstruction) {\r\n        drawInstruction.style.display = '';\r\n    }\r\n\r\n    // Clear both maps\r\n    clearGeometrySelection(true);\r\n    if (addRecordDrawnItems) {\r\n        addRecordDrawnItems.clearLayers();\r\n    }\r\n    if (addRecordMap) {\r\n        addRecordMap.pm.disableDraw();\r\n    }\r\n    currentSketchLayer = null;\r\n    document.getElementById('form-geometry').value = '';\r\n    clearMetricsUI();\r\n    \r\n    const drawStatus = document.getElementById('draw-status');\r\n    if (drawStatus) {\r\n        drawStatus.innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\r\n        drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\r\n    }\r\n}\r\n\r\n// --- Profile & User Management ---\r\nlet currentProfile = null;\r\nlet allUsers = [];\r\n\r\nasync function loadProfile() {\r\n    try {\r\n        currentProfile = await fetch(`${API_BASE}/api/profile`, { credentials: 'include' }).then(r => r.json());\r\n        displayProfile(currentProfile);\r\n    } catch (err) {\r\n        console.error('Failed to load profile:', err);\r\n        }\r\n}\r\n\r\nfunction displayProfile(profile) {\r\n    // Original profile card\r\n    document.getElementById('profile-display-name').textContent = profile.full_name || 'N/A';\r\n    document.getElementById('profile-display-role').textContent = profile.role || 'N/A';\r\n    document.getElementById('profile-username').textContent = profile.username || 'N/A';\r\n    document.getElementById('profile-email').textContent = profile.email || 'N/A';\r\n    document.getElementById('profile-phone').textContent = profile.phone || 'N/A';\r\n    document.getElementById('profile-designation').textContent = profile.designation || 'N/A';\r\n    document.getElementById('profile-department').textContent = profile.department || 'N/A';\r\n    document.getElementById('profile-office').textContent = profile.office_location || 'N/A';\r\n    document.getElementById('profile-last-login').textContent = profile.last_login ? new Date(profile.last_login).toLocaleString() : 'Never';\r\n\r\n    const badge = document.getElementById('profile-status-badge');\r\n    if (badge) {\r\n        badge.textContent = profile.is_active ? 'Active' : 'Inactive';\r\n        badge.className = `inline-block mt-2 px-3 py-1 text-xs font-semibold rounded-full ${profile.is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`;\r\n    }\r\n\r\n    // New profile view fields\r\n    document.getElementById('profile-view-username').textContent = profile.username || 'N/A';\r\n    document.getElementById('profile-view-name').textContent = profile.full_name || 'N/A';\r\n    document.getElementById('profile-view-email').textContent = profile.email || 'N/A';\r\n    document.getElementById('profile-view-phone').textContent = profile.phone || 'N/A';\r\n    document.getElementById('profile-view-designation').textContent = profile.designation || 'N/A';\r\n    document.getElementById('profile-view-department').textContent = profile.department || 'N/A';\r\n    document.getElementById('profile-view-office').textContent = profile.office_location || 'N/A';\r\n    document.getElementById('profile-view-last-login').textContent = profile.last_login ? new Date(profile.last_login).toLocaleString() : 'Never';\r\n}\r\n\r\nasync function loadFeedback() {\r\n    try {\r\n        const tbody = document.getElementById('feedback-table-body');\r\n        if (tbody) tbody.innerHTML = '<tr><td colspan=\"5\" class=\"px-4 py-4 text-center text-gray-500\">Loading...</td></tr>';\r\n        \r\n        const res = await fetch(`${API_BASE}/api/feedback`, { credentials: 'include' });\r\n        const feedbackList = await res.json();\r\n        \r\n        if (tbody) {\r\n            tbody.innerHTML = '';\r\n            if (feedbackList.length === 0) {\r\n                tbody.innerHTML = '<tr><td colspan=\"5\" class=\"px-4 py-4 text-center text-gray-500\">No feedback forms right now.</td></tr>';\r\n                return;\r\n            }\r\n            \r\n            // Sort by timestamp descending\r\n            feedbackList.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));\r\n            \r\n            feedbackList.forEach(fb => {\r\n                const tr = document.createElement('tr');\r\n                tr.className = 'hover:bg-gray-50 transition cursor-pointer';\r\n                const d = new Date(fb.timestamp);\r\n                const dateStr = d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});\r\n                tr.innerHTML = `\r\n                    <td class=\"px-4 py-3 whitespace-nowrap text-gray-800\">${dateStr}</td>\r\n                    <td class=\"px-4 py-3 text-gray-600 truncate max-w-[150px]\" title=\"${escapeHtml(fb.email)}\">${escapeHtml(fb.email)}</td>\r\n                    <td class=\"px-4 py-3\">\r\n                        <span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800\">\r\n                            ${escapeHtml(fb.type)}\r\n                        </span>\r\n                    </td>\r\n                    <td class=\"px-4 py-3 text-gray-700 min-w-[200px] break-words\">${escapeHtml(fb.message)}</td>\r\n                    <td class=\"px-4 py-3 text-right\">\r\n                        <button onclick=\"deleteFeedback('${fb.id}')\" class=\"text-red-600 hover:text-red-800 p-1 rounded hover:bg-red-50 transition\" title=\"Delete\">\r\n                            <svg class=\"w-4 h-4 inline-block\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/></svg>\r\n                        </button>\r\n                    </td>\r\n                `;\r\n                tbody.appendChild(tr);\r\n            });\r\n        }\r\n    } catch (err) {\r\n        console.error('Failed to load feedback:', err);\r\n    }\r\n}\r\n\r\nfunction deleteFeedback(feedbackId) {\r\n    showConfirmModal(\"Are you sure you want to delete this feedback report?\\n\\nThis action is permanent and cannot be recovered.\", async () => {\r\n        try {\r\n            const res = await fetch(`${API_BASE}/api/feedback/${feedbackId}`, {\r\n                credentials: 'include',\r\n                method: 'DELETE'\r\n            });\r\n            \r\n            const data = await res.json();\r\n            if (!res.ok) throw new Error(data.error);\r\n            \r\n            showToast(\"Feedback deleted successfully.\", \"success\");\r\n            loadFeedback(); // Refresh the table\r\n        } catch (err) {\r\n            showToast(`Failed to delete feedback: ${err.message}`, \"error\");\r\n        }\r\n    });\r\n}\r\n\r\nasync function loadUsers() {\r\n    try {\r\n        if (!sessionUsername) {\r\n            await getSessionUsername();\r\n        }\r\n        allUsers = await fetch(`${API_BASE}/api/users`, { credentials: 'include' }).then(r => r.json());\r\n        renderUsersTable();\r\n    } catch (err) {\r\n        console.error('Failed to load users:', err);\r\n    }\r\n}\r\n\r\nfunction renderUsersTable() {\r\n    const tbody = document.getElementById('users-table-body');\r\n    const noUsers = document.getElementById('no-users');\r\n    if (!tbody) return;\r\n\r\n    if (!allUsers.length) {\r\n        tbody.innerHTML = '';\r\n        noUsers.classList.remove('hidden');\r\n        return;\r\n    }\r\n\r\n    noUsers.classList.add('hidden');\r\n\r\n    const roleColors = {\r\n        SuperAdmin: 'bg-purple-100 text-purple-800',\r\n        Admin: 'bg-orange-100 text-orange-800',\r\n        Officer: 'bg-blue-100 text-blue-800',\r\n        Viewer: 'bg-green-100 text-green-800'\r\n    };\r\n\r\n    const currentUsername = sessionUsername;\r\n\r\n    // Update stats\r\n    document.getElementById('stat-total-users').textContent = allUsers.length;\r\n    document.getElementById('stat-active-users').textContent = allUsers.filter(u => u.is_active).length;\r\n    document.getElementById('stat-admin-users').textContent = allUsers.filter(u => ['Admin', 'SuperAdmin'].includes(u.role)).length;\r\n    document.getElementById('stat-viewer-users').textContent = allUsers.filter(u => u.role === 'Viewer').length;\r\n\r\n    tbody.innerHTML = allUsers.map(user => {\r\n        const isCurrentUser = user.username === currentUsername;\r\n        return `\r\n        <tr class=\"hover:bg-gray-50 transition\">\r\n            <td class=\"px-4 py-3\">\r\n                <div class=\"flex items-center gap-3\">\r\n                    <div class=\"w-8 h-8 bg-gray-200 rounded-full flex items-center justify-center text-gray-600 font-semibold text-xs\">\r\n                        ${(user.full_name || user.username || 'U')[0].toUpperCase()}\r\n                    </div>\r\n                    <div>\r\n                        <div class=\"font-medium text-gray-800\">${escapeHtml(user.full_name || 'N/A')}</div>\r\n                        <div class=\"text-xs text-gray-500\">@${escapeHtml(user.username)}</div>\r\n                    </div>\r\n                </div>\r\n            </td>\r\n            <td class=\"px-4 py-3\">\r\n                <span class=\"px-2.5 py-1 text-xs font-semibold rounded-full ${roleColors[user.role] || 'bg-gray-100 text-gray-800'}\">${escapeHtml(user.role)}</span>\r\n            </td>\r\n            <td class=\"px-4 py-3 text-xs text-gray-600\">\r\n                ${user.email ? `<div>${escapeHtml(user.email)}</div>` : '<div class=\"text-gray-400\">--</div>'}\r\n                ${user.phone ? `<div class=\"mt-0.5\">${escapeHtml(user.phone)}</div>` : ''}\r\n            </td>\r\n            <td class=\"px-4 py-3 text-xs text-gray-600\">\r\n                ${user.department ? `<div class=\"font-medium\">${escapeHtml(user.department)}</div>` : '<div class=\"text-gray-400\">--</div>'}\r\n                ${user.designation ? `<div class=\"text-gray-500\">${escapeHtml(user.designation)}</div>` : ''}\r\n            </td>\r\n            <td class=\"px-4 py-3 text-center\">\r\n                <span class=\"px-2 py-1 text-xs font-semibold rounded-full ${user.is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}\">\r\n                    ${user.is_active ? 'Active' : 'Inactive'}\r\n                </span>\r\n            </td>\r\n            <td class=\"px-4 py-3 text-right\">\r\n                <div class=\"flex items-center justify-end gap-2\">\r\n                    <button onclick=\"editUser('${user.user_id}')\" class=\"text-orange-600 hover:text-orange-800 text-xs font-medium px-2 py-1 rounded hover:bg-orange-50 transition\">\r\n                        Edit\r\n                    </button>\r\n                    ${!isCurrentUser ? `<button onclick=\"deleteUser('${user.user_id}', '${escapeHtml(user.username)}')\" class=\"text-red-600 hover:text-red-800 text-xs font-medium px-2 py-1 rounded hover:bg-red-50 transition\">Delete</button>` : '<span class=\"text-xs text-gray-400\">You</span>'}\r\n                </div>\r\n            </td>\r\n        </tr>\r\n        `;\r\n    }).join('');\r\n}\r\n\r\nlet sessionUsername = '';\r\n\r\nasync function getSessionUsername() {\r\n    try {\r\n        const info = await getSessionInfo();\r\n        sessionUsername = info.username || '';\r\n    } catch (err) {\r\n        sessionUsername = '';\r\n    }\r\n}\r\n\r\nasync function editUser(userId) {\r\n    const user = allUsers.find(u => u.user_id === userId);\r\n    if (!user) return;\r\n\r\n    // Populate the edit modal\r\n    document.getElementById('edit-user-id').value = user.user_id;\r\n    document.getElementById('edit-username').value = user.username;\r\n    document.getElementById('edit-role').value = user.role;\r\n    document.getElementById('edit-fullname').value = user.full_name || '';\r\n    document.getElementById('edit-email').value = user.email || '';\r\n    document.getElementById('edit-phone-user').value = user.phone || '';\r\n    document.getElementById('edit-designation-user').value = user.designation || '';\r\n    document.getElementById('edit-department-user').value = user.department || '';\r\n    document.getElementById('edit-office-user').value = user.office_location || '';\r\n    document.getElementById('edit-is-active').checked = user.is_active !== false;\r\n    document.getElementById('edit-new-password').value = '';\r\n\r\n    // Show the modal\r\n    document.getElementById('edit-user-modal').classList.remove('hidden');\r\n}\r\n\r\n// Close edit user modal\r\nfunction closeEditUserModal() {\r\n    document.getElementById('edit-user-modal').classList.add('hidden');\r\n    document.getElementById('user-edit-form').reset();\r\n}\r\n\r\n// Edit user modal event listeners\r\ndocument.addEventListener('DOMContentLoaded', function() {\r\n    const closeBtn = document.getElementById('btn-close-edit-user');\r\n    const cancelBtn = document.getElementById('btn-cancel-edit-user');\r\n    \r\n    if (closeBtn) closeBtn.addEventListener('click', closeEditUserModal);\r\n    if (cancelBtn) cancelBtn.addEventListener('click', closeEditUserModal);\r\n    \r\n    // Close on backdrop click\r\n    const modal = document.getElementById('edit-user-modal');\r\n    if (modal) {\r\n        modal.addEventListener('click', function(e) {\r\n            if (e.target === modal) {\r\n                closeEditUserModal();\r\n            }\r\n        });\r\n    }\r\n    \r\n    // Submit edit user form\r\n    const editUserForm = document.getElementById('user-edit-form');\r\n    if (editUserForm) {\r\n        editUserForm.addEventListener('submit', async function(e) {\r\n            e.preventDefault();\r\n            \r\n            const userId = document.getElementById('edit-user-id').value;\r\n            const updateData = {\r\n                full_name: document.getElementById('edit-fullname').value.trim(),\r\n                email: document.getElementById('edit-email').value.trim(),\r\n                phone: document.getElementById('edit-phone-user').value.trim(),\r\n                designation: document.getElementById('edit-designation-user').value.trim(),\r\n                department: document.getElementById('edit-department-user').value.trim(),\r\n                office_location: document.getElementById('edit-office-user').value.trim(),\r\n                role: document.getElementById('edit-role').value,\r\n                is_active: document.getElementById('edit-is-active').checked\r\n            };\r\n            \r\n            const newPassword = document.getElementById('edit-new-password').value;\r\n            if (newPassword) {\r\n                updateData.new_password = newPassword;\r\n            }\r\n            \r\n            try {\r\n                const res = await fetch(`${API_BASE}/api/users/${userId}`, {\r\n                    credentials: 'include',\r\n                    method: 'PUT',\r\n                    headers: { 'Content-Type': 'application/json' },\r\n                    body: JSON.stringify(updateData)\r\n                });\r\n                \r\n                const data = await res.json();\r\n                if (!res.ok) throw new Error(data.error);\r\n                \r\n                showToast('User updated successfully.', 'success');\r\n                closeEditUserModal();\r\n                loadUsers();\r\n            } catch (err) {\r\n                showToast(`Failed to update user: ${err.message}`, 'error');\r\n            }\r\n        });\r\n    }\r\n});\r\n\r\nfunction deleteUser(userId, username) {\r\n    showConfirmModal(`Are you sure you want to delete user \"${username}\"?`, async () => {\r\n        try {\r\n            await fetch(`${API_BASE}/api/users/${userId}`, {\r\n                credentials: 'include',\r\n                method: 'DELETE'\r\n            }).then(async r => {\r\n                const data = await r.json();\r\n                if (!r.ok) throw new Error(data.error);\r\n                return data;\r\n            });\r\n            \r\n            showToast(`User \"${username}\" deleted.`, 'success');\r\n            loadUsers();\r\n        } catch (err) {\r\n            showToast(`Failed to delete user: ${err.message}`, 'error');\r\n        }\r\n    });\r\n}\r\n"
  },
  {
    "path": "static/js/modules/admin.js",
    "content": "/**\n * admin.js - Admin UI, User Management, and Audit Logs logic\n */\n\nconst roleColors = {\n    'superadmin': 'bg-red-100 text-red-800',\n    'admin': 'bg-purple-100 text-purple-800',\n    'officer': 'bg-blue-100 text-blue-800',\n    'viewer': 'bg-gray-100 text-gray-800'\n};\n\nfunction showAuditModal(entries) {\n    const container = document.getElementById('audit-entries');\n    if (!container) return;\n\n    if (!Array.isArray(entries)) entries = [];\n    container.__auditEntries = entries.slice();\n    container.__auditPage = 1;\n    container.__auditPageSize = 10;\n\n    function getActionBadge(action) {\n        const a = (action || '').toLowerCase();\n        if (a.includes('delete')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800\">Delete</span>';\n        if (a.includes('create') || a.includes('add')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800\">Create</span>';\n        if (a.includes('update') || a.includes('edit')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800\">Update</span>';\n        if (a.includes('restore')) return '<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800\">Restore</span>';\n        return `<span class=\"inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800\">${escapeHtml(action)}</span>`;\n    }\n\n    function renderAuditList(page = 1, pageSize = 10, filter = {}) {\n        const all = container.__auditEntries || [];\n        let filtered = all.filter(e => {\n            if (filter.q) {\n                const q = filter.q.toLowerCase();\n                const hay = [e.action, e.performed_by, e.user, e.record_id, JSON.stringify(e.details || {})].join(' ').toLowerCase();\n                if (!hay.includes(q)) return false;\n            }\n            if (filter.action && filter.action !== 'all') { if (!String(e.action || '').toLowerCase().includes(filter.action)) return false; }\n            if (filter.user && filter.user !== 'all') { if (!String((e.performed_by||e.user||'')).toLowerCase().includes(filter.user)) return false; }\n            return true;\n        });\n\n        const total = filtered.length;\n        const pages = Math.max(1, Math.ceil(total / pageSize));\n        if (page > pages) page = pages;\n        container.__auditPage = page;\n        container.__auditPageSize = pageSize;\n\n        const start = (page - 1) * pageSize;\n        const pageItems = filtered.slice(start, start + pageSize);\n\n        container.innerHTML = `\n            <div class=\"flex flex-col md:flex-row md:items-center justify-between mb-4 gap-4\">\n                <div class=\"flex flex-wrap items-center gap-3\">\n                    <input id=\"audit-search\" placeholder=\"Search logs...\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm w-64 focus:ring-orange-500 focus:border-orange-500\" value=\"${escapeHtml(filter.q || '')}\" />\n                    <select id=\"audit-action-filter\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\n                        <option value=\"all\">All Actions</option>\n                        <option value=\"create\" ${filter.action === 'create' ? 'selected' : ''}>Create</option>\n                        <option value=\"update\" ${filter.action === 'update' ? 'selected' : ''}>Update</option>\n                        <option value=\"delete\" ${filter.action === 'delete' ? 'selected' : ''}>Delete</option>\n                        <option value=\"restore\" ${filter.action === 'restore' ? 'selected' : ''}>Restore</option>\n                    </select>\n                    <select id=\"audit-user-filter\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\n                        <option value=\"all\">All Users</option>\n                    </select>\n                </div>\n                <div class=\"flex items-center gap-3\">\n                    <div class=\"text-sm font-medium text-gray-600\">${total} results</div>\n                    <select id=\"audit-page-size\" class=\"px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-orange-500 focus:border-orange-500\">\n                        <option value=\"10\" ${pageSize === 10 ? 'selected' : ''}>10 per page</option>\n                        <option value=\"25\" ${pageSize === 25 ? 'selected' : ''}>25 per page</option>\n                        <option value=\"50\" ${pageSize === 50 ? 'selected' : ''}>50 per page</option>\n                    </select>\n                    <button id=\"audit-download\" class=\"px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-orange-500 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg> Export\n                    </button>\n                </div>\n            </div>\n            <div class=\"overflow-x-auto bg-white border border-gray-200 rounded-lg shadow-sm\">\n                <table class=\"min-w-full divide-y divide-gray-200\">\n                    <thead class=\"bg-gray-50\">\n                        <tr>\n                            <th class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Timestamp</th>\n                            <th class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Action</th>\n                            <th class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">User</th>\n                            <th class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Record ID</th>\n                            <th class=\"px-6 py-3 text-left text-xs font-bold text-gray-500 uppercase tracking-wider\">Details</th>\n                        </tr>\n                    </thead>\n                    <tbody id=\"audit-list\" class=\"bg-white divide-y divide-gray-200\"></tbody>\n                </table>\n            </div>\n            <div id=\"audit-pager\" class=\"mt-4 flex items-center justify-between text-sm text-gray-700 bg-white p-3 rounded-lg border border-gray-200 shadow-sm\"></div>\n        `;\n\n        const users = Array.from(new Set(all.map(a => (a.performed_by||a.user||'').toLowerCase()).filter(Boolean))).sort();\n        const userFilter = document.getElementById('audit-user-filter');\n        users.forEach(u => { const opt = document.createElement('option'); opt.value = u; opt.textContent = u; if (filter.user === u) opt.selected = true; userFilter.appendChild(opt); });\n\n        const listEl = document.getElementById('audit-list');\n        if (pageItems.length === 0) {\n            listEl.innerHTML = '<tr><td colspan=\"5\" class=\"px-6 py-4 text-center text-sm text-gray-500\">No logs found.</td></tr>';\n        } else {\n            pageItems.forEach(e => {\n                const ts = e.timestamp || e.time || e.created_at || '';\n                const action = e.action || 'unknown';\n                const by = e.performed_by || e.user || 'system';\n                const rid = e.record_id || '-';\n                let detailsStr = '';\n                if (e.details) {\n                    if (typeof e.details === 'string') detailsStr = e.details;\n                    else {\n                        const parts = [];\n                        if (e.details.khasra_no) parts.push(`Khasra: ${e.details.khasra_no}`);\n                        if (e.details.ulpin) parts.push(`ULPIN: ${e.details.ulpin}`);\n                        if (e.details.changes) parts.push(`${Object.keys(e.details.changes).length} field(s) changed`);\n                        detailsStr = parts.length > 0 ? parts.join(' | ') : JSON.stringify(e.details).slice(0, 100);\n                    }\n                }\n                const tr = document.createElement('tr');\n                tr.className = 'hover:bg-gray-50 transition-colors';\n                tr.innerHTML = `<td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500\">${new Date(ts).toLocaleString()}</td><td class=\"px-6 py-4 whitespace-nowrap text-sm\">${getActionBadge(action)}</td><td class=\"px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900\">${escapeHtml(by)}</td><td class=\"px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono\">${escapeHtml(rid)}</td><td class=\"px-6 py-4 text-sm text-gray-600 max-w-md\"><div class=\"truncate cursor-pointer hover:text-gray-900\" title=\"${escapeHtml(JSON.stringify(e.details, null, 2))}\">${escapeHtml(detailsStr || 'No details')}</div></td>`;\n                listEl.appendChild(tr);\n            });\n        }\n\n        const pager = document.getElementById('audit-pager');\n        if (pager) {\n            pager.innerHTML = `\n                <div class=\"font-medium text-gray-500\">Page ${page} of ${pages}</div>\n                <div class=\"flex gap-2\">\n                    <button id=\"audit-prev\" class=\"px-3 py-1.5 bg-white border border-gray-300 rounded-md text-sm font-medium text-gray-700 ${page <= 1 ? 'opacity-50' : 'hover:bg-gray-50'}\" ${page <= 1 ? 'disabled' : ''}>Previous</button>\n                    <button id=\"audit-next\" class=\"px-3 py-1.5 bg-white border border-gray-300 rounded-md text-sm font-medium text-gray-700 ${page >= pages ? 'opacity-50' : 'hover:bg-gray-50'}\" ${page >= pages ? 'disabled' : ''}>Next</button>\n                </div>\n            `;\n        }\n\n        const getFilters = () => ({\n            q: document.getElementById('audit-search').value,\n            action: document.getElementById('audit-action-filter').value,\n            user: document.getElementById('audit-user-filter').value\n        });\n\n        // Event Listeners (ensure we don't stack them)\n        const refresh = () => renderAuditList(1, container.__auditPageSize, getFilters());\n        \n        document.getElementById('audit-search').oninput = debounce(refresh, 300);\n        document.getElementById('audit-action-filter').onchange = refresh;\n        document.getElementById('audit-user-filter').onchange = refresh;\n        \n        document.getElementById('audit-page-size').onchange = (e) => {\n            renderAuditList(1, parseInt(e.target.value, 10), getFilters());\n        };\n        \n        document.getElementById('audit-prev').onclick = () => {\n            if (container.__auditPage > 1) renderAuditList(container.__auditPage - 1, container.__auditPageSize, getFilters());\n        };\n        \n        document.getElementById('audit-next').onclick = () => {\n            if (container.__auditPage < pages) renderAuditList(container.__auditPage + 1, container.__auditPageSize, getFilters());\n        };\n        \n        document.getElementById('audit-download').onclick = () => {\n            const dataToExport = filtered.length > 0 ? filtered : all;\n            const blob = new Blob([JSON.stringify(dataToExport, null, 2)], { type: 'application/json' });\n            const url = URL.createObjectURL(blob);\n            const a = document.createElement('a');\n            a.href = url;\n            a.download = `audit_logs_${new Date().toISOString().split('T')[0]}.json`;\n            a.click();\n            URL.revokeObjectURL(url);\n            showToast('Audit logs exported.', 'success');\n        };\n    }\n\n    renderAuditList(1, 10, {});\n}\n\nfunction showAdminDetails(record) {\n    selectedRecordId = record._id;\n    selectedRecord = record;\n    const detailsPanel = document.getElementById('map-details-panel');\n    const detailsActions = document.getElementById('map-details-actions');\n    const content = document.getElementById('map-details-content');\n    if (detailsPanel) detailsPanel.classList.remove('hidden');\n    if (detailsActions) detailsActions.classList.remove('hidden');\n    if (!content) return;\n\n    const loc = record.location || {};\n    const attrs = record.attributes || {};\n    const owner = record.owner || {};\n    const mutations = record.mutation_history || [];\n\n    content.innerHTML = `\n        <div class=\"fade-in\">\n            <div class=\"flex items-center justify-between mb-3\">\n                <div class=\"flex items-center gap-2\"><h3 class=\"text-sm font-bold text-inherit\">${record.khasra_no || 'N/A'}</h3>${record.deleted ? `<span class=\"badge-deleted\">Deleted</span>` : ''}</div>\n                <span class=\"land-use-badge badge-${(attrs.land_use || '').toLowerCase()}\">${attrs.land_use || 'N/A'}</span>\n            </div>\n            <div class=\"space-y-2 text-xs text-inherit\">\n                <div class=\"grid grid-cols-2 gap-x-3 gap-y-1 opacity-80\">\n                    <span class=\"opacity-60\">Role</span><span class=\"font-bold\">${(record.role || 'Officer').toUpperCase()}</span>\n                    <span class=\"opacity-60\">ULPIN</span><span class=\"font-mono\">${record.ulpin || 'N/A'}</span>\n                    <span class=\"opacity-60\">Area</span><span>${attrs.area_ha || 'N/A'} Ha</span>\n                    <span class=\"opacity-60\">Village</span><span>${loc.village || 'N/A'}</span>\n                    <span class=\"opacity-60\">District</span><span>${loc.district || 'N/A'}</span>\n                </div>\n                <hr class=\"my-2 opacity-10\">\n                <p class=\"text-[10px] font-bold opacity-40 uppercase\">OWNER</p>\n                <div class=\"grid grid-cols-2 gap-x-3 gap-y-1\">\n                    <span class=\"opacity-60\">Name</span><span class=\"font-medium\">${owner.name || 'N/A'}</span>\n                    <span class=\"opacity-60\">Share</span><span>${owner.share_pct || 'N/A'}%</span>\n                </div>\n            </div>\n            <div class=\"mt-4\"><button id=\"btn-map-edit-init\" class=\"w-full bg-orange-600 hover:bg-orange-700 text-white text-xs py-2 rounded-lg transition\">Edit / Mutate</button></div>\n        </div>\n    `;\n    \n    document.getElementById('btn-map-edit-init').addEventListener('click', () => editRecord(record._id));\n\n    if (detailsActions) {\n        if (record.deleted) {\n            detailsActions.innerHTML = `<button id=\"btn-map-restore\" class=\"flex-1 bg-green-600 hover:bg-green-700 text-white text-xs py-2 rounded-lg transition\">Restore</button><button id=\"btn-map-hard-delete\" class=\"flex-1 bg-red-600 hover:bg-red-700 text-white text-xs py-2 rounded-lg transition\">Hard Delete</button>`;\n            document.getElementById('btn-map-restore').addEventListener('click', () => {\n                showConfirmModal(`Restore record?`, async () => {\n                    await restoreRecord(record._id); loadRecordsOnMap(); detailsPanel.classList.add('hidden');\n                });\n            });\n            document.getElementById('btn-map-hard-delete').addEventListener('click', () => {\n                showConfirmModal(`Permanently delete?`, async () => {\n                    await deleteRecord(record._id); loadRecordsOnMap(); detailsPanel.classList.add('hidden');\n                });\n            });\n        } else {\n            detailsActions.innerHTML = `<button id=\"btn-map-print\" class=\"flex-1 bg-blue-600 hover:bg-blue-700 text-white text-xs py-2 rounded-lg transition\">Print</button><button id=\"btn-map-delete\" class=\"flex-1 bg-red-600 hover:bg-red-700 text-white text-xs py-2 rounded-lg transition\">Delete</button>`;\n            document.getElementById('btn-map-print').addEventListener('click', () => printCard(record.ulpin));\n            document.getElementById('btn-map-delete').addEventListener('click', () => confirmDelete(record._id, record.khasra_no));\n        }\n    }\n}\n\nasync function capturePolygonMapForPdf(geometry) {\n    return new Promise((resolve) => {\n        const W = 800, H = 450;\n        const wrap = document.createElement('div');\n        wrap.style.cssText = `position:fixed;left:0;top:0;width:${W}px;height:${H}px;z-index:99999;opacity:0.01;pointer-events:none;`;\n        document.body.appendChild(wrap);\n        const pdfMap = L.map(wrap, { zoomControl: false, attributionControl: false });\n        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(pdfMap);\n        if (geometry && geometry.coordinates && geometry.coordinates[0]) {\n            const poly = L.polygon(geometry.coordinates[0].map(c => [c[1], c[0]]), { color: '#dc2626', weight: 3, fillColor: '#fca5a5', fillOpacity: 0.35 }).addTo(pdfMap);\n            pdfMap.fitBounds(poly.getBounds(), { padding: [55, 55] });\n        } else pdfMap.setView([23.5, 77.5], 5);\n\n        setTimeout(async () => {\n            try {\n                wrap.style.opacity = '1';\n                resolve(await window.htmlToImage.toPng(wrap, { pixelRatio: 1.5, width: W, height: H }));\n            } catch (e) { resolve(null); }\n            finally { pdfMap.remove(); document.body.removeChild(wrap); }\n        }, 2500);\n    });\n}\n\nasync function printCard(ulpin) {\n    if (!ulpin) return showToast('No ULPIN available.', 'error');\n    showToast('Generating PDF — capturing map...', 'info');\n    const record = selectedRecord || (allRecordsCache || []).find(r => r.ulpin === ulpin);\n    const mapImageBase64 = record && record.geometry ? await capturePolygonMapForPdf(record.geometry) : null;\n    try {\n        const res = await fetch(getPropertyCardUrl(ulpin), { method: 'POST', headers: { 'Content-Type': 'application/json', ...FETCH_OPTS.headers }, body: JSON.stringify({ map_image: mapImageBase64 }) });\n        const blob = await res.blob();\n        const url = window.URL.createObjectURL(blob);\n        downloadFile(url, `Property_Card_${ulpin}.pdf`);\n        window.URL.revokeObjectURL(url);\n    } catch (err) { showToast(err.message, 'error'); }\n}\n\nfunction confirmDelete(recordId, khasraNo) {\n    showConfirmModal(`Delete record \"${khasraNo}\"?`, () => {\n        deleteRecord(recordId).then(() => {\n            showToast(`Deleted.`, 'success');\n            loadRecordsOnMap(); switchMainTab('records');\n            document.getElementById('map-details-panel').classList.add('hidden');\n        }).catch(err => showToast(`Delete failed: ${err.message}`, 'error'));\n    });\n}\n\nasync function loadProfile() {\n    try {\n        currentProfile = await fetch(`${API_BASE}/api/profile`, { credentials: 'include' }).then(r => r.json());\n        displayProfile(currentProfile);\n    } catch (err) {\n        console.error('Failed to load profile:', err);\n    }\n}\n\nfunction displayProfile(profile) {\n    // Original profile card\n    const profileName = document.getElementById('profile-display-name');\n    const profileRole = document.getElementById('profile-display-role');\n    const profileUser = document.getElementById('profile-username');\n    const profileEmail = document.getElementById('profile-email');\n    const profilePhone = document.getElementById('profile-phone');\n    const profileDesig = document.getElementById('profile-designation');\n    const profileDept = document.getElementById('profile-department');\n    const profileOffice = document.getElementById('profile-office');\n    const profileLastLogin = document.getElementById('profile-last-login');\n\n    if (profileName) profileName.textContent = profile.full_name || 'N/A';\n    if (profileRole) profileRole.textContent = profile.role || 'N/A';\n    if (profileUser) profileUser.textContent = profile.username || 'N/A';\n    if (profileEmail) profileEmail.textContent = profile.email || 'N/A';\n    if (profilePhone) profilePhone.textContent = profile.phone || 'N/A';\n    if (profileDesig) profileDesig.textContent = profile.designation || 'N/A';\n    if (profileDept) profileDept.textContent = profile.department || 'N/A';\n    if (profileOffice) profileOffice.textContent = profile.office_location || 'N/A';\n    if (profileLastLogin) profileLastLogin.textContent = profile.last_login ? new Date(profile.last_login).toLocaleString() : 'Never';\n\n    const badge = document.getElementById('profile-status-badge');\n    if (badge) {\n        badge.textContent = profile.is_active ? 'Active' : 'Inactive';\n        badge.className = `inline-block mt-2 px-3 py-1 text-xs font-semibold rounded-full ${profile.is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}`;\n    }\n\n    // Update Top Navbar Badge\n    const navRoleBadge = document.querySelector('nav .bg-white\\\\/30') || document.querySelector('.nav-role-badge');\n    if (navRoleBadge) {\n        const displayRole = (profile.role || 'Admin').toUpperCase();\n        navRoleBadge.textContent = displayRole;\n        if (displayRole === 'SUPERADMIN') {\n            navRoleBadge.className = 'text-[10px] sm:text-xs bg-red-500 text-white px-2 py-0.5 rounded-full font-bold animate-pulse';\n        } else {\n            navRoleBadge.className = 'text-[10px] sm:text-xs bg-white/30 text-white px-2 py-0.5 rounded-full font-semibold';\n        }\n    }\n\n    // Role-based visibility for sidebar tabs\n    const role = (profile.role || '').toLowerCase();\n    const isFullAdmin = (role === 'admin' || role === 'superadmin');\n    isAdmin = isFullAdmin; // Update global state\n    \n    const usersTabBtn = document.getElementById('btn-users');\n    const auditTabBtn = document.getElementById('btn-audit');\n    \n    if (usersTabBtn) {\n        usersTabBtn.classList.toggle('hidden', !isFullAdmin);\n    }\n    if (auditTabBtn) {\n        auditTabBtn.classList.toggle('hidden', !isFullAdmin);\n    }\n\n    // New profile view fields\n    const viewUser = document.getElementById('profile-view-username');\n    const viewName = document.getElementById('profile-view-name');\n    const viewEmail = document.getElementById('profile-view-email');\n    const viewPhone = document.getElementById('profile-view-phone');\n    const viewDesig = document.getElementById('profile-view-designation');\n    const viewDept = document.getElementById('profile-view-department');\n    const viewOffice = document.getElementById('profile-view-office');\n    const viewLastLogin = document.getElementById('profile-view-last-login');\n\n    if (viewUser) viewUser.textContent = profile.username || 'N/A';\n    if (viewName) viewName.textContent = profile.full_name || 'N/A';\n    if (viewEmail) viewEmail.textContent = profile.email || 'N/A';\n    if (viewPhone) viewPhone.textContent = profile.phone || 'N/A';\n    if (viewDesig) viewDesig.textContent = profile.designation || 'N/A';\n    if (viewDept) viewDept.textContent = profile.department || 'N/A';\n    if (viewOffice) viewOffice.textContent = profile.office_location || 'N/A';\n    if (viewLastLogin) viewLastLogin.textContent = profile.last_login ? new Date(profile.last_login).toLocaleString() : 'Never';\n\n    // Toggle Recovery Containers based on status\n    const createRec = document.getElementById('create-recovery-container');\n    const editRec = document.getElementById('edit-recovery-container');\n    if (profile.is_recovery) {\n        if (createRec) createRec.classList.remove('hidden');\n        if (editRec) editRec.classList.remove('hidden');\n    } else {\n        if (createRec) createRec.classList.add('hidden');\n        if (editRec) editRec.classList.add('hidden');\n    }\n}\n\n// deleteFeedback is defined as a global function later in this file\n\nwindow.toggleFeedbackStatus = async (id, currentStatus) => {\n    const newStatus = currentStatus === 'New' ? 'Reviewed' : 'New';\n    try {\n        const res = await fetch(`${API_BASE}/api/feedback/${id}/status`, {\n            method: 'PUT',\n            headers: { 'Content-Type': 'application/json' },\n            body: JSON.stringify({ status: newStatus }),\n            credentials: 'include'\n        });\n        if (!res.ok) throw new Error('Update failed');\n        showToast(`Feedback marked as ${newStatus}.`, 'success');\n        loadFeedback();\n    } catch (err) { showToast(err.message, 'error'); }\n};\n\nasync function loadFeedback() {\n    try {\n        const tbody = document.getElementById('feedback-table-body');\n        if (!tbody) return;\n        tbody.innerHTML = '<tr><td colspan=\"5\" class=\"px-4 py-4 text-center text-gray-500\">Loading...</td></tr>';\n        \n        const res = await fetch(`${API_BASE}/api/feedback`, { credentials: 'include' });\n        const feedbackList = await res.json();\n        \n        tbody.innerHTML = '';\n        if (feedbackList.length === 0) {\n            tbody.innerHTML = '<tr><td colspan=\"5\" class=\"px-4 py-4 text-center text-gray-500\">No feedback submissions found.</td></tr>';\n            return;\n        }\n        \n        // Sort by timestamp descending\n        feedbackList.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));\n        \n        feedbackList.forEach(fb => {\n            const tr = document.createElement('tr');\n            tr.className = 'hover:bg-gray-50 transition';\n            const d = new Date(fb.timestamp);\n            const dateStr = d.toLocaleDateString() + ' ' + d.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});\n            const isNew = fb.status === 'New';\n            const statusClass = isNew ? 'bg-blue-100 text-blue-800' : 'bg-gray-100 text-gray-800';\n            \n            tr.innerHTML = `\n                <td class=\"px-4 py-3 whitespace-nowrap text-gray-800 text-xs\">${dateStr}</td>\n                <td class=\"px-4 py-3\">\n                    <div class=\"text-sm font-medium text-gray-900\">${escapeHtml(fb.email)}</div>\n                    <span class=\"inline-flex items-center px-2 py-0.5 rounded text-[10px] font-medium bg-orange-100 text-orange-800\">\n                        ${escapeHtml(fb.type)}\n                    </span>\n                </td>\n                <td class=\"px-4 py-3 text-gray-700 min-w-[200px] break-words text-sm\">${escapeHtml(fb.message)}</td>\n                <td class=\"px-4 py-3\">\n                    <span class=\"inline-flex items-center px-2 py-0.5 rounded-full text-[10px] font-bold ${statusClass}\">\n                        ${fb.status || 'New'}\n                    </span>\n                </td>\n                <td class=\"px-4 py-3 text-right\">\n                    <div class=\"flex items-center justify-end gap-2\">\n                        <button onclick=\"toggleFeedbackStatus('${fb.id}', '${fb.status || 'New'}')\" class=\"${isNew ? 'text-blue-600 hover:text-blue-900' : 'text-gray-400 hover:text-gray-600'}\" title=\"${isNew ? 'Mark as Reviewed' : 'Mark as New'}\">\n                            <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>\n                        </button>\n                        <button onclick=\"deleteFeedback('${fb.id}')\" class=\"text-red-600 hover:text-red-800 p-1 rounded hover:bg-red-50 transition\" title=\"Delete\">\n                            <svg class=\"w-4 h-4 inline-block\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/></svg>\n                        </button>\n                    </div>\n                </td>\n            `;\n            tbody.appendChild(tr);\n        });\n    } catch (err) {\n        console.error('Failed to load feedback:', err);\n    }\n}\n\nwindow.deleteFeedback = function(feedbackId) {\n    showConfirmModal(\"Are you sure you want to delete this feedback report?\\n\\nThis action is permanent and cannot be recovered.\", async () => {\n        try {\n            const res = await fetch(`${API_BASE}/api/feedback/${feedbackId}`, {\n                credentials: 'include',\n                method: 'DELETE'\n            });\n            \n            const data = await res.json();\n            if (!res.ok) throw new Error(data.error);\n            \n            showToast(\"Feedback deleted successfully.\", \"success\");\n            loadFeedback(); // Refresh the table\n        } catch (err) {\n            showToast(`Failed to delete feedback: ${err.message}`, \"error\");\n        }\n    });\n}\n\nasync function loadUsers() {\n    try {\n        if (!sessionUsername) {\n            await getSessionUsername();\n        }\n        allUsers = await fetch(`${API_BASE}/api/users`, { credentials: 'include' }).then(r => r.json());\n        renderUsersTable();\n    } catch (err) {\n        console.error('Failed to load users:', err);\n    }\n}\n\nfunction renderUsersTable() {\n    const tbody = document.getElementById('users-table-body');\n    const noUsers = document.getElementById('no-users');\n    if (!tbody) return;\n\n    if (!allUsers.length) {\n        tbody.innerHTML = '';\n        if (noUsers) noUsers.classList.remove('hidden');\n        return;\n    }\n\n    if (noUsers) noUsers.classList.add('hidden');\n\n    const roleColors = {\n        superadmin: 'bg-purple-100 text-purple-800',\n        admin: 'bg-orange-100 text-orange-800',\n        officer: 'bg-blue-100 text-blue-800',\n        viewer: 'bg-green-100 text-green-800'\n    };\n\n    const currentUsername = sessionUsername;\n\n    // Update stats\n    const totalUsersEl = document.getElementById('stat-total-users');\n    const activeUsersEl = document.getElementById('stat-active-users');\n    const adminUsersEl = document.getElementById('stat-admin-users');\n    const viewerUsersEl = document.getElementById('stat-viewer-users');\n\n    if (totalUsersEl) totalUsersEl.textContent = allUsers.length;\n    if (activeUsersEl) activeUsersEl.textContent = allUsers.filter(u => u.is_active).length;\n    if (adminUsersEl) adminUsersEl.textContent = allUsers.filter(u => ['Admin', 'SuperAdmin'].includes(u.role)).length;\n    if (viewerUsersEl) viewerUsersEl.textContent = allUsers.filter(u => u.role === 'Viewer').length;\n\n    tbody.innerHTML = allUsers.map(user => {\n        const isCurrentUser = user.username === currentUsername;\n        const roleKey = (user.role || '').toLowerCase();\n        const colorClass = roleColors[roleKey] || 'bg-gray-100 text-gray-800';\n        return `\n        <tr class=\"hover:bg-gray-50 transition\">\n            <td class=\"px-4 py-3\">\n                <div class=\"flex items-center gap-3\">\n                    <div class=\"w-8 h-8 bg-gray-200 rounded-full flex items-center justify-center text-gray-600 font-semibold text-xs\">\n                        ${(user.full_name || user.username || 'U')[0].toUpperCase()}\n                    </div>\n                    <div>\n                        <div class=\"font-medium text-gray-800\">${escapeHtml(user.full_name || 'N/A')}</div>\n                        <div class=\"text-xs text-gray-500\">@${escapeHtml(user.username)}</div>\n                    </div>\n                </div>\n            </td>\n            <td class=\"px-4 py-3\">\n                <span class=\"px-2.5 py-1 text-xs font-semibold rounded-full ${colorClass}\">${escapeHtml(user.role)}</span>\n            </td>\n            <td class=\"px-4 py-3 text-xs text-gray-600\">\n                ${user.email ? `<div>${escapeHtml(user.email)}</div>` : '<div class=\"text-gray-400\">--</div>'}\n                ${user.phone ? `<div class=\"mt-0.5\">${escapeHtml(user.phone)}</div>` : ''}\n            </td>\n            <td class=\"px-4 py-3 text-xs text-gray-600\">\n                ${user.department ? `<div class=\"font-medium\">${escapeHtml(user.department)}</div>` : '<div class=\"text-gray-400\">--</div>'}\n                ${user.designation ? `<div class=\"text-gray-500\">${escapeHtml(user.designation)}</div>` : ''}\n            </td>\n            <td class=\"px-4 py-3 text-center\">\n                <span class=\"px-2 py-1 text-xs font-semibold rounded-full ${user.is_active ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}\">\n                    ${user.is_active ? 'Active' : 'Inactive'}\n                </span>\n            </td>\n            <td class=\"px-4 py-3 text-right\">\n                <div class=\"flex items-center justify-end gap-2\">\n                    <button onclick=\"editUser('${user.user_id}')\" class=\"text-orange-600 hover:text-orange-800 text-xs font-medium px-2 py-1 rounded hover:bg-orange-50 transition\">\n                        Edit\n                    </button>\n                    ${!isCurrentUser ? `<button onclick=\"deleteUser('${user.user_id}', '${escapeHtml(user.username)}')\" class=\"text-red-600 hover:text-red-800 text-xs font-medium px-2 py-1 rounded hover:bg-red-50 transition\">Delete</button>` : '<span class=\"text-xs text-gray-400\">You</span>'}\n                </div>\n            </td>\n        </tr>\n        `;\n    }).join('');\n}\n\nasync function getSessionUsername() {\n    try {\n        const info = await getSessionInfo();\n        sessionUsername = info.username || '';\n    } catch (err) {\n        sessionUsername = '';\n    }\n}\n\nasync function editUser(userId) {\n    const user = allUsers.find(u => u.user_id === userId);\n    if (!user) return;\n\n    // Populate the edit modal\n    const editId = document.getElementById('edit-user-id');\n    const editUser = document.getElementById('edit-username');\n    const editRole = document.getElementById('edit-role');\n    const editFullname = document.getElementById('edit-fullname');\n    const editEmail = document.getElementById('edit-email');\n    const editPhone = document.getElementById('edit-phone-user');\n    const editDesig = document.getElementById('edit-designation-user');\n    const editDept = document.getElementById('edit-department-user');\n    const editOffice = document.getElementById('edit-office-user');\n    const editActive = document.getElementById('edit-is-active');\n    const editPass = document.getElementById('edit-new-password');\n\n    if (editId) editId.value = user.user_id;\n    if (editUser) editUser.value = user.username;\n    \n    // Dynamic Role Filtering for Edit Modal\n    if (editRole) {\n        const myRole = (currentProfile.role || '').toLowerCase();\n        const targetRole = (user.role || '').toLowerCase();\n        \n        // Populate options based on hierarchy\n        let roles = ['Officer', 'Viewer'];\n        if (myRole === 'superadmin') roles = ['SuperAdmin', 'Admin', 'Officer', 'Viewer'];\n        else if (myRole === 'admin') roles = ['Admin', 'Officer', 'Viewer'];\n\n        editRole.innerHTML = roles.map(r => `<option value=\"${r}\">${r}</option>`).join('');\n        editRole.value = user.role;\n\n        // Extra safety: If editing a SuperAdmin as a non-SuperAdmin, disable the role dropdown\n        if (targetRole === 'superadmin' && myRole !== 'superadmin') {\n            editRole.disabled = true;\n        } else {\n            editRole.disabled = false;\n        }\n    }\n\n    if (editFullname) editFullname.value = user.full_name || '';\n    if (editEmail) editEmail.value = user.email || '';\n    if (editPhone) editPhone.value = user.phone || '';\n    if (editDesig) editDesig.value = user.designation || '';\n    if (editDept) editDept.value = user.department || '';\n    if (editOffice) editOffice.value = user.office_location || '';\n    if (editActive) editActive.checked = user.is_active !== false;\n    const editRec = document.getElementById('edit-is-recovery');\n    if (editRec) editRec.checked = user.is_recovery === true;\n    if (editPass) editPass.value = '';\n\n    // Show the modal\n    const modal = document.getElementById('edit-user-modal');\n    if (modal) modal.classList.remove('hidden');\n}\n\nfunction closeEditUserModal() {\n    const modal = document.getElementById('edit-user-modal');\n    const form = document.getElementById('user-edit-form');\n    if (modal) modal.classList.add('hidden');\n    if (form) form.reset();\n}\n\nfunction deleteUser(userId, username) {\n    showConfirmModal(`Are you sure you want to delete user \"${username}\"?`, async () => {\n        try {\n            await fetch(`${API_BASE}/api/users/${userId}`, {\n                credentials: 'include',\n                method: 'DELETE'\n            }).then(async r => {\n                const data = await r.json();\n                if (!r.ok) throw new Error(data.error);\n                return data;\n            });\n            \n            showToast(`User \"${username}\" deleted.`, 'success');\n            loadUsers();\n        } catch (err) {\n            showToast(`Failed to delete user: ${err.message}`, 'error');\n        }\n    });\n}\n\n// Edit user modal event listeners (added to admin.js logic)\ndocument.addEventListener('DOMContentLoaded', function() {\n    // Initialize Dashboard Data\n    loadProfile();\n    loadFeedback();\n    loadUsers();\n\n    const closeBtn = document.getElementById('btn-close-edit-user');\n    const cancelBtn = document.getElementById('btn-cancel-edit-user');\n    \n    if (closeBtn) closeBtn.addEventListener('click', closeEditUserModal);\n    if (cancelBtn) cancelBtn.addEventListener('click', closeEditUserModal);\n    \n    const modal = document.getElementById('edit-user-modal');\n    if (modal) {\n        modal.addEventListener('click', function(e) {\n            if (e.target === modal) closeEditUserModal();\n        });\n    }\n    \n    const editUserForm = document.getElementById('user-edit-form');\n    if (editUserForm) {\n        editUserForm.addEventListener('submit', async function(e) {\n            e.preventDefault();\n            \n            const userId = document.getElementById('edit-user-id').value;\n            const updateData = {\n                full_name: document.getElementById('edit-fullname').value.trim(),\n                email: document.getElementById('edit-email').value.trim(),\n                phone: document.getElementById('edit-phone-user').value.trim(),\n                designation: document.getElementById('edit-designation-user').value.trim(),\n                department: document.getElementById('edit-department-user').value.trim(),\n                office_location: document.getElementById('edit-office-user').value.trim(),\n                role: document.getElementById('edit-role').value,\n                is_active: document.getElementById('edit-is-active').checked,\n                is_recovery: document.getElementById('edit-is-recovery') ? document.getElementById('edit-is-recovery').checked : false\n            };\n            \n            const newPassword = document.getElementById('edit-new-password').value;\n            if (newPassword) updateData.new_password = newPassword;\n            \n            try {\n                const res = await fetch(`${API_BASE}/api/users/${userId}`, {\n                    credentials: 'include',\n                    method: 'PUT',\n                    headers: { 'Content-Type': 'application/json' },\n                    body: JSON.stringify(updateData)\n                });\n                \n                const data = await res.json();\n                if (!res.ok) throw new Error(data.error);\n                \n                showToast('User updated successfully.', 'success');\n                closeEditUserModal();\n                loadUsers();\n            } catch (err) {\n                showToast(`Failed to update user: ${err.message}`, 'error');\n            }\n        });\n    }\n\n    // Create User Form Handler\n    const btnCreateUser = document.getElementById('btn-create-user');\n    const createUserForm = document.getElementById('create-user-form');\n    const userCreateForm = document.getElementById('user-create-form');\n    const cancelCreateUserBtn = document.getElementById('btn-cancel-create-user');\n    const cancelCreateUserBtn2 = document.getElementById('btn-cancel-create-user-2');\n\n    function hideCreateUserForm() {\n        if (createUserForm) createUserForm.classList.add('hidden');\n        if (userCreateForm) userCreateForm.reset();\n    }\n\n    if (btnCreateUser && createUserForm) {\n        btnCreateUser.addEventListener('click', () => {\n            createUserForm.classList.remove('hidden');\n            createUserForm.scrollIntoView({ behavior: 'smooth' });\n            \n            // Dynamic Role Filtering for Create Modal\n            const createRoleEl = document.getElementById('create-role');\n            if (createRoleEl) {\n                const myRole = (currentProfile.role || '').toLowerCase();\n                let roles = ['Officer', 'Viewer'];\n                if (myRole === 'superadmin') roles = ['SuperAdmin', 'Admin', 'Officer', 'Viewer'];\n                else if (myRole === 'admin') roles = ['Admin', 'Officer', 'Viewer'];\n\n                createRoleEl.innerHTML = roles.map(r => `<option value=\"${r}\">${r}</option>`).join('');\n            }\n        });\n    }\n\n    if (cancelCreateUserBtn) cancelCreateUserBtn.addEventListener('click', hideCreateUserForm);\n    if (cancelCreateUserBtn2) cancelCreateUserBtn2.addEventListener('click', hideCreateUserForm);\n\n    if (userCreateForm) {\n        userCreateForm.addEventListener('submit', async function(e) {\n            e.preventDefault();\n            const userData = {\n                username: document.getElementById('create-username').value.trim(),\n                password: document.getElementById('create-password').value,\n                full_name: document.getElementById('create-full-name').value.trim(),\n                email: document.getElementById('create-email').value.trim(),\n                phone: document.getElementById('create-phone').value.trim(),\n                designation: document.getElementById('create-designation').value.trim(),\n                department: document.getElementById('create-department').value.trim(),\n                role: document.getElementById('create-role').value,\n                office_location: document.getElementById('create-office').value.trim(),\n                is_active: document.getElementById('create-active').checked,\n                is_recovery: document.getElementById('create-is-recovery') ? document.getElementById('create-is-recovery').checked : false\n            };\n\n            try {\n                const res = await fetch(`${API_BASE}/api/users`, {\n                    credentials: 'include',\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application/json' },\n                    body: JSON.stringify(userData)\n                });\n                const data = await res.json();\n                if (!res.ok) throw new Error(data.error);\n\n                showToast(`User \"${userData.username}\" created successfully.`, 'success');\n                hideCreateUserForm();\n                loadUsers();\n            } catch (err) {\n                showToast(`Failed to create user: ${err.message}`, 'error');\n            }\n        });\n    }\n\n    // Profile Edit Logic\n    const btnEditProfile = document.getElementById('btn-edit-profile-inline');\n    const btnCancelEditProfile = document.getElementById('btn-cancel-edit-profile');\n    const profileView = document.getElementById('profile-view');\n    const profileEditForm = document.getElementById('edit-profile-form');\n    const profileForm = document.getElementById('profile-form');\n\n    if (btnEditProfile) {\n        btnEditProfile.addEventListener('click', () => {\n            if (currentProfile) {\n                document.getElementById('edit-full-name').value = currentProfile.full_name || '';\n                document.getElementById('edit-email').value = currentProfile.email || '';\n                document.getElementById('edit-phone').value = currentProfile.phone || '';\n                document.getElementById('edit-designation').value = currentProfile.designation || '';\n                document.getElementById('edit-department').value = currentProfile.department || '';\n                document.getElementById('edit-office').value = currentProfile.office_location || '';\n                \n                // Reset password fields\n                document.getElementById('edit-current-password').value = '';\n                document.getElementById('edit-new-password').value = '';\n            }\n            if (profileView) profileView.classList.add('hidden');\n            if (profileEditForm) profileEditForm.classList.remove('hidden');\n        });\n    }\n\n    if (btnCancelEditProfile) {\n        btnCancelEditProfile.addEventListener('click', () => {\n            if (profileView) profileView.classList.remove('hidden');\n            if (profileEditForm) profileEditForm.classList.add('hidden');\n        });\n    }\n\n    if (profileForm) {\n        profileForm.addEventListener('submit', async (e) => {\n            e.preventDefault();\n            \n            const updateData = {\n                full_name: document.getElementById('edit-full-name').value.trim(),\n                email: document.getElementById('edit-email').value.trim(),\n                phone: document.getElementById('edit-phone').value.trim(),\n                designation: document.getElementById('edit-designation').value.trim(),\n                department: document.getElementById('edit-department').value.trim(),\n                office_location: document.getElementById('edit-office').value.trim()\n            };\n\n            const currentPass = document.getElementById('edit-current-password').value;\n            const newPass = document.getElementById('edit-new-password').value;\n\n            if (newPass) {\n                if (!currentPass) {\n                    showToast('Current password is required to set a new password.', 'error');\n                    return;\n                }\n                updateData.current_password = currentPass;\n                updateData.new_password = newPass;\n            }\n\n            try {\n                const res = await fetch(`${API_BASE}/api/profile`, {\n                    credentials: 'include',\n                    method: 'PUT',\n                    headers: { 'Content-Type': 'application/json' },\n                    body: JSON.stringify(updateData)\n                });\n                \n                const data = await res.json();\n                if (!res.ok) throw new Error(data.error);\n\n                showToast('Profile updated successfully.', 'success');\n                \n                // Update global state and UI\n                currentProfile = data.profile;\n                displayProfile(currentProfile);\n                \n                // Switch back to view mode\n                if (profileView) profileView.classList.remove('hidden');\n                if (profileEditForm) profileEditForm.classList.add('hidden');\n            } catch (err) {\n                showToast(`Update failed: ${err.message}`, 'error');\n            }\n        });\n    }\n\n    // Refresh Buttons\n    const btnRefreshDashboard = document.getElementById('btn-refresh-dashboard');\n    if (btnRefreshDashboard) {\n        btnRefreshDashboard.addEventListener('click', async () => {\n            btnRefreshDashboard.classList.add('animate-spin');\n            try {\n                await loadRecordsOnMap(); \n                showToast('Dashboard data refreshed.', 'success');\n            } finally {\n                setTimeout(() => btnRefreshDashboard.classList.remove('animate-spin'), 600);\n            }\n        });\n    }\n\n    const btnRefreshFeedback = document.getElementById('btn-refresh-feedback');\n    if (btnRefreshFeedback) {\n        btnRefreshFeedback.addEventListener('click', async () => {\n            btnRefreshFeedback.classList.add('animate-spin');\n            try {\n                await loadFeedback();\n                showToast('Feedback list updated.', 'success');\n            } finally {\n                setTimeout(() => btnRefreshFeedback.classList.remove('animate-spin'), 600);\n            }\n        });\n    }\n\n    const btnExportExcel = document.getElementById('btn-export-excel');\n    if (btnExportExcel) {\n        btnExportExcel.addEventListener('click', () => {\n            const village = document.getElementById('village-filter')?.value || '';\n            const url = getVillageExcelUrl(village);\n            showToast('Preparing village ledger (Excel)...', 'info');\n            window.location.href = url;\n        });\n    }\n\n    const btnRefreshRecords = document.getElementById('btn-refresh-records');\n    if (btnRefreshRecords) {\n        btnRefreshRecords.addEventListener('click', async () => {\n            btnRefreshRecords.classList.add('animate-spin');\n            try {\n                await loadRecordsOnMap();\n                showToast('Records refreshed.', 'success');\n            } finally {\n                setTimeout(() => btnRefreshRecords.classList.remove('animate-spin'), 600);\n            }\n        });\n    }\n    // Header Actions\n    const btnLogout = document.getElementById('btn-logout');\n    if (btnLogout) {\n        btnLogout.addEventListener('click', () => {\n            showConfirmModal('Are you sure you want to log out?', async () => {\n                const data = await logout();\n                window.location.href = data.redirect || '/login';\n            });\n        });\n    }\n\n    // Theme toggle is now handled globally in admin_dashboard.html\n});\n"
  },
  {
    "path": "static/js/modules/dashboard.js",
    "content": "/**\n * dashboard.js - Dashboard rendering and analytics logic\n */\n\nfunction renderKpiCards(records) {\n    const totalParcelsEl = document.getElementById('kpi-total-parcels');\n    const totalAreaEl = document.getElementById('kpi-total-area');\n    const estimatedValueEl = document.getElementById('kpi-estimated-value');\n    const mutationEl = document.getElementById('kpi-mutations');\n\n    if (!totalParcelsEl || !totalAreaEl || !estimatedValueEl || !mutationEl) return;\n\n    let totalArea = 0;\n    let totalValue = 0;\n    let totalMutations = 0;\n\n    records.forEach(rec => {\n        const attrs = rec.attributes || {};\n        const area = asNumber(attrs.area_ha);\n        const rate = asNumber(attrs.circle_rate_inr);\n\n        totalArea += area;\n        totalValue += area * rate;\n        totalMutations += (rec.mutation_history || []).length;\n    });\n\n    totalParcelsEl.textContent = String(records.length);\n    totalAreaEl.textContent = totalArea.toFixed(2);\n    estimatedValueEl.textContent = formatInr(totalValue);\n    mutationEl.textContent = String(totalMutations);\n}\n\nfunction buildDonutChart(slices, colors) {\n    const total = slices.reduce((s, x) => s + x.value, 0);\n    if (total === 0) return '<p style=\"color:#9ca3af;font-size:12px;\">No data</p>';\n    const R = 40, CX = 50, CY = 50, stroke = 18;\n    let cumAngle = -90;\n    const arcs = slices.map((s, i) => {\n        const pct = s.value / total;\n        const angle = pct * 360;\n        const r1 = (cumAngle * Math.PI) / 180;\n        const r2 = ((cumAngle + angle) * Math.PI) / 180;\n        const x1 = CX + R * Math.cos(r1), y1 = CY + R * Math.sin(r1);\n        const x2 = CX + R * Math.cos(r2), y2 = CY + R * Math.sin(r2);\n        const large = angle > 180 ? 1 : 0;\n        const d = `M ${x1} ${y1} A ${R} ${R} 0 ${large} 1 ${x2} ${y2}`;\n        cumAngle += angle;\n        return `<path d=\"${d}\" fill=\"none\" stroke=\"${colors[i % colors.length]}\" stroke-width=\"${stroke}\" stroke-linecap=\"butt\"/>`;\n    }).join('');\n    return `\n        <div style=\"display:flex;align-items:center;gap:16px;\">\n            <svg viewBox=\"0 0 100 100\" style=\"width:90px;height:90px;flex-shrink:0;\">\n                <circle cx=\"${CX}\" cy=\"${CY}\" r=\"${R}\" fill=\"none\" stroke=\"currentColor\" stroke-opacity=\"0.1\" stroke-width=\"${stroke}\"/>\n                ${arcs}\n                <text x=\"${CX}\" y=\"${CY}\" text-anchor=\"middle\" dominant-baseline=\"central\" style=\"font-size:11px;font-weight:700;fill:currentColor;\">${total}</text>\n                <text x=\"${CX}\" y=\"${CY+12}\" text-anchor=\"middle\" dominant-baseline=\"central\" style=\"font-size:7px;fill:currentColor;opacity:0.6;\">parcels</text>\n            </svg>\n            <div style=\"flex:1;min-width:0;\">\n                ${slices.map((s, i) => `\n                    <div style=\"display:flex;align-items:center;gap:6px;margin-bottom:6px;\">\n                        <div style=\"width:10px;height:10px;border-radius:2px;background:${colors[i % colors.length]};flex-shrink:0;\"></div>\n                        <span style=\"font-size:11px;color:inherit;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100px;\" title=\"${escapeHtml(s.label)}\">${escapeHtml(s.label)}</span>\n                        <span style=\"margin-left:auto;font-size:11px;font-weight:700;color:inherit;\">${s.value}</span>\n                        <span style=\"font-size:10px;color:inherit;opacity:0.6;\">(${(s.value/total*100).toFixed(0)}%)</span>\n                    </div>`).join('')}\n            </div>\n        </div>`;\n}\n\nfunction buildRankedList(items, colors) {\n    const max = Math.max(...items.map(x => x.value), 1);\n    return items.map((item, i) => {\n        const pct = Math.max((item.value / max) * 100, 4);\n        const color = colors[i % colors.length];\n        return `\n            <div style=\"margin-bottom:10px;\">\n                <div style=\"display:flex;justify-content:space-between;align-items:baseline;margin-bottom:3px;\">\n                    <span style=\"font-size:11px;font-weight:600;color:inherit;\">${escapeHtml(item.label)}</span>\n                    <span style=\"font-size:11px;font-weight:700;color:${color};white-space:nowrap;margin-left:8px;\">${item.sublabel}</span>\n                </div>\n                <div style=\"background:currentColor;background-opacity:0.1;background-color:rgba(0,0,0,0.05);border-radius:99px;height:6px;\">\n                    <div style=\"background:${color};width:${pct}%;height:100%;border-radius:99px;transition:width 0.5s ease;\"></div>\n                </div>\n            </div>`;\n    }).join('');\n}\n\nfunction renderLandUseDistribution(records) {\n    const target = document.getElementById('dashboard-land-use');\n    if (!target) return;\n    if (!records.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No records yet.</p>'; return; }\n    const metrics = {};\n    records.forEach(rec => {\n        const lu = (rec.attributes && rec.attributes.land_use) || 'Unknown';\n        if (!metrics[lu]) metrics[lu] = { count: 0, area: 0 };\n        metrics[lu].count++;\n        metrics[lu].area += asNumber(rec.attributes && rec.attributes.area_ha);\n    });\n    const COLORS = ['#f97316','#3b82f6','#22c55e','#a855f7','#ec4899','#14b8a6','#f59e0b','#ef4444'];\n    const slices = Object.entries(metrics).sort((a,b)=>b[1].count-a[1].count)\n        .map(([label, v]) => ({ label, value: v.count, extra: v.area.toFixed(1)+' Ha' }));\n    target.innerHTML = buildDonutChart(slices, COLORS);\n}\n\nfunction renderDistrictOverview(records) {\n    const target = document.getElementById('dashboard-districts');\n    if (!target) return;\n    if (!records.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No district data.</p>'; return; }\n    const districtMap = {};\n    records.forEach(rec => {\n        const d = (rec.location && rec.location.district) || 'Unknown';\n        if (!districtMap[d]) districtMap[d] = { count: 0, area: 0, value: 0 };\n        const area = asNumber(rec.attributes && rec.attributes.area_ha);\n        districtMap[d].count++;\n        districtMap[d].area += area;\n        districtMap[d].value += area * asNumber(rec.attributes && rec.attributes.circle_rate_inr);\n    });\n    const COLORS = ['#6366f1','#f97316','#10b981','#f59e0b','#f43f5e','#a855f7'];\n    const items = Object.entries(districtMap).sort((a,b)=>b[1].count-a[1].count).slice(0,6)\n        .map(([label, v]) => ({\n            label,\n            value: v.count,\n            sublabel: `${v.count} parcels · ${v.area.toFixed(1)} Ha`\n        }));\n    target.innerHTML = buildRankedList(items, COLORS);\n}\n\nfunction renderTopValueParcel(records) {\n    const target = document.getElementById('dashboard-top-parcel');\n    if (!target) return;\n\n    if (!records.length) {\n        target.textContent = 'No parcel data yet.';\n        return;\n    }\n\n    let topRecord = null;\n    let topValue = -1;\n\n    records.forEach(rec => {\n        const area = asNumber(rec.attributes && rec.attributes.area_ha);\n        const rate = asNumber(rec.attributes && rec.attributes.circle_rate_inr);\n        const value = area * rate;\n        if (value > topValue) {\n            topValue = value;\n            topRecord = rec;\n        }\n    });\n\n    if (!topRecord) {\n        target.textContent = 'No parcel data yet.';\n        return;\n    }\n\n    const loc = topRecord.location || {};\n    const attrs = topRecord.attributes || {};\n    target.innerHTML = `\n        <div class=\"text-sm font-semibold text-inherit\">${escapeHtml(topRecord.khasra_no || 'N/A')} (${escapeHtml(topRecord.ulpin || 'N/A')})</div>\n        <div class=\"mt-1 text-xs text-inherit opacity-70\">${escapeHtml(loc.village || 'N/A')}, ${escapeHtml(loc.district || 'N/A')} | ${escapeHtml(attrs.land_use || 'N/A')}</div>\n        <div class=\"mt-1 text-xs text-inherit opacity-60\">Area: ${asNumber(attrs.area_ha).toFixed(2)} Ha | Estimated: Rs. ${formatInr(topValue)}</div>\n    `;\n}\n\nfunction renderRecentMutations(records) {\n    const target = document.getElementById('dashboard-mutations');\n    if (!target) return;\n\n    const entries = [];\n    records.forEach(rec => {\n        (rec.mutation_history || []).forEach(item => {\n            entries.push({\n                khasraNo: rec.khasra_no || 'N/A',\n                district: (rec.location && rec.location.district) || 'N/A',\n                previousOwner: item.previous_owner || 'N/A',\n                mutationType: item.mutation_type || 'N/A',\n                mutationDate: item.mutation_date || 'N/A',\n                mutationRef: item.mutation_ref || 'N/A'\n            });\n        });\n    });\n\n    entries.sort((a, b) => String(b.mutationDate).localeCompare(String(a.mutationDate)));\n\n    if (!entries.length) {\n        target.innerHTML = '<div class=\"dashboard-row text-xs text-gray-500\">No mutation history available for selected filters.</div>';\n        return;\n    }\n\n    target.innerHTML = entries.slice(0, 6).map(item => `\n        <div class=\"dashboard-row text-xs border-b border-gray-100 p-2\">\n            <div class=\"font-semibold text-inherit\">${escapeHtml(item.khasraNo)} | ${escapeHtml(item.mutationType)}</div>\n            <div class=\"mt-1 text-inherit opacity-70\">${escapeHtml(item.previousOwner)} -> ${escapeHtml(item.mutationDate)}</div>\n            <div class=\"text-inherit opacity-50\">${escapeHtml(item.district)} | Ref: ${escapeHtml(item.mutationRef)}</div>\n        </div>\n    `).join('');\n}\n\nfunction renderDashboardAnalytics(filteredRecords) {\n    renderKpiCards(filteredRecords);\n    renderLandUseDistribution(filteredRecords);\n    renderDistrictOverview(filteredRecords);\n    renderTopValueParcel(filteredRecords);\n    renderRecentMutations(filteredRecords);\n}\n\n// Server-side rendering helpers\nfunction renderKpiCardsFromServer(kpis) {\n    const totalParcelsEl = document.getElementById('kpi-total-parcels');\n    const totalAreaEl = document.getElementById('kpi-total-area');\n    const estimatedValueEl = document.getElementById('kpi-estimated-value');\n    const mutationEl = document.getElementById('kpi-mutations');\n    \n    if (totalParcelsEl) totalParcelsEl.textContent = String(kpis.total_parcels || 0);\n    if (totalAreaEl) totalAreaEl.textContent = (kpis.total_area || 0).toFixed(2);\n    if (estimatedValueEl) estimatedValueEl.textContent = formatInr(kpis.estimated_value || 0);\n    if (mutationEl) mutationEl.textContent = String(kpis.total_mutations || 0);\n}\n\nfunction renderLandUseDistributionFromServer(stats) {\n    const target = document.getElementById('dashboard-land-use');\n    if (!target) return;\n    const entries = Object.entries(stats || {});\n    if (!entries.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No records yet.</p>'; return; }\n    const COLORS = ['#f97316','#3b82f6','#22c55e','#a855f7','#ec4899','#14b8a6','#f59e0b','#ef4444'];\n    const slices = entries.sort((a,b)=>b[1].count-a[1].count)\n        .map(([label, s]) => ({ label, value: s.count, extra: s.area.toFixed(1)+' Ha' }));\n    target.innerHTML = buildDonutChart(slices, COLORS);\n}\n\nfunction renderDistrictOverviewFromServer(districts) {\n    const target = document.getElementById('dashboard-districts');\n    if (!target) return;\n    if (!districts || !districts.length) { target.innerHTML = '<p style=\"color:#9ca3af;font-size:12px;\">No district data.</p>'; return; }\n    const COLORS = ['#6366f1','#f97316','#10b981','#f59e0b','#f43f5e','#a855f7'];\n    const items = districts.map(d => ({\n        label: d.name,\n        value: d.count,\n        sublabel: `${d.count} parcels · ${d.area.toFixed(1)} Ha`\n    }));\n    target.innerHTML = buildRankedList(items, COLORS);\n}\n\nfunction renderTopValueParcelFromServer(parcel) {\n    const target = document.getElementById('dashboard-top-parcel');\n    if (!target) return;\n    \n    if (!parcel) {\n        target.textContent = 'No parcel data yet.';\n        return;\n    }\n    \n    target.innerHTML = `\n        <div class=\"text-sm font-semibold text-inherit\">${escapeHtml(parcel.khasra_no)} (${escapeHtml(parcel.ulpin)})</div>\n        <div class=\"mt-1 text-xs text-inherit opacity-70\">${escapeHtml(parcel.village)}, ${escapeHtml(parcel.district)} | ${escapeHtml(parcel.land_use)}</div>\n        <div class=\"mt-1 text-xs text-inherit opacity-60\">Area: ${parcel.area_ha} Ha | Estimated: Rs. ${formatInr(parcel.estimated_value)}</div>\n    `;\n}\n\nfunction renderRecentMutationsFromServer(mutations) {\n    const target = document.getElementById('dashboard-mutations');\n    if (!target) return;\n    \n    if (!mutations || !mutations.length) {\n        target.innerHTML = '<div class=\"dashboard-row text-xs text-gray-500\">No mutation history available for selected filters.</div>';\n        return;\n    }\n    \n    target.innerHTML = mutations.slice(0, 6).map(m => `\n        <div class=\"dashboard-row text-xs border-b border-gray-100 p-2\">\n            <div class=\"font-semibold text-inherit\">${escapeHtml(m.khasra_no)} | ${escapeHtml(m.mutation_type)}</div>\n            <div class=\"mt-1 text-inherit opacity-70\">${escapeHtml(m.previous_owner)} -> ${escapeHtml(m.mutation_date)}</div>\n        </div>\n    `).join('');\n}\n"
  },
  {
    "path": "static/js/modules/forms.js",
    "content": "/**\n * forms.js - Form handling and location auto-fill logic\n */\n\nfunction getFormElements() {\n    return {\n        stateEl: document.getElementById('form-state'),\n        districtEl: document.getElementById('form-district'),\n        villageEl: document.getElementById('form-village'),\n        manualOverrideEl: document.getElementById('location-manual-override'),\n        manualFieldsEl: document.getElementById('location-manual-fields'),\n        stateManualEl: document.getElementById('form-state-manual'),\n        districtManualEl: document.getElementById('form-district-manual'),\n        villageManualEl: document.getElementById('form-village-manual'),\n        sourceEl: document.getElementById('form-location-source')\n    };\n}\n\nfunction populateSelect(selectEl, values, placeholder, selectedValue) {\n    if (!selectEl) return;\n\n    const current = selectedValue || '';\n    selectEl.innerHTML = '';\n\n    const placeholderOption = document.createElement('option');\n    placeholderOption.value = '';\n    placeholderOption.textContent = placeholder;\n    selectEl.appendChild(placeholderOption);\n\n    values.forEach(value => {\n        const option = document.createElement('option');\n        option.value = value;\n        option.textContent = value;\n        selectEl.appendChild(option);\n    });\n\n    if (current && !values.includes(current)) {\n        const customOption = document.createElement('option');\n        customOption.value = current;\n        customOption.textContent = current;\n        selectEl.appendChild(customOption);\n    }\n\n    selectEl.value = current;\n}\n\nfunction refreshStateOptions(selectedState) {\n    const { stateEl } = getFormElements();\n    if (!stateEl) return;\n    const states = sortedValues(Object.keys(locationCatalog));\n    populateSelect(stateEl, states, 'Select State', selectedState || '');\n}\n\nfunction refreshDistrictOptions(selectedDistrict) {\n    const { stateEl, districtEl } = getFormElements();\n    if (!stateEl || !districtEl) return;\n\n    const state = stateEl.value;\n    const districts = state && locationCatalog[state] ? sortedValues(Object.keys(locationCatalog[state])) : [];\n    populateSelect(districtEl, districts, 'Select District', selectedDistrict || '');\n}\n\nfunction refreshVillageOptions(selectedVillage) {\n    const { stateEl, districtEl, villageEl } = getFormElements();\n    if (!stateEl || !districtEl || !villageEl) return;\n\n    const state = stateEl.value;\n    const district = districtEl.value;\n    const villages = state && district && locationCatalog[state] && locationCatalog[state][district]\n        ? sortedValues(locationCatalog[state][district])\n        : [];\n\n    populateSelect(villageEl, villages, 'Select Village / Ward', selectedVillage || '');\n}\n\nfunction setLocationValues(state, district, village) {\n    ensureLocationInCatalog(state, district, village);\n\n    refreshStateOptions(state || '');\n    refreshDistrictOptions(district || '');\n    refreshVillageOptions(village || '');\n\n    const { stateManualEl, districtManualEl, villageManualEl } = getFormElements();\n    if (stateManualEl && !stateManualEl.value.trim()) {\n        stateManualEl.value = state || '';\n    }\n    if (districtManualEl && !districtManualEl.value.trim()) {\n        districtManualEl.value = district || '';\n    }\n    if (villageManualEl && !villageManualEl.value.trim()) {\n        villageManualEl.value = village || '';\n    }\n}\n\nfunction setLocationSource(text) {\n    const { sourceEl } = getFormElements();\n    if (sourceEl) {\n        sourceEl.textContent = text;\n    }\n}\n\nfunction isManualLocationOverrideEnabled() {\n    const { manualOverrideEl } = getFormElements();\n    return !!(manualOverrideEl && manualOverrideEl.checked);\n}\n\nfunction toggleManualLocationOverride(enabled) {\n    const {\n        stateEl,\n        districtEl,\n        villageEl,\n        manualFieldsEl,\n        stateManualEl,\n        districtManualEl,\n        villageManualEl\n    } = getFormElements();\n\n    if (!stateEl || !districtEl || !villageEl || !manualFieldsEl || !stateManualEl || !districtManualEl || !villageManualEl) {\n        return;\n    }\n\n    manualFieldsEl.classList.toggle('hidden', !enabled);\n    stateEl.disabled = enabled;\n    districtEl.disabled = enabled;\n    villageEl.disabled = enabled;\n\n    stateEl.required = !enabled;\n    districtEl.required = !enabled;\n    villageEl.required = !enabled;\n\n    stateManualEl.required = enabled;\n    districtManualEl.required = enabled;\n    villageManualEl.required = enabled;\n\n    if (enabled) {\n        stateManualEl.value = stateEl.value || stateManualEl.value;\n        districtManualEl.value = districtEl.value || districtManualEl.value;\n        villageManualEl.value = villageEl.value || villageManualEl.value;\n        setLocationSource('Source: manual override enabled');\n    } else {\n        setLocationSource('Source: map auto-fill enabled');\n    }\n}\n\nfunction getEffectiveLocationValues() {\n    const {\n        stateEl,\n        districtEl,\n        villageEl,\n        stateManualEl,\n        districtManualEl,\n        villageManualEl\n    } = getFormElements();\n\n    if (isManualLocationOverrideEnabled()) {\n        return {\n            state: (stateManualEl ? stateManualEl.value : '').trim(),\n            district: (districtManualEl ? districtManualEl.value : '').trim(),\n            village: (villageManualEl ? villageManualEl.value : '').trim()\n        };\n    }\n\n    return {\n        state: stateEl ? stateEl.value : '',\n        district: districtEl ? districtEl.value : '',\n        village: villageEl ? villageEl.value : ''\n    };\n}\n\nfunction initializeLocationFilters() {\n    const {\n        stateEl,\n        districtEl,\n        villageEl,\n        manualOverrideEl,\n        stateManualEl,\n        districtManualEl,\n        villageManualEl\n    } = getFormElements();\n\n    if (!stateEl || !districtEl || !villageEl) return;\n\n    // Reset catalog (will be repopulated from records)\n    locationCatalog = {};\n\n    refreshStateOptions('');\n    refreshDistrictOptions('');\n    refreshVillageOptions('');\n\n    stateEl.addEventListener('change', function() {\n        lastAutoLocation = { state: '', district: '', village: '' };\n        refreshDistrictOptions('');\n        refreshVillageOptions('');\n    });\n\n    districtEl.addEventListener('change', function() {\n        lastAutoLocation = { state: '', district: '', village: '' };\n        refreshVillageOptions('');\n    });\n\n    if (manualOverrideEl) {\n        manualOverrideEl.addEventListener('change', function() {\n            toggleManualLocationOverride(this.checked);\n        });\n    }\n\n    if (stateManualEl && districtManualEl && villageManualEl) {\n        const onManualEntry = function() {\n            if (!isManualLocationOverrideEnabled()) return;\n            lastAutoLocation = {\n                state: stateManualEl.value.trim(),\n                district: districtManualEl.value.trim(),\n                village: villageManualEl.value.trim()\n            };\n        };\n\n        stateManualEl.addEventListener('input', onManualEntry);\n        districtManualEl.addEventListener('input', onManualEntry);\n        villageManualEl.addEventListener('input', onManualEntry);\n    }\n    \n    toggleManualLocationOverride(false);\n\n    ['form-area', 'form-circle-rate', 'form-land-use'].forEach(id => {\n        const el = document.getElementById(id);\n        if (el) {\n            el.addEventListener('input', () => window.updateLiveValuation());\n            el.addEventListener('change', () => window.updateLiveValuation());\n        }\n    });\n}\n\n// Live Valuation Logic\nwindow.updateLiveValuation = function() {\n    const areaInput = document.getElementById('form-area');\n    const rateInput = document.getElementById('form-circle-rate');\n    const luInput = document.getElementById('form-land-use');\n    \n    if (!areaInput || !rateInput || !luInput) return;\n\n    const area = asNumber(areaInput.value);\n    const rate = asNumber(rateInput.value);\n    const landUse = luInput.value;\n    \n    const total = calculateValuation(area, rate, landUse);\n    \n    const valEl = document.getElementById('form-live-value');\n    const multEl = document.getElementById('form-live-multiplier');\n    \n    if (valEl) valEl.textContent = 'Rs. ' + formatInr(total);\n    if (multEl) {\n        const multipliers = {\n            'Commercial': 2.5, 'Industrial': 1.8, 'Residential': 1.5,\n            'Agricultural': 1.0, 'Government': 1.2, 'Forest': 0.8, 'Wasteland': 0.5\n        };\n        const multiplier = multipliers[landUse] || 1.0;\n        multEl.textContent = multiplier.toFixed(1) + 'x';\n    }\n};\n\nfunction isMapAutofillEnabled() {\n    const enabledEl = document.getElementById('map-autofill-enabled');\n    return enabledEl ? enabledEl.checked : true;\n}\n\nfunction updateGpsBanner(locationData) {\n    const gpsBanner = document.getElementById('gps-detected-banner');\n    if (!gpsBanner) return;\n\n    if (!locationData) {\n        gpsBanner.classList.add('hidden');\n        return;\n    }\n\n    gpsBanner.classList.remove('hidden');\n    \n    if (locationData.loading) {\n        gpsBanner.innerHTML = `\n            <div class=\"flex items-center gap-2\">\n                <svg class=\"w-4 h-4 text-green-600 animate-spin\" fill=\"none\" viewBox=\"0 0 24 24\"><circle class=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" stroke-width=\"4\"></circle><path class=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"></path></svg>\n                <span class=\"text-xs font-semibold text-green-700\">Detecting location from GPS coordinates...</span>\n            </div>`;\n        return;\n    }\n\n    if (locationData.state && locationData.district) {\n        const villageDisplay = locationData.village ? `${escapeHtml(locationData.village)}, ` : '';\n        gpsBanner.innerHTML = `\n            <div class=\"flex items-start justify-between gap-4\">\n                <div class=\"flex items-start gap-2\">\n                    <svg class=\"w-4 h-4 text-green-600 flex-shrink-0 mt-0.5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\n                    <div>\n                        <p class=\"text-xs font-bold text-green-800\">GPS Detected Location</p>\n                        <p class=\"text-xs text-green-700 mt-0.5\">\n                            <span class=\"font-semibold\">${escapeHtml(locationData.state)}</span> &rsaquo;\n                            <span class=\"font-semibold\">${escapeHtml(locationData.district)}</span> &rsaquo;\n                            ${escapeHtml(locationData.village || 'N/A')}\n                        </p>\n                    </div>\n                </div>\n                <button type=\"button\" id=\"btn-sync-gps\" class=\"flex-shrink-0 bg-green-600 hover:bg-green-700 text-white text-[10px] font-bold px-2 py-1 rounded transition-colors shadow-sm\">\n                    USE THIS\n                </button>\n            </div>`;\n        \n        const syncBtn = document.getElementById('btn-sync-gps');\n        if (syncBtn) {\n            syncBtn.addEventListener('click', () => {\n                applyResolvedLocation(locationData, true);\n                showToast('Form updated with GPS location.', 'success');\n            });\n        }\n    } else {\n        gpsBanner.classList.add('hidden');\n    }\n}\n\nfunction shouldApplyLocationUpdate(nextLocation, forceUpdate) {\n    const { stateEl, districtEl, villageEl } = getFormElements();\n    if (!stateEl || !districtEl || !villageEl) return false;\n\n    if (isManualLocationOverrideEnabled()) return false;\n\n    if (forceUpdate) return true;\n\n    const current = {\n        state: stateEl.value || '',\n        district: districtEl.value || '',\n        village: villageEl.value || ''\n    };\n\n    const currentIsEmpty = !current.state && !current.district;\n    \n    // Loosened check: if state and district match previous auto, we can still update village\n    // OR if the new location is in a completely different state/district, we should probably update if it was auto-filled\n    const currentIsPreviousAuto =\n        current.state === (lastAutoLocation.state || '') &&\n        current.district === (lastAutoLocation.district || '');\n\n    const nextLooksUseful = nextLocation.state || nextLocation.district;\n    return !!nextLooksUseful && (currentIsEmpty || currentIsPreviousAuto);\n}\n\nfunction applyResolvedLocation(locationData, forceUpdate) {\n    const nextLocation = {\n        state: locationData.state || '',\n        district: locationData.district || '',\n        village: locationData.village || ''\n    };\n\n    // Update the GPS detected state for mismatch validation\n    if (nextLocation.state && nextLocation.district) {\n        lastGpsDetectedLocation = { ...nextLocation };\n        updateGpsBanner(nextLocation);\n    } else {\n        lastGpsDetectedLocation = { state: '', district: '', village: '' };\n        updateGpsBanner(null);\n    }\n\n    if (!shouldApplyLocationUpdate(nextLocation, forceUpdate)) {\n        updateAutofillStatus('Map location detected. Location fields kept as manually selected.', false);\n        return;\n    }\n\n    setLocationValues(nextLocation.state, nextLocation.district, nextLocation.village);\n    lastAutoLocation = nextLocation;\n    setLocationSource(`Source: ${locationData.display_name || 'Map reverse geocoding'}`);\n    \n    const displayVillage = nextLocation.village ? `${nextLocation.village}, ` : '';\n    updateAutofillStatus(`Auto-filled: ${displayVillage}${nextLocation.district || 'District N/A'}, ${nextLocation.state || 'State N/A'}`, false);\n}\n\nfunction scheduleMapLocationLookup(lat, lng, forceUpdate) {\n    if (!isAdmin) return;\n    \n    // Stop tracker if manual override is enabled\n    if (isManualLocationOverrideEnabled()) {\n        updateGpsBanner(null);\n        return;\n    }\n    \n    if (typeof lat !== 'number' || typeof lng !== 'number') return;\n\n    const geocodeKey = `${lat.toFixed(4)},${lng.toFixed(4)}`;\n    if (!forceUpdate && geocodeKey === lastGeocodeKey) return;\n    lastGeocodeKey = geocodeKey;\n\n    if (reverseGeocodeTimer) {\n        clearTimeout(reverseGeocodeTimer);\n    }\n\n    // Show loading state in GPS banner\n    updateGpsBanner({ loading: true });\n\n    reverseGeocodeTimer = setTimeout(async function() {\n        updateAutofillStatus('Detecting state and district from map...', false);\n        try {\n            const locationData = await fetchLocationFromCoordinates(lat, lng);\n            applyResolvedLocation(locationData, forceUpdate);\n        } catch (err) {\n            updateAutofillStatus(err.message || 'Map location detection failed.', true);\n            updateGpsBanner(null);\n        }\n    }, 650);\n}\n\nfunction setMutationMode(enabled) {\n    const mutationSection = document.getElementById('mutation-section');\n    const mutationTabBtn = document.getElementById('form-mutation-tab-btn');\n\n    if (!mutationSection || !mutationTabBtn) return;\n\n    mutationSection.classList.toggle('hidden', !enabled);\n    mutationTabBtn.classList.toggle('hidden', !enabled);\n}\n\nasync function editRecord(recordId) {\n    try {\n        const record = await fetchRecord(recordId);\n        switchMainTab('add-record');\n        \n        // Wait for tab to show\n        await new Promise(resolve => setTimeout(resolve, 200));\n\n        const { manualOverrideEl } = getFormElements();\n        if (manualOverrideEl) {\n            manualOverrideEl.checked = false;\n        }\n        toggleManualLocationOverride(false);\n\n        document.getElementById('form-record-id').value = record._id;\n        document.getElementById('form-title').textContent = 'Edit Record: ' + (record.khasra_no || '');\n        document.getElementById('form-khasra').value = record.khasra_no || '';\n        document.getElementById('form-khata').value = record.khata_no || '';\n        document.getElementById('form-ulpin').value = record.ulpin || '';\n        document.getElementById('form-land-use').value = (record.attributes && record.attributes.land_use) || '';\n        document.getElementById('form-area').value = (record.attributes && record.attributes.area_ha) || '';\n        document.getElementById('form-circle-rate').value = (record.attributes && record.attributes.circle_rate_inr) || 0;\n        document.getElementById('form-owner-name').value = (record.owner && record.owner.name) || '';\n        document.getElementById('form-share').value = (record.owner && record.owner.share_pct) || 100;\n        document.getElementById('form-aadhaar').value = (record.owner && record.owner.aadhaar_mask) || '';\n\n        const loc = record.location || {};\n        setLocationValues(loc.state || '', loc.district || '', loc.village || '');\n        setLocationSource('Source: loaded from existing record');\n\n        const geometry = record.geometry || null;\n        if (geometry) {\n            await updateGeometryMetricsForAddRecord(geometry);\n            if (addRecordDrawnItems) addRecordDrawnItems.clearLayers();\n            const editableLayer = L.geoJSON(geometry, {\n                style: { color: '#ea580c', fillColor: '#fed7aa', fillOpacity: 0.4, weight: 3 }\n            });\n            editableLayer.eachLayer(layer => {\n                if (addRecordDrawnItems) addRecordDrawnItems.addLayer(layer);\n                currentSketchLayer = layer;\n            });\n            if (addRecordMap) {\n                const bounds = editableLayer.getBounds();\n                addRecordMap.fitBounds(bounds, { padding: [50, 50], maxZoom: 16 });\n                setTimeout(() => {\n                    editableLayer.eachLayer(layer => { if (layer.pm) layer.pm.enable(); });\n                }, 300);\n            }\n        }\n\n        setMutationMode(true);\n        document.getElementById('form-submit-btn').textContent = 'Update Record';\n\n        const drawInstruction = document.getElementById('draw-instruction');\n        if (drawInstruction) drawInstruction.style.display = 'none';\n\n        const drawStatus = document.getElementById('draw-status');\n        if (drawStatus) {\n            drawStatus.innerHTML = '<strong>Editing existing record.</strong> You can modify the polygon on the map.';\n            drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\n        }\n\n        switchFormTab('location');\n    } catch (_err) {\n        showToast('Failed to load record for editing.', 'error');\n    }\n}\n\nasync function handleFormSubmit() {\n    const recordId = document.getElementById('form-record-id').value;\n    const geometryStr = document.getElementById('form-geometry').value;\n\n    const locationValues = {\n        state: document.getElementById('form-state-manual').value || document.getElementById('form-state').value,\n        district: document.getElementById('form-district-manual').value || document.getElementById('form-district').value,\n        village: document.getElementById('form-village-manual').value || document.getElementById('form-village').value\n    };\n\n    if (!locationValues.state || !locationValues.district || !locationValues.village) {\n        showToast('Location (State, District, Village) is required.', 'error');\n        switchFormTab('location');\n        return;\n    }\n\n    const manualOverrideEl = document.getElementById('location-manual-override');\n    const isManualOverride = manualOverrideEl && manualOverrideEl.checked;\n    const hasGpsData = lastGpsDetectedLocation.state && lastGpsDetectedLocation.district;\n\n    if (isManualOverride && hasGpsData) {\n        const stateMatch = locationValues.state.trim().toLowerCase() === lastGpsDetectedLocation.state.trim().toLowerCase();\n        const districtMatch = locationValues.district.trim().toLowerCase() === lastGpsDetectedLocation.district.trim().toLowerCase();\n\n        if (!stateMatch || !districtMatch) {\n            const proceed = await new Promise(resolve => {\n                const overlay = document.createElement('div');\n                overlay.className = 'fixed inset-0 bg-black bg-opacity-60 z-[9999] flex items-center justify-center p-4';\n                overlay.style.backdropFilter = 'blur(3px)';\n\n                overlay.innerHTML = `\n                    <div class=\"bg-white rounded-xl shadow-2xl max-w-md w-full overflow-hidden\">\n                        <div class=\"bg-yellow-50 border-b-4 border-yellow-400 px-6 py-4 flex items-center gap-3\">\n                            <svg class=\"w-8 h-8 text-yellow-500 flex-shrink-0\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>\n                            <h3 class=\"text-lg font-bold text-yellow-800\">Location Mismatch Warning</h3>\n                        </div>\n                        <div class=\"px-6 py-5\">\n                            <p class=\"text-sm text-gray-600 mb-4\">The location you entered does not match the GPS coordinates of the parcel you drew on the map.</p>\n                            <div class=\"grid grid-cols-2 gap-3 mb-4\">\n                                <div class=\"bg-green-50 rounded-lg p-3 border border-green-200\">\n                                    <p class=\"text-xs font-bold text-green-700 mb-1\">📡 GPS Detected</p>\n                                    <p class=\"text-sm font-semibold text-green-800\">${escapeHtml(lastGpsDetectedLocation.state)}</p>\n                                    <p class=\"text-xs text-green-600\">${escapeHtml(lastGpsDetectedLocation.district)}</p>\n                                </div>\n                                <div class=\"bg-red-50 rounded-lg p-3 border border-red-200\">\n                                    <p class=\"text-xs font-bold text-red-700 mb-1\">✏️ You Entered</p>\n                                    <p class=\"text-sm font-semibold text-red-800\">${escapeHtml(locationValues.state)}</p>\n                                    <p class=\"text-xs text-red-600\">${escapeHtml(locationValues.district)}</p>\n                                </div>\n                            </div>\n                            <p class=\"text-xs text-gray-500 mb-5\">Are you sure you want to continue with the manually entered location?</p>\n                            <div class=\"flex gap-3\">\n                                <button id=\"mismatch-cancel\" class=\"flex-1 px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg text-sm font-medium transition\">← Use GPS Location</button>\n                                <button id=\"mismatch-force\" class=\"flex-1 px-4 py-2 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg text-sm font-medium transition\">Force Save Anyway</button>\n                            </div>\n                        </div>\n                    </div>`;\n\n                document.body.appendChild(overlay);\n                overlay.querySelector('#mismatch-cancel').addEventListener('click', () => {\n                    const manualOvEl = document.getElementById('location-manual-override');\n                    if (manualOvEl) manualOvEl.checked = false;\n                    toggleManualLocationOverride(false);\n                    setLocationValues(lastGpsDetectedLocation.state, lastGpsDetectedLocation.district, lastGpsDetectedLocation.village);\n                    document.body.removeChild(overlay);\n                    resolve(false);\n                });\n                overlay.querySelector('#mismatch-force').addEventListener('click', () => {\n                    document.body.removeChild(overlay);\n                    resolve(true);\n                });\n            });\n\n            if (!proceed) return;\n        }\n    }\n\n    const khasra = document.getElementById('form-khasra').value.trim();\n    const khata = document.getElementById('form-khata').value.trim();\n    const landUse = document.getElementById('form-land-use').value;\n    \n    if (!khasra) {\n        showToast('Khasra No. is required.', 'error');\n        switchFormTab('parcel');\n        return;\n    }\n    if (!khata) {\n        showToast('Khata No. is required.', 'error');\n        switchFormTab('parcel');\n        return;\n    }\n    if (!landUse) {\n        showToast('Land Use is required.', 'error');\n        switchFormTab('parcel');\n        return;\n    }\n\n    if (!geometryStr) {\n        showToast('Please draw a parcel on the map first.', 'error');\n        switchFormTab('parcel');\n        return;\n    }\n\n    if (!recordId) {\n        const ownerName = document.getElementById('form-owner-name').value.trim();\n        if (!ownerName) {\n            showToast('Owner Name is required.', 'error');\n            switchFormTab('owner');\n            return;\n        }\n    }\n\n    let geometry;\n    try {\n        geometry = JSON.parse(geometryStr);\n    } catch (_err) {\n        showToast('Invalid geometry data.', 'error');\n        return;\n    }\n\n    const submitBtn = document.getElementById('form-submit-btn');\n    const originalText = submitBtn.textContent;\n    submitBtn.disabled = true;\n    submitBtn.innerHTML = 'Saving...';\n\n    try {\n        let ownerDocB64 = undefined;\n        const ownerDocFile = document.getElementById('form-owner-doc')?.files[0];\n        if (ownerDocFile) ownerDocB64 = await fileToBase64(ownerDocFile);\n\n        let mutationDocB64 = undefined;\n        const mutationDocFile = document.getElementById('form-mutation-doc')?.files[0];\n        if (mutationDocFile) mutationDocB64 = await fileToBase64(mutationDocFile);\n\n        if (recordId) {\n            const updateData = {\n                khasra_no: document.getElementById('form-khasra').value,\n                khata_no: document.getElementById('form-khata').value,\n                land_use: document.getElementById('form-land-use').value,\n                circle_rate_inr: parseFloat(document.getElementById('form-circle-rate').value) || 0,\n                share_pct: parseFloat(document.getElementById('form-share').value) || 100,\n                aadhaar_mask: document.getElementById('form-aadhaar').value,\n                geometry: geometry,\n                location: locationValues,\n                owner_proof_doc_b64: ownerDocB64\n            };\n\n            const newOwner = document.getElementById('form-new-owner').value.trim();\n            if (newOwner) {\n                updateData.mutation = true;\n                updateData.new_owner_name = newOwner;\n                updateData.new_share_pct = parseFloat(document.getElementById('form-new-share').value) || 100;\n                updateData.mutation_type = document.getElementById('form-mutation-type').value;\n                updateData.mutation_date = document.getElementById('form-mutation-date').value || new Date().toISOString().split('T')[0];\n                updateData.mutation_proof_doc_b64 = mutationDocB64;\n            }\n\n            await updateRecord(recordId, updateData);\n            showToast('Record updated successfully.', 'success');\n        } else {\n            const recordData = {\n                khasra_no: document.getElementById('form-khasra').value,\n                khata_no: document.getElementById('form-khata').value,\n                ulpin: document.getElementById('form-ulpin').value || undefined,\n                land_use: document.getElementById('form-land-use').value,\n                circle_rate_inr: parseFloat(document.getElementById('form-circle-rate').value) || 0,\n                owner_name: document.getElementById('form-owner-name').value,\n                share_pct: parseFloat(document.getElementById('form-share').value) || 100,\n                aadhaar_mask: document.getElementById('form-aadhaar').value || 'XXXX-XXXX-XXXX',\n                geometry: geometry,\n                location: locationValues,\n                owner_proof_doc_b64: ownerDocB64\n            };\n\n            const result = await createRecord(recordData);\n            showToast('Record created successfully.', 'success');\n        }\n\n        resetForm();\n        switchMainTab('records');\n        loadRecordsOnMap();\n    } catch (err) {\n        showToast(`Error: ${err.message}`, 'error');\n    } finally {\n        submitBtn.disabled = false;\n        submitBtn.textContent = originalText;\n    }\n}\n\nfunction resetForm() {\n    const form = document.getElementById('record-form');\n    if (form) form.reset();\n\n    document.getElementById('form-record-id').value = '';\n    document.getElementById('form-title').textContent = 'Add New Record';\n    document.getElementById('form-submit-btn').textContent = 'Create Record';\n    document.getElementById('form-geometry').value = '';\n\n    setMutationMode(false);\n    clearGeometrySelection(true);\n\n    const { manualOverrideEl } = getFormElements();\n    if (manualOverrideEl) manualOverrideEl.checked = false;\n    toggleManualLocationOverride(false);\n\n    const drawInstruction = document.getElementById('draw-instruction');\n    if (drawInstruction) drawInstruction.style.display = 'block';\n\n    const drawStatus = document.getElementById('draw-status');\n    if (drawStatus) {\n        drawStatus.innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\n        drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\n    }\n\n    lastGpsDetectedLocation = { state: '', district: '', village: '' };\n    updateGpsBanner(null);\n\n    switchFormTab('location');\n}\n\n/**\n * Ensures a location exists in the local catalog so it can be selected in dropdowns.\n * Used primarily when map auto-detection finds a location not yet in our database.\n */\nfunction ensureLocationInCatalog(state, district, village) {\n    if (!state) return;\n    if (!locationCatalog) locationCatalog = {};\n    if (!locationCatalog[state]) locationCatalog[state] = {};\n    if (district) {\n        if (!locationCatalog[state][district]) locationCatalog[state][district] = [];\n        if (village && !locationCatalog[state][district].includes(village)) {\n            locationCatalog[state][district].push(village);\n            locationCatalog[state][district].sort();\n        }\n    }\n}\n"
  },
  {
    "path": "static/js/modules/gis.js",
    "content": "/**\n * gis.js - GIS-related UI updates (area, perimeter, centroid)\n */\n\nasync function updateGeometryMetrics(geometry, options) {\n    const shouldRefreshMetrics = options && options.shouldRefreshMetrics;\n\n    if (!geometry) return;\n\n    const geometryInput = document.getElementById('form-geometry');\n    if (geometryInput) {\n        geometryInput.value = JSON.stringify(geometry);\n    }\n\n    if (!shouldRefreshMetrics) {\n        return;\n    }\n\n    try {\n        const result = await calculateArea(geometry);\n\n        if (!result.area) {\n            return;\n        }\n\n        const area = result.area;\n        const perimeter = result.perimeter || {};\n        const centroid = result.centroid || {};\n\n        const areaInput = document.getElementById('form-area');\n        const areaAuto = document.getElementById('area-auto');\n        const areaEquivalents = document.getElementById('area-equivalents');\n        const geometryMetrics = document.getElementById('geometry-metrics');\n        const perimeterEl = document.getElementById('metric-perimeter');\n        const centroidEl = document.getElementById('metric-centroid');\n\n        if (areaInput) {\n            areaInput.value = area.area_ha;\n        }\n        if (areaAuto) {\n            areaAuto.textContent = '(auto)';\n        }\n\n        if (areaEquivalents) {\n            areaEquivalents.classList.remove('hidden');\n            areaEquivalents.innerHTML = `\n                <span><strong>${area.area_acres}</strong> Acres</span>\n                <span><strong>${area.area_guntha}</strong> Guntha</span>\n                <span><strong>${area.area_bigha_mp}</strong> Bigha</span>\n            `;\n        }\n\n        if (geometryMetrics) {\n            geometryMetrics.classList.remove('hidden');\n        }\n\n        if (perimeterEl) {\n            perimeterEl.textContent = `${Math.round(perimeter.perimeter_m || 0)} m`;\n        }\n        if (centroidEl) {\n            centroidEl.textContent = `${centroid.lat.toFixed(5)}, ${centroid.lng.toFixed(5)}`;\n        }\n    } catch (err) {\n        console.error('Failed to update geometry metrics:', err);\n    }\n}\n\nasync function updateGeometryMetricsForAddRecord(geometry) {\n    const geometryInput = document.getElementById('form-geometry');\n\n    // Set geometry IMMEDIATELY so form can save\n    if (geometryInput) geometryInput.value = JSON.stringify(geometry);\n\n    // Now fetch area metrics\n    try {\n        const result = await calculateArea(geometry);\n\n        // API returns: { area: { area_ha, area_acres, ... }, perimeter: {...}, centroid: {...} }\n        const areaData = result.area || result;\n        const perimeterData = result.perimeter || {};\n        const centroidData = result.centroid || {};\n\n        const areaInput = document.getElementById('form-area');\n        const areaAuto = document.getElementById('area-auto');\n        const areaEquivalents = document.getElementById('area-equivalents');\n        const geometryMetrics = document.getElementById('geometry-metrics');\n        const perimeterEl = document.getElementById('metric-perimeter');\n        const centroidEl = document.getElementById('metric-centroid');\n\n        if (areaInput) areaInput.value = areaData.area_ha || '';\n        if (areaAuto) areaAuto.textContent = `(Auto-calculated)`;\n\n        if (areaEquivalents) {\n            areaEquivalents.classList.remove('hidden');\n            const bigha = areaData.area_bigha_assam || (areaData.area_ha * 7.4752).toFixed(2);\n            const lecha = areaData.area_lecha_assam || Math.round(areaData.area_ha * 747.52);\n            areaEquivalents.innerHTML = `\n                <div><strong>${areaData.area_ha || '?'}</strong> Ha</div>\n                <div><strong>${areaData.area_acres || '?'}</strong> Acres</div>\n                <div><strong>${areaData.area_guntha || '?'}</strong> Guntha</div>\n                <div style=\"color:#ea580c\"><strong>${bigha}</strong> Bigha</div>\n                <div style=\"color:#ea580c\"><strong>${lecha}</strong> Lecha</div>\n            `;\n        }\n\n        if (geometryMetrics) geometryMetrics.classList.remove('hidden');\n        if (perimeterEl) perimeterEl.textContent = perimeterData.perimeter_m ? `${Math.round(perimeterData.perimeter_m)} m` : 'N/A';\n        if (centroidEl) centroidEl.textContent = centroidData.lat ? `${centroidData.lat.toFixed(5)}, ${centroidData.lng.toFixed(5)}` : 'N/A';\n\n        const drawStatus = document.getElementById('draw-status');\n        if (drawStatus) {\n            drawStatus.innerHTML = `<strong>Parcel geometry captured.</strong> Area: ${areaData.area_ha} Ha. Ready to save.`;\n            drawStatus.className = 'bg-green-50 border-l-4 border-green-500 rounded-r-lg p-3 text-sm text-green-800';\n        }\n\n        // Trigger live valuation update in forms.js\n        if (typeof updateLiveValuation === 'function') {\n            updateLiveValuation();\n        }\n    } catch (err) {\n        console.error('Failed to fetch geometry metrics:', err);\n        updateAutofillStatus('Failed to calculate area.', true);\n    }\n}\n\nfunction clearMetricsUI() {\n    const areaInput = document.getElementById('form-area');\n    const areaAuto = document.getElementById('area-auto');\n    const areaEquivalents = document.getElementById('area-equivalents');\n    const geometryMetrics = document.getElementById('geometry-metrics');\n\n    if (areaInput) areaInput.value = '';\n    if (areaAuto) areaAuto.textContent = '(Draw on map)';\n    if (areaEquivalents) {\n        areaEquivalents.classList.add('hidden');\n        areaEquivalents.innerHTML = '';\n    }\n    if (geometryMetrics) {\n        geometryMetrics.classList.add('hidden');\n    }\n}\n\nfunction clearGeometrySelection(clearAddRecordMap) {\n    if (drawnItems) {\n        drawnItems.clearLayers();\n    }\n    if (clearAddRecordMap && addRecordDrawnItems) {\n        addRecordDrawnItems.clearLayers();\n    }\n    currentSketchLayer = null;\n    const geomInput = document.getElementById('form-geometry');\n    if (geomInput) geomInput.value = '';\n\n    clearMetricsUI();\n}\n"
  },
  {
    "path": "static/js/modules/map_engine.js",
    "content": "/**\n * map_engine.js - Leaflet map initialization, layers, and drawing logic\n */\n\nfunction switchBaseLayer(layerName) {\n    if (!baseLayers[layerName] || layerName === currentBaseLayer) return;\n\n    map.removeLayer(baseLayers[currentBaseLayer]);\n    baseLayers[layerName].addTo(map);\n    currentBaseLayer = layerName;\n}\n\nasync function addIndiaMask(mapInstance) {\n    try {\n        const response = await fetch('/api/boundary');\n        if (!response.ok) return;\n        \n        const indiaGeoJSON = await response.json();\n        const worldBounds = [[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]];\n        let maskCoordinates = [worldBounds];\n        const geometry = indiaGeoJSON.features[0].geometry;\n        \n        if (geometry.type === 'MultiPolygon') {\n            geometry.coordinates.forEach(polygon => { maskCoordinates.push(polygon[0]); });\n        } else if (geometry.type === 'Polygon') {\n            maskCoordinates.push(geometry.coordinates[0]);\n        }\n        \n        const maskGeoJSON = { type: 'Feature', geometry: { type: 'Polygon', coordinates: maskCoordinates } };\n        const maskLayer = L.geoJSON(maskGeoJSON, {\n            style: { color: '#1f2937', weight: 2, fillColor: '#1f2937', fillOpacity: 0.7 },\n            interactive: false\n        });\n        maskLayer.addTo(mapInstance);\n        \n        const indiaBorder = L.geoJSON(indiaGeoJSON, {\n            style: { color: '#f97316', weight: 3, fillColor: 'transparent', fillOpacity: 0, opacity: 0.9 },\n            interactive: false\n        });\n        indiaBorder.addTo(mapInstance);\n    } catch (err) {\n        console.warn('Failed to load India boundary mask:', err);\n    }\n}\n\nfunction initMap(adminMode) {\n    isAdmin = adminMode || false;\n    const indiaBounds = L.latLngBounds(L.latLng(4.0, 60.0), L.latLng(40.0, 105.0));\n\n    map = L.map('map', {\n        center: [23.5, 77.5], zoom: 7, minZoom: 4, maxZoom: 18,\n        maxBounds: indiaBounds, maxBoundsViscosity: 1.0,\n        zoomControl: true, attributionControl: true\n    });\n\n    baseLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; OpenStreetMap contributors', maxZoom: 19, crossOrigin: true });\n    baseLayers.google = L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', { attribution: '&copy; Google Maps', maxZoom: 20, crossOrigin: true });\n    baseLayers.esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { attribution: '&copy; Esri', maxZoom: 18, crossOrigin: true });\n\n    baseLayers.osm.addTo(map);\n    currentBaseLayer = 'osm';\n\n    document.querySelectorAll('input[name=\"basemap\"]').forEach(radio => {\n        radio.addEventListener('change', function() { switchBaseLayer(this.value); });\n    });\n\n    addIndiaMask(map);\n    drawnItems = new L.FeatureGroup();\n    map.addLayer(drawnItems);\n\n    if (isAdmin) {\n        // map.pm.addControls is removed to use custom draw tools box\n        map.pm.setPathOptions({ color: '#ea580c', fillColor: '#fed7aa', fillOpacity: 0.3, weight: 3 });\n\n        map.on('pm:create', async function(e) {\n            const layer = e.layer;\n            drawnItems.clearLayers();\n            drawnItems.addLayer(layer);\n            currentSketchLayer = layer;\n            map.pm.disableDraw();\n            const geometry = layer.toGeoJSON().geometry;\n            await updateGeometryMetrics(geometry, { shouldRefreshMetrics: true });\n            switchMainTab('add-record');\n            switchFormTab('parcel');\n            const inst = document.getElementById('draw-instruction'); if (inst) inst.style.display = 'none';\n            showToast('Polygon ready. Review parcel details and save.', 'success');\n        });\n\n        map.on('pm:edit', async function(e) {\n            const layer = e.layer; if (!layer) return;\n            currentSketchLayer = layer;\n            const geometry = layer.toGeoJSON().geometry;\n            await updateGeometryMetrics(geometry, { shouldRefreshMetrics: true });\n        });\n\n        map.on('pm:remove', function() { clearGeometrySelection(false); });\n    }\n\n    map.on('mousemove', function(e) {\n        const coordsEl = document.getElementById('cursor-coords');\n        if (coordsEl) coordsEl.textContent = `Lat: ${e.latlng.lat.toFixed(6)}, Lng: ${e.latlng.lng.toFixed(6)}`;\n    });\n\n    if (isAdmin) {\n        map.on('moveend', function() { scheduleMapLocationLookup(map.getCenter().lat, map.getCenter().lng, false); });\n        map.on('click', function(e) {\n            if (map.pm && typeof map.pm.globalDrawModeEnabled === 'function' && map.pm.globalDrawModeEnabled()) return;\n            scheduleMapLocationLookup(e.latlng.lat, e.latlng.lng, true);\n        });\n    }\n    loadRecordsOnMap();\n}\n\nasync function loadRecordsOnMap() {\n    try {\n        const records = await fetchRecords();\n        allRecordsCache = records;\n        if (isAdmin) {\n            syncLocationCatalogWithRecords(records);\n            populateStateFilter(records);\n            populateDistrictFilter(records);\n            const { stateEl, districtEl, villageEl } = getFormElements();\n            refreshStateOptions(stateEl ? stateEl.value : '');\n            refreshDistrictOptions(districtEl ? districtEl.value : '');\n            refreshVillageOptions(villageEl ? villageEl.value : '');\n            applyAdminFilters(false);\n            return;\n        }\n        clearMapLayers();\n        addRecordsToMap(records);\n    } catch (err) {\n        console.error('Failed to load records:', err);\n        showToast('Failed to load land records.', 'error');\n    }\n}\n\nfunction clearMapLayers() {\n    parcelLayers.forEach(layer => { map.removeLayer(layer); });\n    parcelLayers = [];\n}\n\nfunction addRecordsToMap(records) {\n    records.forEach(record => {\n        if (!record.geometry || record.geometry.type !== 'Polygon') return;\n        const landUse = (record.attributes && record.attributes.land_use) || 'Agricultural';\n        const color = getLandUseColor(landUse);\n\n        const geoJsonLayer = L.geoJSON(record.geometry, {\n            style: { color: color, fillColor: color, fillOpacity: 0.25, weight: 2.5, opacity: 0.9 },\n            onEachFeature: function(_f, layer) {\n                layer.on('click', () => onParcelClick(record, layer));\n                layer.on('mouseover', function() { this.setStyle({ fillOpacity: 0.45, weight: 3.5 }); });\n                layer.on('mouseout', function() { this.setStyle({ fillOpacity: 0.25, weight: 2.5 }); });\n                const attrs = record.attributes || {};\n                const owner = record.owner || {};\n                const loc = record.location || {};\n                layer.bindTooltip(`<div class=\"tooltip-content\"><div class=\"tooltip-title\">${record.khasra_no || 'N/A'}</div><div class=\"tooltip-row\"><span class=\"tooltip-label\">ULPIN:</span><span class=\"tooltip-value\">${record.ulpin || 'N/A'}</span></div><div class=\"tooltip-row\"><span class=\"tooltip-label\">Land Use:</span><span class=\"tooltip-value\">${landUse}</span></div><div class=\"tooltip-row\"><span class=\"tooltip-label\">Area:</span><span class=\"tooltip-value\">${attrs.area_ha || '?'} Ha</span></div><div class=\"tooltip-divider\"></div><div class=\"tooltip-row\"><span class=\"tooltip-label\">Owner:</span><span class=\"tooltip-value\">${owner.name || 'N/A'}</span></div><div class=\"tooltip-row\"><span class=\"tooltip-label\">Location:</span><span class=\"tooltip-value\">${loc.village || '?'}, ${loc.district || '?'}</span></div><div class=\"tooltip-hint\">Click to view details →</div></div>`, { sticky: true, className: 'parcel-tooltip', direction: 'top', offset: [0, -10] });\n            }\n        });\n        geoJsonLayer.addTo(map);\n        geoJsonLayer._recordId = record._id;\n        parcelLayers.push(geoJsonLayer);\n    });\n    if (isAdmin) renderRecordsList(records);\n}\n\nfunction getLandUseColor(landUse) {\n    const colors = { Agricultural: '#22c55e', Residential: '#3b82f6', Commercial: '#f59e0b', Industrial: '#8b5cf6', Government: '#ef4444', Forest: '#065f46', Wasteland: '#9ca3af' };\n    return colors[landUse] || '#6b7280';\n}\n\nfunction onParcelClick(record) {\n    if (isAdmin) { showAdminDetails(record); flyToRecord(record); }\n    else if (typeof showViewerInfo === 'function') { showViewerInfo(record); }\n}\n\nfunction flyToRecord(record) {\n    if (!record.geometry) return;\n    const bounds = L.geoJSON(record.geometry).getBounds();\n    map.fitBounds(bounds, { padding: [60, 60], maxZoom: 16 });\n}\n\nfunction initViewRecordMap(record) {\n    const target = document.getElementById('view-record-map');\n    if (!target) return;\n    if (viewRecordMap) { viewRecordMap.remove(); viewRecordMap = null; }\n    const indiaBounds = L.latLngBounds(L.latLng(4.0, 60.0), L.latLng(40.0, 105.0));\n    viewRecordMap = L.map('view-record-map', { center: [23.5, 77.5], zoom: 7, minZoom: 4, maxZoom: 18, maxBounds: indiaBounds, maxBoundsViscosity: 1.0, zoomControl: true });\n    \n    const layers = {\n        osm: L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }),\n        google: L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', { maxZoom: 20 }),\n        esri: L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { maxZoom: 18 })\n    };\n    layers.osm.addTo(viewRecordMap);\n    let current = 'osm';\n\n    document.querySelectorAll('input[name=\"view-basemap\"]').forEach(radio => {\n        radio.addEventListener('change', function() {\n            if (layers[this.value] && this.value !== current) {\n                viewRecordMap.removeLayer(layers[current]);\n                layers[this.value].addTo(viewRecordMap);\n                current = this.value;\n            }\n        });\n    });\n\n    viewRecordMap.on('mousemove', e => {\n        const el = document.getElementById('view-cursor-coords');\n        if (el) el.textContent = `Lat: ${e.latlng.lat.toFixed(5)}, Lng: ${e.latlng.lng.toFixed(5)}`;\n    });\n\n    if (record.geometry) {\n        const lu = (record.attributes && record.attributes.land_use) || '';\n        const color = getLandUseColor(lu);\n        const layer = L.geoJSON(record.geometry, { style: { color: color || '#ea580c', fillColor: color || '#ea580c', fillOpacity: 0.35, weight: 3 } });\n        layer.addTo(viewRecordMap);\n        viewRecordMap.fitBounds(layer.getBounds(), { padding: [40, 40] });\n    }\n    setTimeout(() => { viewRecordMap.invalidateSize(); addIndiaMask(viewRecordMap); }, 200);\n}\n\nfunction initAddRecordMap() {\n    const target = document.getElementById('add-record-map');\n    if (!target) return;\n    const indiaBounds = L.latLngBounds(L.latLng(4.0, 60.0), L.latLng(40.0, 105.0));\n    addRecordMap = L.map('add-record-map', { center: [23.5, 77.5], zoom: 7, minZoom: 4, maxZoom: 18, maxBounds: indiaBounds, maxBoundsViscosity: 1.0, zoomControl: true });\n    \n    const layers = {\n        osm: L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }),\n        google: L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', { maxZoom: 20 }),\n        esri: L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { maxZoom: 18 })\n    };\n    layers.osm.addTo(addRecordMap);\n    document.querySelectorAll('input[name=\"add-basemap\"]').forEach(r => {\n        r.addEventListener('change', function() {\n            Object.values(layers).forEach(l => addRecordMap.removeLayer(l));\n            layers[this.value].addTo(addRecordMap);\n        });\n    });\n\n    addRecordDrawnItems = new L.FeatureGroup();\n    addRecordMap.addLayer(addRecordDrawnItems);\n    // map.pm.addControls is removed to use custom draw tools box\n    addRecordMap.pm.setPathOptions({ color: '#ea580c', fillColor: '#fed7aa', fillOpacity: 0.4, weight: 3 });\n\n    addRecordMap.on('pm:create', async e => {\n        addRecordDrawnItems.clearLayers();\n        const layer = e.layer;\n        addRecordDrawnItems.addLayer(layer);\n        currentSketchLayer = layer;\n        if (layer.pm) {\n            layer.pm.enable();\n        }\n        const geometry = layer.toGeoJSON().geometry;\n        await updateGeometryMetricsForAddRecord(geometry);\n        if (typeof scheduleMapLocationLookup === 'function') {\n            const centroid = layer.getBounds().getCenter();\n            scheduleMapLocationLookup(centroid.lat, centroid.lng, true);\n        }\n    });\n\n    addRecordMap.on('pm:edit', async e => {\n        currentSketchLayer = e.layer;\n        await updateGeometryMetricsForAddRecord(e.layer.toGeoJSON().geometry);\n    });\n\n    addRecordMap.on('moveend', function() {\n        if (typeof scheduleMapLocationLookup === 'function') {\n            scheduleMapLocationLookup(addRecordMap.getCenter().lat, addRecordMap.getCenter().lng, false);\n        }\n    });\n    \n    addRecordMap.on('click', function(e) {\n        if (addRecordMap.pm && typeof addRecordMap.pm.globalDrawModeEnabled === 'function' && addRecordMap.pm.globalDrawModeEnabled()) return;\n        if (typeof scheduleMapLocationLookup === 'function') {\n            scheduleMapLocationLookup(e.latlng.lat, e.latlng.lng, true);\n        }\n    });\n\n    const startDrawBtn = document.getElementById('btn-start-draw-new');\n    if (startDrawBtn) {\n        startDrawBtn.addEventListener('click', function() {\n            addRecordDrawnItems.clearLayers();\n            const formGeom = document.getElementById('form-geometry');\n            if (formGeom) formGeom.value = '';\n            if (typeof clearMetricsUI === 'function') clearMetricsUI();\n            addRecordMap.pm.enableDraw('Polygon', { snappable: true, snapDistance: 20, continueDrawing: false, allowSelfIntersection: false, finishOn: 'dblclick' });\n            const drawStatus = document.getElementById('draw-status');\n            if (drawStatus) {\n                drawStatus.innerHTML = '<strong>Drawing mode active.</strong> Click to add points. Double-click to finish.';\n                drawStatus.className = 'bg-yellow-50 border-l-4 border-yellow-500 rounded-r-lg p-3 text-sm text-yellow-800';\n            }\n        });\n    }\n\n    const finishDrawBtn = document.getElementById('btn-finish-draw-new');\n    if (finishDrawBtn) {\n        finishDrawBtn.addEventListener('click', function() {\n            addRecordMap.pm.disableDraw();\n        });\n    }\n\n    const cancelDrawBtn = document.getElementById('btn-cancel-draw-new');\n    if (cancelDrawBtn) {\n        cancelDrawBtn.addEventListener('click', function() {\n            addRecordMap.pm.disableDraw();\n            addRecordDrawnItems.clearLayers();\n            currentSketchLayer = null;\n            const formGeom = document.getElementById('form-geometry');\n            if (formGeom) formGeom.value = '';\n            if (typeof clearMetricsUI === 'function') clearMetricsUI();\n            const drawStatus = document.getElementById('draw-status');\n            if (drawStatus) {\n                drawStatus.innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\n                drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\n            }\n        });\n    }\n\n    const clearDrawBtn = document.getElementById('btn-clear-draw-new');\n    if (clearDrawBtn) {\n        clearDrawBtn.addEventListener('click', function() {\n            addRecordDrawnItems.clearLayers();\n            currentSketchLayer = null;\n            const formGeom = document.getElementById('form-geometry');\n            if (formGeom) formGeom.value = '';\n            if (typeof clearMetricsUI === 'function') clearMetricsUI();\n            const drawStatus = document.getElementById('draw-status');\n            if (drawStatus) {\n                drawStatus.innerHTML = '<strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.';\n                drawStatus.className = 'bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800';\n            }\n        });\n    }\n\n    setTimeout(() => { addRecordMap.invalidateSize(); addIndiaMask(addRecordMap); }, 200);\n}\n"
  },
  {
    "path": "static/js/modules/records.js",
    "content": "/**\n * records.js - Records list and filtering logic\n */\n\nfunction ensureLocationInCatalog(state, district, village) {\n    if (!state || !district || !village) return;\n\n    if (!locationCatalog[state]) {\n        locationCatalog[state] = {};\n    }\n    if (!locationCatalog[state][district]) {\n        locationCatalog[state][district] = [];\n    }\n    if (!locationCatalog[state][district].includes(village)) {\n        locationCatalog[state][district].push(village);\n        locationCatalog[state][district] = sortedValues(locationCatalog[state][district]);\n    }\n}\n\nfunction syncLocationCatalogWithRecords(records) {\n    records.forEach(rec => {\n        const loc = rec.location || {};\n        ensureLocationInCatalog(loc.state, loc.district, loc.village);\n    });\n}\n\nfunction populateStateFilter(records) {\n    const stateFilterEl = document.getElementById('state-filter');\n    if (!stateFilterEl) return;\n\n    const selected = stateFilterEl.value || '';\n    const states = sortedValues(records\n        .map(rec => (rec.location && rec.location.state) || '')\n        .filter(Boolean));\n\n    stateFilterEl.innerHTML = '<option value=\"\">All States</option>';\n    states.forEach(state => {\n        const option = document.createElement('option');\n        option.value = state;\n        option.textContent = state;\n        stateFilterEl.appendChild(option);\n    });\n\n    if (selected && states.includes(selected)) {\n        stateFilterEl.value = selected;\n    }\n}\n\nfunction populateDistrictFilter(records) {\n    const districtFilterEl = document.getElementById('district-filter');\n    if (!districtFilterEl) return;\n\n    const selected = districtFilterEl.value || '';\n    const districts = sortedValues(records\n        .map(rec => (rec.location && rec.location.district) || '')\n        .filter(Boolean));\n\n    districtFilterEl.innerHTML = '<option value=\"\">All Districts</option>';\n    districts.forEach(district => {\n        const option = document.createElement('option');\n        option.value = district;\n        option.textContent = district;\n        districtFilterEl.appendChild(option);\n    });\n\n    if (selected && districts.includes(selected)) {\n        districtFilterEl.value = selected;\n    }\n}\n\nfunction populateVillageFilter(records) {\n    const villageFilterEl = document.getElementById('village-filter');\n    if (!villageFilterEl) return;\n\n    const selected = villageFilterEl.value || '';\n    const villages = sortedValues(records\n        .map(rec => (rec.location && rec.location.village) || '')\n        .filter(Boolean));\n\n    villageFilterEl.innerHTML = '<option value=\"\">All Villages</option>';\n    villages.forEach(v => {\n        const option = document.createElement('option');\n        option.value = v;\n        option.textContent = v;\n        villageFilterEl.appendChild(option);\n    });\n\n    if (selected && villages.includes(selected)) {\n        villageFilterEl.value = selected;\n    }\n}\n\nfunction getAdminFilterState() {\n    const query = (document.getElementById('record-search') || {}).value || '';\n    const landUse = (document.getElementById('land-use-filter') || {}).value || '';\n    const state = (document.getElementById('state-filter') || {}).value || '';\n    const district = (document.getElementById('district-filter') || {}).value || '';\n    const village = (document.getElementById('village-filter') || {}).value || '';\n\n    return {\n        query: query.trim().toLowerCase(),\n        landUse,\n        state,\n        district,\n        village\n    };\n}\n\nfunction initializeRecordFilters() {\n    const filters = ['state-filter', 'district-filter', 'village-filter', 'land-use-filter'];\n    filters.forEach(id => {\n        const el = document.getElementById(id);\n        if (el) el.addEventListener('change', () => applyAdminFilters(true));\n    });\n\n    const searchInput = document.getElementById('record-search');\n    if (searchInput) {\n        searchInput.addEventListener('input', debounce(() => applyAdminFilters(true), 400));\n    }\n\n    const clearBtn = document.getElementById('btn-clear-filters');\n    if (clearBtn) {\n        clearBtn.addEventListener('click', () => {\n            filters.forEach(id => {\n                const el = document.getElementById(id);\n                if (el) el.value = '';\n            });\n            if (searchInput) searchInput.value = '';\n            applyAdminFilters(true);\n        });\n    }\n}\n\nfunction filterRecordsByState(records, filterState) {\n    return records.filter(rec => {\n        const attrs = rec.attributes || {};\n        const loc = rec.location || {};\n        const owner = rec.owner || {};\n        const searchText = [\n            rec.khasra_no,\n            rec.ulpin,\n            rec.khata_no,\n            loc.village,\n            loc.district,\n            loc.state,\n            owner.name,\n            attrs.land_use\n        ].join(' ').toLowerCase();\n\n        const queryMatch = !filterState.query || searchText.includes(filterState.query);\n        const landUseMatch = !filterState.landUse || attrs.land_use === filterState.landUse;\n        const stateMatch = !filterState.state || loc.state === filterState.state;\n        const districtMatch = !filterState.district || loc.district === filterState.district;\n\n        return queryMatch && landUseMatch && stateMatch && districtMatch;\n    });\n}\n\nfunction switchRecordsView(mode) {\n    const cardsContainer = document.getElementById('records-list-cards');\n    const tableContainer = document.getElementById('records-list-table');\n\n    if (!cardsContainer || !tableContainer) return;\n\n    recordsViewMode = mode === 'table' ? 'table' : 'cards';\n\n    cardsContainer.classList.toggle('hidden', recordsViewMode !== 'cards');\n    tableContainer.classList.toggle('hidden', recordsViewMode !== 'table');\n\n    document.querySelectorAll('.records-view-tab').forEach(tab => {\n        tab.classList.toggle('active', tab.dataset.view === recordsViewMode);\n    });\n}\n\nfunction renderRecordsList(records) {\n    const cardsEl = document.getElementById('records-list-cards');\n    const tableEl = document.getElementById('records-list-table');\n    const noRecordsEl = document.getElementById('no-records');\n    const countLabel = document.getElementById('records-count-label');\n\n    if (!cardsEl || !tableEl) return;\n\n    if (countLabel) {\n        countLabel.textContent = String(records.length);\n    }\n\n    if (records.length === 0) {\n        cardsEl.innerHTML = '';\n        tableEl.innerHTML = '';\n        if (noRecordsEl) noRecordsEl.classList.remove('hidden');\n        return;\n    }\n\n    if (noRecordsEl) noRecordsEl.classList.add('hidden');\n\n    cardsEl.innerHTML = records.map(rec => {\n        const attrs = rec.attributes || {};\n        const owner = rec.owner || {};\n        const loc = rec.location || {};\n        const landUse = attrs.land_use || 'Unknown';\n        const badgeClass = 'badge-' + landUse.toLowerCase();\n\n        return `\n            <div class=\"record-card fade-in ${selectedRecordId === rec._id ? 'active' : ''}\"\n                 data-id=\"${rec._id}\">\n                <div class=\"flex items-center justify-between mb-1\">\n                    <div class=\"flex items-center gap-2\">\n                        <span class=\"font-semibold text-inherit text-sm\">${rec.khasra_no || 'N/A'}</span>\n                        ${rec.deleted ? `<span class=\"badge-deleted\">Deleted</span>` : ''}\n                    </div>\n                    <span class=\"land-use-badge ${badgeClass}\">${landUse}</span>\n                </div>\n                <div class=\"text-xs text-inherit opacity-70 space-y-0.5\">\n                    <div>ULPIN: <span class=\"font-mono\">${rec.ulpin || 'N/A'}</span></div>\n                    <div>${owner.name || 'No Owner'} | ${attrs.area_ha || '?'} Ha</div>\n                    <div>${loc.village || ''}, ${loc.district || ''}</div>\n                </div>\n                <div class=\"flex items-center justify-between mt-2\">\n                    <div class=\"text-xs text-inherit opacity-50\">\n                        <div>${loc.state || ''} • ${loc.district || ''}</div>\n                    </div>\n                    <button type=\"button\" class=\"view-record-btn bg-orange-600 hover:bg-orange-700 text-white text-xs px-4 py-1.5 rounded-lg transition font-medium\">\n                        View\n                    </button>\n                </div>\n            </div>\n        `;\n    }).join('');\n\n    // Attach click handlers for view buttons\n    cardsEl.querySelectorAll('.view-record-btn').forEach((btn, index) => {\n        btn.addEventListener('click', function(e) {\n            e.stopPropagation();\n            const card = this.closest('.record-card');\n            if (card) {\n                viewRecordDetails(records[index]._id);\n            }\n        });\n    });\n\n    tableEl.innerHTML = records.map((rec, index) => {\n        const attrs = rec.attributes || {};\n        const loc = rec.location || {};\n        const landUse = attrs.land_use || 'Unknown';\n        const recordValue = calculateValuation(attrs.area_ha, attrs.circle_rate_inr, landUse);\n\n        return `\n            <div class=\"record-table-row ${selectedRecordId === rec._id ? 'active' : ''} ${rec.deleted ? 'deleted' : ''}\" data-id=\"${rec._id}\">\n                <div class=\"flex items-center justify-between gap-2\">\n                    <div class=\"min-w-0\">\n                        <p class=\"text-sm font-semibold text-inherit truncate\">${escapeHtml(rec.khasra_no || 'N/A')} ${rec.deleted ? '<span class=\"badge-deleted ml-2\">Deleted</span>' : ''}</p>\n                        <p class=\"text-[11px] text-inherit opacity-70 truncate\">${escapeHtml(rec.ulpin || 'N/A')} | ${escapeHtml(loc.district || 'N/A')}</p>\n                    </div>\n                    <button type=\"button\" class=\"view-record-btn-table bg-orange-600 hover:bg-orange-700 text-white text-[11px] px-2 py-1 rounded\">View</button>\n                </div>\n                <div class=\"mt-2 grid grid-cols-3 gap-2 text-[11px] text-inherit opacity-60\">\n                    <span>${escapeHtml(landUse)}</span>\n                    <span>${asNumber(attrs.area_ha).toFixed(2)} Ha</span>\n                    <span>Rs. ${formatInr(recordValue)}</span>\n                </div>\n            </div>\n        `;\n    }).join('');\n\n    // Attach click handlers for table view buttons\n    tableEl.querySelectorAll('.view-record-btn-table').forEach((btn, index) => {\n        btn.addEventListener('click', function(e) {\n            e.stopPropagation();\n            const row = this.closest('.record-table-row');\n            if (row) {\n                viewRecordDetails(records[index]._id);\n            }\n        });\n    });\n\n    switchRecordsView(recordsViewMode);\n}\n\nfunction applyAdminFilters(notify) {\n    if (!isAdmin) return;\n\n    const filterState = getAdminFilterState();\n    \n    // Use server-side filtering\n    fetchFilteredRecords({\n        state: filterState.query ? '' : filterState.state,\n        district: filterState.query ? '' : filterState.district,\n        village: filterState.query ? '' : filterState.village,\n        land_use: filterState.landUse,\n        search: filterState.query\n    }).then(filtered => {\n        filteredRecordsCache = filtered;\n        \n        // Update map\n        clearMapLayers();\n        addRecordsToMap(filtered);\n        \n        // Update records list\n        renderRecordsList(filtered);\n        \n        // Update dashboard from server\n        fetchDashboardAnalytics({\n            state: filterState.state,\n            land_use: filterState.landUse,\n            district: filterState.district,\n            search: filterState.query\n        }).then(analytics => {\n            renderKpiCardsFromServer(analytics.kpis);\n            renderLandUseDistributionFromServer(analytics.land_use_distribution);\n            renderDistrictOverviewFromServer(analytics.district_overview);\n            renderTopValueParcelFromServer(analytics.top_parcel);\n            renderRecentMutationsFromServer(analytics.recent_mutations);\n        }).catch(err => {\n            console.error('Dashboard analytics failed:', err);\n        });\n\n        if (notify) {\n            showToast(`Showing ${filtered.length} record(s).`, 'info');\n        }\n    }).catch(err => {\n        console.error('Filter failed:', err);\n        showToast('Failed to filter records.', 'error');\n    });\n}\n"
  },
  {
    "path": "static/js/modules/state.js",
    "content": "/**\n * state.js - Global state for India LIMS\n */\n\nlet map = null;\nlet viewRecordMap = null;\nlet addRecordMap = null;\nlet addRecordDrawnItems = null;\nlet baseLayers = {};\nlet currentBaseLayer = null;\nlet parcelLayers = [];\nlet drawnItems = null;\nlet currentSketchLayer = null;\nlet isAdmin = false;\nlet selectedRecordId = null;\nlet selectedRecord = null;\nlet reverseGeocodeTimer = null;\nlet lastGeocodeKey = '';\nlet lastAutoLocation = { state: '', district: '', village: '' };\nlet lastGpsDetectedLocation = { state: '', district: '', village: '' };\nlet allRecordsCache = [];\nlet filteredRecordsCache = [];\nlet recordsViewMode = 'cards';\nlet locationCatalog = {};\nlet currentProfile = null;\nlet allUsers = [];\nlet sessionUsername = '';\n\nconst DEFAULT_SNAP_DISTANCE = 20;\n"
  },
  {
    "path": "static/js/modules/utils.js",
    "content": "/**\n * utils.js - General utility functions for India LIMS\n */\n\nfunction showConfirmModal(message, onConfirm) {\n    const overlay = document.createElement('div');\n    overlay.className = 'fixed inset-0 bg-black bg-opacity-50 z-[9999] flex items-center justify-center p-4';\n    overlay.style.backdropFilter = 'blur(2px)';\n    \n    const modal = document.createElement('div');\n    modal.className = 'bg-white rounded-lg shadow-xl max-w-sm w-full overflow-hidden fade-in';\n    \n    const content = document.createElement('div');\n    content.className = 'p-6';\n    \n    const iconContainer = document.createElement('div');\n    iconContainer.className = 'mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4';\n    iconContainer.innerHTML = '<svg class=\"h-6 w-6 text-red-600\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>';\n    \n    const textContainer = document.createElement('div');\n    textContainer.className = 'text-center';\n    \n    const title = document.createElement('h3');\n    title.className = 'text-lg leading-6 font-medium text-gray-900 mb-2';\n    title.textContent = 'Confirm Action';\n    \n    const messageEl = document.createElement('p');\n    messageEl.className = 'text-sm text-gray-500 whitespace-pre-line';\n    messageEl.textContent = message;\n    \n    textContainer.appendChild(title);\n    textContainer.appendChild(messageEl);\n    content.appendChild(iconContainer);\n    content.appendChild(textContainer);\n    \n    const buttonsContainer = document.createElement('div');\n    buttonsContainer.className = 'bg-gray-50 px-4 py-3 sm:px-6 flex flex-row-reverse gap-2';\n    \n    const confirmBtn = document.createElement('button');\n    confirmBtn.className = 'w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none sm:w-auto sm:text-sm transition-colors cursor-pointer';\n    confirmBtn.textContent = 'Confirm';\n    \n    const cancelBtn = document.createElement('button');\n    cancelBtn.className = 'w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none sm:w-auto sm:text-sm transition-colors cursor-pointer';\n    cancelBtn.textContent = 'Cancel';\n    \n    buttonsContainer.appendChild(confirmBtn);\n    buttonsContainer.appendChild(cancelBtn);\n    modal.appendChild(content);\n    modal.appendChild(buttonsContainer);\n    overlay.appendChild(modal);\n    document.body.appendChild(overlay);\n    \n    const close = () => document.body.removeChild(overlay);\n    confirmBtn.addEventListener('click', () => { close(); if (onConfirm) onConfirm(); });\n    cancelBtn.addEventListener('click', close);\n    overlay.addEventListener('click', (e) => { if (e.target === overlay) close(); });\n}\n\nfunction sortedValues(values) {\n    return [...new Set(values)].sort((a, b) => a.localeCompare(b));\n}\n\nfunction asNumber(value) {\n    const parsed = parseFloat(value);\n    return Number.isFinite(parsed) ? parsed : 0;\n}\n\nfunction formatInr(value) {\n    return Math.round(value).toLocaleString('en-IN');\n}\n\nfunction escapeHtml(value) {\n    return String(value || '')\n        .replace(/&/g, '&amp;')\n        .replace(/</g, '&lt;')\n        .replace(/>/g, '&gt;')\n        .replace(/\"/g, '&quot;')\n        .replace(/'/g, '&#039;');\n}\n\nfunction debounce(fn, wait = 200) {\n    let t = null;\n    return function(...args) {\n        const ctx = this;\n        clearTimeout(t);\n        t = setTimeout(() => fn.apply(ctx, args), wait);\n    };\n}\n\nfunction updateAutofillStatus(message, isError) {\n    const statusEl = document.getElementById('map-autofill-status');\n    if (!statusEl) return;\n\n    statusEl.textContent = message || '';\n    statusEl.classList.toggle('text-red-600', !!isError);\n    statusEl.classList.toggle('text-gray-500', !isError);\n}\n\nfunction fileToBase64(file) {\n    return new Promise((resolve, reject) => {\n        const reader = new FileReader();\n        reader.readAsDataURL(file);\n        reader.onload = () => resolve(reader.result);\n        reader.onerror = error => reject(error);\n    });\n}\nfunction calculateValuation(areaHa, circleRateInr, landUse) {\n    const multipliers = {\n        'Commercial': 2.5,\n        'Industrial': 1.8,\n        'Residential': 1.5,\n        'Agricultural': 1.0,\n        'Government': 1.2,\n        'Forest': 0.8,\n        'Wasteland': 0.5\n    };\n    const multiplier = multipliers[landUse] || 1.0;\n    return asNumber(areaHa) * asNumber(circleRateInr) * multiplier;\n}\n"
  },
  {
    "path": "templates/admin_dashboard.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>LIMS - Admin Dashboard</title>\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n    <script>\n        if (window.tailwind) {\n            tailwind.config = {\n                darkMode: 'class',\n                theme: {\n                    extend: {\n                        colors: {\n                            orange: {\n                                50: '#fff7ed',\n                                100: '#ffedd5',\n                                500: '#f97316',\n                                600: '#ea580c',\n                                700: '#c2410c',\n                            }\n                        }\n                    }\n                }\n            }\n        }\n    </script>\n    <script>\n        // Global Dark Mode Controller\n        function toggleLimsTheme() {\n            const isDark = document.documentElement.classList.toggle('dark');\n            const sunIcon = document.getElementById('sun-icon');\n            const moonIcon = document.getElementById('moon-icon');\n            \n            if (isDark) {\n                if (sunIcon) sunIcon.classList.remove('hidden');\n                if (moonIcon) moonIcon.classList.add('hidden');\n            } else {\n                if (sunIcon) sunIcon.classList.add('hidden');\n                if (moonIcon) moonIcon.classList.remove('hidden');\n            }\n            localStorage.setItem('lims-theme', isDark ? 'dark' : 'light');\n            console.log('LIMS Theme toggled to:', isDark ? 'DARK' : 'LIGHT');\n        }\n\n        // Apply theme immediately\n        (function() {\n            const savedTheme = localStorage.getItem('lims-theme');\n            const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n            if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) {\n                document.documentElement.classList.add('dark');\n            }\n        })();\n\n        // Sync icons once DOM is ready\n        document.addEventListener('DOMContentLoaded', () => {\n            const isDark = document.documentElement.classList.contains('dark');\n            const sunIcon = document.getElementById('sun-icon');\n            const moonIcon = document.getElementById('moon-icon');\n            if (isDark) {\n                if (sunIcon) sunIcon.classList.remove('hidden');\n                if (moonIcon) moonIcon.classList.add('hidden');\n            } else {\n                if (sunIcon) sunIcon.classList.add('hidden');\n                if (moonIcon) moonIcon.classList.remove('hidden');\n            }\n        });\n    </script>\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\" />\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/@geoman-io/leaflet-geoman-free@2.17.0/dist/leaflet-geoman.css\" />\n    <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/style.css') }}\" />\n    <style>\n        /* Dark Mode Transitions */\n        .dark-mode-transition * { transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease; }\n        \n        /* Dark Mode Overrides */\n        .dark #admin-sidebar, .dark .bg-white, .dark .main-tab-panel {\n            background-color: #1e293b !important; /* slate-800 */\n            color: #f1f5f9 !important;\n            border-color: #334155 !important;\n        }\n        .dark .bg-gray-50, .dark .bg-gray-100 {\n            background-color: #0f172a !important; /* slate-900 */\n        }\n        .dark .text-gray-700, .dark .text-gray-800, .dark .text-gray-600 {\n            color: #e2e8f0 !important;\n        }\n        .dark .border-gray-200, .dark .border-gray-100, .dark .border-b {\n            border-color: #334155 !important;\n        }\n        .dark input, .dark select, .dark textarea {\n            background-color: #334155 !important;\n            color: white !important;\n            border-color: #475569 !important;\n        }\n        .dark .main-tab-btn {\n            color: #94a3b8 !important;\n        }\n        .dark .main-tab-btn:hover {\n            background-color: #334155 !important;\n            color: white !important;\n        }\n        .dark .main-tab-btn.active {\n            background-color: #f97316 !important; /* orange-500 */\n            color: white !important;\n        }\n        .dark .bg-blue-50, .dark .bg-orange-50, .dark .bg-green-50, .dark .bg-purple-50 {\n            background-color: #1e293b !important;\n            border-color: #334155 !important;\n        }\n        .dark .bg-orange-100, .dark .bg-blue-100, .dark .bg-green-100, .dark .bg-purple-100 {\n            background-color: #334155 !important; /* Muted icon background */\n        }\n        /* Gradient Fixes for Dark Mode */\n        .dark .from-green-50, .dark .to-green-100, .dark .from-blue-50, .dark .to-blue-100 {\n            --tw-gradient-from: #1e293b !important;\n            --tw-gradient-to: #0f172a !important;\n            background-color: #1e293b !important;\n        }\n        \n        /* Empty State & Chart Polish */\n        .dark #dashboard-top-parcel, .dark #dashboard-mutations > div, .dark .bg-white, .dark .record-card, .dark .record-table-row, .dark .records-header {\n            background-color: #1e293b !important;\n            color: #f1f5f9 !important;\n            border-color: #334155 !important;\n        }\n        .dark .record-card:hover, .dark .record-table-row:hover {\n            background-color: #334155 !important;\n        }\n        .dark .record-card.active, .dark .record-table-row.active {\n            background-color: rgba(249, 115, 22, 0.15) !important;\n            border-color: #f97316 !important;\n        }\n        .dark .records-view-tab {\n            background-color: #334155 !important;\n            color: #94a3b8 !important;\n            border-color: #475569 !important;\n        }\n        .dark .records-view-tab.active {\n            background-color: #f97316 !important;\n            color: white !important;\n        }\n        .dark #dashboard-land-use div { color: #cbd5e1 !important; }\n        .dark .legend-text { color: #f1f5f9 !important; }\n\n        /* Comprehensive Text Polish */\n        .dark .text-gray-900, .dark .text-slate-900 { color: #f8fafc !important; }\n        .dark .text-gray-800, .dark .text-slate-800 { color: #f1f5f9 !important; }\n        .dark .text-gray-700, .dark .text-slate-700 { color: #e2e8f0 !important; }\n        .dark .text-gray-600, .dark .text-slate-600, .dark .text-gray-500, .dark .text-slate-500 { color: #cbd5e1 !important; }\n        .dark .text-gray-400, .dark .text-slate-400 { color: #94a3b8 !important; }\n\n        /* Backgrounds & Borders */\n        .dark .bg-gray-200, .dark .bg-slate-200 { background-color: #334155 !important; }\n        .dark .bg-gray-300, .dark .bg-slate-300 { background-color: #475569 !important; }\n        .dark .border-gray-300, .dark .border-gray-200, .dark .divide-gray-100, .dark .divide-y > * {\n            border-color: #334155 !important;\n        }\n        /* Badges Dark Mode */\n        .dark .badge-agricultural { background-color: #064e3b !important; color: #6ee7b7 !important; }\n        .dark .badge-residential { background-color: #1e3a8a !important; color: #93c5fd !important; }\n        .dark .badge-commercial { background-color: #78350f !important; color: #fcd34d !important; }\n        .dark .badge-industrial { background-color: #4c1d95 !important; color: #c4b5fd !important; }\n        .dark .badge-government { background-color: #7f1d1d !important; color: #fca5a5 !important; }\n        .dark .badge-forest { background-color: #064e3b !important; color: #34d399 !important; }\n        .dark .badge-wasteland { background-color: #374151 !important; color: #d1d5db !important; }\n        .dark .badge-deleted { background-color: #7f1d1d !important; color: #fca5a5 !important; }\n        \n        /* Toolbar and list headers */\n        .dark .records-header, .dark .bg-white.border-b {\n            background-color: #0f172a !important; /* slate-900 for toolbars */\n            border-color: #334155 !important;\n        }\n\n        /* Map Dark Mode - CSS Filter Trick */\n        .dark #map, .dark #add-record-map, .dark #view-record-map, .dark .leaflet-container {\n            filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);\n        }\n        .dark .leaflet-tile-pane { opacity: 0.8; }\n        .dark .leaflet-control-zoom a, .dark .leaflet-control-layers, .dark .leaflet-control-attribution {\n            filter: invert(100%) hue-rotate(180deg);\n        }\n\n        /* Map Popups */\n        .dark .leaflet-popup-content-wrapper, .dark .leaflet-popup-tip {\n            background-color: #1e293b !important;\n            color: #f1f5f9 !important;\n            border: 1px solid #334155;\n        }\n\n        .dark input::placeholder {\n            color: #64748b !important;\n        }\n        /* Dark Scrollbar */\n        .dark ::-webkit-scrollbar { width: 8px; height: 8px; }\n        .dark ::-webkit-scrollbar-track { background: #0f172a; }\n        .dark ::-webkit-scrollbar-thumb { background: #334155; border-radius: 4px; }\n        .dark ::-webkit-scrollbar-thumb:hover { background: #475569; }\n    </style>\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏛️</text></svg>\">\n</head>\n<body class=\"bg-gray-100 h-[100dvh] flex flex-col overflow-hidden\">\n\n    <!-- Top Navigation Bar -->\n    <nav class=\"bg-gradient-to-r from-orange-600 to-orange-700 shadow-lg z-50 flex-shrink-0\">\n        <div class=\"flex items-center justify-between px-3 sm:px-6 py-2 sm:py-3\">\n            <div class=\"flex items-center gap-2 sm:gap-3\">\n                <!-- Mobile: Sidebar Toggle Button -->\n                <button id=\"btn-toggle-sidebar\" class=\"md:hidden text-white hover:bg-white/20 transition p-2 rounded-lg\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"/></svg>\n                </button>\n                <div class=\"w-7 h-7 sm:w-10 sm:h-10 bg-white rounded-lg flex items-center justify-center shadow-md\">\n                    <svg class=\"w-5 h-5 sm:w-6 sm:h-6 text-orange-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/>\n                    </svg>\n                </div>\n                <div>\n                    <h1 class=\"text-lg sm:text-xl font-bold text-white\">LIMS</h1>\n                    <p class=\"text-[10px] sm:text-xs text-orange-100 hidden sm:block\">Land Information Management System</p>\n                </div>\n            </div>\n            <div class=\"flex items-center gap-2 sm:gap-4\">\n                <div id=\"real-time-clock\" class=\"text-xs sm:text-sm font-medium text-white/90 bg-black/20 px-2 sm:px-3 py-1.5 rounded-lg flex flex-col justify-center items-center hidden sm:flex\">\n                    <span id=\"rtc-time\" class=\"block font-bold\">--:--:--</span>\n                    <span id=\"rtc-date\" class=\"text-[9px] sm:text-[10px] uppercase tracking-wider\">--/--/----</span>\n                </div>\n                <button id=\"btn-toggle-darkmode\" onclick=\"toggleLimsTheme()\" class=\"text-white/80 hover:text-white hover:bg-white/20 transition p-2 rounded-lg\" title=\"Toggle Dark Mode\">\n                    <svg id=\"sun-icon\" class=\"w-5 h-5 hidden\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707m12.728 0l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707-.707M12 8a4 4 0 110 8 4 4 0 010-8z\"/></svg>\n                    <svg id=\"moon-icon\" class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z\"/></svg>\n                </button>\n                <div class=\"flex items-center gap-2 bg-white/20 backdrop-blur px-2 sm:px-4 py-1.5 sm:py-2 rounded-lg\">\n                    <div class=\"w-1.5 h-1.5 bg-green-400 rounded-full animate-pulse\"></div>\n                    <span class=\"text-xs sm:text-sm font-medium text-white truncate max-w-[60px] sm:max-w-none\">{{ username }}</span>\n                    <span class=\"hidden xs:inline-block text-[10px] sm:text-xs bg-white/30 text-white px-2 py-0.5 rounded-full font-semibold\">ADMIN</span>\n                </div>\n            </div>\n        </div>\n    </nav>\n\n    <!-- Dark Mode Support Script (Independent) -->\n    <script>\n        (function() {\n            const savedTheme = localStorage.getItem('lims-theme');\n            const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;\n            if (savedTheme === 'dark' || (!savedTheme && systemPrefersDark)) {\n                document.documentElement.classList.add('dark');\n            }\n        })();\n    </script>\n\n    <!-- Main Layout: Left Sidebar + Content -->\n    <div class=\"flex-1 flex overflow-hidden relative\">\n        \n        <!-- Mobile Sidebar Overlay -->\n        <div id=\"sidebar-overlay\" class=\"fixed inset-0 bg-black/50 z-30 hidden md:hidden\"></div>\n\n        <!-- Left Sidebar Navigation -->\n        <div id=\"admin-sidebar\" class=\"absolute md:relative w-56 bg-white shadow-lg z-40 h-full flex flex-col transform -translate-x-full md:translate-x-0 transition-transform duration-300 ease-in-out\">\n            <div class=\"py-4 px-2 space-y-1 flex-1 overflow-y-auto\">\n                <button class=\"main-tab-btn active\" data-tab=\"dashboard\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"/></svg>\n                    <span>Dashboard</span>\n                </button>\n                <button class=\"main-tab-btn\" data-tab=\"records\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"/></svg>\n                    <span>Records</span>\n                </button>\n                <button class=\"main-tab-btn\" data-tab=\"map\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7\"/></svg>\n                    <span>Map View</span>\n                </button>\n                <button class=\"main-tab-btn\" data-tab=\"add-record\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                    <span>Add Record</span>\n                </button>\n                <button class=\"main-tab-btn\" data-tab=\"reports\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"/></svg>\n                    <span>Reports & Feedback</span>\n                </button>\n            </div>\n\n            <!-- Bottom Section: Profile & Logout -->\n            <div class=\"border-t pb-safe mt-auto\">\n                <div class=\"p-2 space-y-1 mb-[env(safe-area-inset-bottom,20px)] md:mb-0\">\n                    <button id=\"btn-profile\" class=\"main-tab-btn\" data-tab=\"profile\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"/></svg>\n                        <span>Profile</span>\n                    </button>\n                    <button id=\"btn-users\" class=\"main-tab-btn\" data-tab=\"users\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\"/></svg>\n                        <span>User Management</span>\n                    </button>\n                    <button id=\"btn-audit\" class=\"main-tab-btn\" data-tab=\"audit\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 7h18M3 12h18M3 17h18\"/></svg>\n                        <span>Audit Log</span>\n                    </button>\n                    <button id=\"btn-logout\" class=\"w-full flex items-center gap-3 px-4 py-3 text-red-600 hover:bg-red-50 transition rounded-lg border-l-4 border-transparent hover:border-red-600 pb-6 md:pb-3\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1\"/></svg>\n                        <span>Logout</span>\n                    </button>\n                </div>\n            </div>\n        </div>\n\n        <!-- Main Content Area -->\n        <div class=\"flex-1 overflow-hidden bg-gray-100 flex flex-col relative w-full\">\n\n            <!-- Records Tab -->\n            <div id=\"main-tab-records\" class=\"main-tab-panel hidden h-full flex flex-col\">\n                <!-- Toolbar -->\n                <div class=\"bg-white border-b px-2 sm:px-4 py-2 sm:py-3 flex flex-wrap items-center gap-2 sm:gap-3 flex-shrink-0\">\n                    <div class=\"flex w-full sm:w-auto gap-2\">\n                        <select id=\"state-filter\" class=\"flex-1 sm:flex-none px-2 sm:px-3 py-1.5 sm:py-2 border border-gray-300 rounded-lg text-xs sm:text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500 sm:min-w-40\">\n                            <option value=\"\">All States</option>\n                        </select>\n                        <select id=\"district-filter\" class=\"flex-1 sm:flex-none px-2 sm:px-3 py-1.5 sm:py-2 border border-gray-300 rounded-lg text-xs sm:text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500 sm:min-w-40\">\n                            <option value=\"\">All Districts</option>\n                        </select>\n                        <select id=\"village-filter\" class=\"flex-1 sm:flex-none px-2 sm:px-3 py-1.5 sm:py-2 border border-gray-300 rounded-lg text-xs sm:text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500 sm:min-w-40\">\n                            <option value=\"\">All Villages</option>\n                        </select>\n                    </div>\n                    <div class=\"flex w-full sm:w-auto gap-2\">\n                        <select id=\"land-use-filter\" class=\"flex-1 sm:flex-none px-2 sm:px-3 py-1.5 sm:py-2 border border-gray-300 rounded-lg text-xs sm:text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500\">\n                            <option value=\"\">All Land Types</option>\n                            <option value=\"Agricultural\">Agricultural</option>\n                            <option value=\"Residential\">Residential</option>\n                            <option value=\"Commercial\">Commercial</option>\n                            <option value=\"Industrial\">Industrial</option>\n                            <option value=\"Government\">Government</option>\n                            <option value=\"Forest\">Forest</option>\n                            <option value=\"Wasteland\">Wasteland</option>\n                        </select>\n                        <button id=\"btn-clear-filters\" class=\"px-3 sm:px-4 py-1.5 sm:py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition text-xs sm:text-sm font-medium whitespace-nowrap\">\n                            Clear\n                        </button>\n                    </div>\n                    <div class=\"hidden sm:block flex-1\"></div>\n                    <div class=\"relative w-full sm:w-auto flex items-center gap-2\">\n                        <div class=\"relative flex-1\">\n                            <input type=\"text\" id=\"record-search\" placeholder=\"Search by Khasra or Owner...\"\n                                class=\"w-full sm:w-64 pl-9 pr-4 py-1.5 sm:py-2 border border-gray-300 rounded-lg text-xs sm:text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500\">\n                            <svg class=\"w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"/></svg>\n                        </div>\n                        <!-- Primary CTA Button -->\n                        <button id=\"btn-add-record-new\" class=\"px-3 sm:px-4 py-1.5 sm:py-2 bg-orange-600 hover:bg-orange-700 text-white rounded-lg transition text-xs sm:text-sm font-bold flex items-center gap-1.5 sm:gap-2 shadow-md whitespace-nowrap\">\n                            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                            <span class=\"max-sm:hidden\">Add New Record</span>\n                            <span class=\"sm:hidden\">Add</span>\n                        </button>\n                        <button id=\"btn-export-excel\" class=\"px-3 sm:px-4 py-1.5 sm:py-2 bg-green-600 hover:bg-green-700 text-white rounded-lg transition text-xs sm:text-sm font-medium flex items-center gap-1.5 sm:gap-2 shadow-sm whitespace-nowrap\">\n                            <svg class=\"w-3.5 h-3.5 sm:w-4 sm:h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"/></svg>\n                            <span class=\"max-sm:hidden\">Export Excel</span>\n                            <span class=\"sm:hidden\">Export</span>\n                        </button>\n                        <button id=\"btn-refresh-records\" class=\"p-1.5 sm:p-2 text-gray-500 hover:text-orange-600 hover:bg-orange-50 rounded-lg transition\" title=\"Refresh Records\">\n                            <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"/></svg>\n                        </button>\n                    </div>\n                </div>\n\n                <!-- Records List -->\n                <div class=\"flex-1 overflow-y-auto\">\n                    <div class=\"records-header sticky top-0 z-5 bg-white border-b px-4 py-3 flex items-center justify-between\">\n                        <p class=\"text-sm text-gray-600\">Showing <span id=\"records-count-label\" class=\"font-semibold text-gray-800\">0</span> parcels</p>\n                        <div class=\"records-view-tabs\" role=\"tablist\" aria-label=\"Record list view mode\">\n                            <button type=\"button\" class=\"records-view-tab active\" data-view=\"cards\">Card View</button>\n                            <button type=\"button\" class=\"records-view-tab\" data-view=\"table\">Table View</button>\n                        </div>\n                    </div>\n\n                    <div id=\"records-list-cards\" class=\"divide-y divide-gray-100 px-4\">\n                        <!-- Populated by JS -->\n                    </div>\n\n                    <div id=\"records-list-table\" class=\"hidden p-4 overflow-x-auto\">\n                        <!-- Populated by JS -->\n                    </div>\n\n                    <div id=\"no-records\" class=\"hidden p-12 text-center text-gray-400\">\n                        <svg class=\"w-16 h-16 mx-auto mb-3 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"/></svg>\n                        <p class=\"text-base\">No records found</p>\n                        <p class=\"text-sm mt-1 mb-4\">Try adjusting your search or filters</p>\n                        <button onclick=\"document.querySelector('[data-tab=\\'add-record\\']').click()\" class=\"mx-auto bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-lg transition text-sm font-medium flex items-center justify-center gap-2 shadow-sm\">\n                            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                            Add New Record\n                        </button>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Map View Tab -->\n            <div id=\"main-tab-map\" class=\"main-tab-panel hidden h-full relative\">\n                <div id=\"map\" class=\"w-full h-full\" data-mode=\"admin\"></div>\n\n                <!-- Layer Switcher -->\n                <div class=\"absolute top-3 right-3 z-[999] bg-white rounded-lg shadow-lg p-3 hidden md:block\">\n                    <p class=\"text-xs font-semibold text-gray-500 mb-2 px-1\">MAP LAYERS</p>\n                    <div class=\"space-y-2\">\n                        <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                            <input type=\"radio\" name=\"basemap\" value=\"osm\" checked class=\"text-orange-600 focus:ring-orange-500\">\n                            <span>Street Map (OSM)</span>\n                        </label>\n                        <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                            <input type=\"radio\" name=\"basemap\" value=\"google\" class=\"text-orange-600 focus:ring-orange-500\">\n                            <span>Google Hybrid</span>\n                        </label>\n                        <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                            <input type=\"radio\" name=\"basemap\" value=\"esri\" class=\"text-orange-600 focus:ring-orange-500\">\n                            <span>Satellite (ESRI)</span>\n                        </label>\n                    </div>\n                </div>\n\n                <!-- Coordinates Display -->\n                <div class=\"absolute bottom-3 left-3 z-[998] bg-white/90 backdrop-blur rounded-lg shadow px-4 py-2 text-xs text-gray-600 font-mono hidden md:block\">\n                    <span id=\"cursor-coords\">Lat: --, Lng: --</span>\n                </div>\n\n                <!-- Mini Details Panel on Map -->\n                <div id=\"map-details-panel\" class=\"absolute bottom-3 right-3 z-[1001] bg-white rounded-lg shadow-lg p-4 w-80 hidden max-h-96 overflow-y-auto\">\n                    <div class=\"flex items-center justify-between mb-3\">\n                        <h4 class=\"text-sm font-semibold text-gray-800\">Parcel Details</h4>\n                        <button id=\"close-map-details\" class=\"text-gray-400 hover:text-gray-600\">\n                            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                        </button>\n                    </div>\n                    <div id=\"map-details-content\"></div>\n                    <div id=\"map-details-actions\" class=\"mt-3 flex gap-2 pt-3 border-t hidden\">\n                        <button id=\"btn-map-edit\"\n                            class=\"flex-1 bg-orange-600 hover:bg-orange-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">\n                            <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"/></svg>\n                            Edit\n                        </button>\n                        <button id=\"btn-map-print\"\n                            class=\"flex-1 bg-blue-600 hover:bg-blue-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">\n                            <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z\"/></svg>\n                            Print\n                        </button>\n                        <button id=\"btn-map-delete\"\n                            class=\"flex-1 bg-red-600 hover:bg-red-700 text-white text-xs py-2 rounded-lg transition flex items-center justify-center gap-1\">\n                            <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/></svg>\n                            Delete\n                        </button>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Add Record Tab (Split View: Map + Form) -->\n            <div id=\"main-tab-add-record\" class=\"main-tab-panel hidden h-full flex flex-col md:flex-row\">\n                <!-- Left: Map for drawing -->\n                <div class=\"w-full md:w-1/2 h-[40vh] md:h-full relative border-b md:border-b-0 md:border-r border-gray-200 flex-shrink-0\">\n                    <div id=\"add-record-map\" class=\"w-full h-full\"></div>\n                    <div class=\"absolute top-3 left-1/2 transform -translate-x-1/2 z-[1000] bg-orange-600 text-white px-4 py-2.5 rounded-lg shadow-lg\">\n                        <p class=\"text-sm font-semibold text-center\">Step 1: Draw parcel on map</p>\n                        <p class=\"text-xs text-orange-100 text-center\">Use controls below to draw polygon</p>\n                    </div>\n                    <!-- Layer Switcher for Add Record -->\n                    <div class=\"absolute top-3 right-3 z-[999] bg-white rounded-lg shadow-lg p-2 hidden md:block\">\n                        <p class=\"text-xs font-semibold text-gray-500 mb-1 px-1\">MAP LAYERS</p>\n                        <div class=\"space-y-1\">\n                            <label class=\"flex items-center gap-2 px-2 py-1 rounded hover:bg-gray-50 cursor-pointer text-xs\">\n                                <input type=\"radio\" name=\"add-basemap\" value=\"osm\" checked>\n                                <span>Street Map</span>\n                            </label>\n                            <label class=\"flex items-center gap-2 px-2 py-1 rounded hover:bg-gray-50 cursor-pointer text-xs\">\n                                <input type=\"radio\" name=\"add-basemap\" value=\"google\">\n                                <span>Google Hybrid</span>\n                            </label>\n                            <label class=\"flex items-center gap-2 px-2 py-1 rounded hover:bg-gray-50 cursor-pointer text-xs\">\n                                <input type=\"radio\" name=\"add-basemap\" value=\"esri\">\n                                <span>Satellite (ESRI)</span>\n                            </label>\n                        </div>\n                    </div>\n                    <!-- Drawing Controls for Add Record -->\n                    <div id=\"add-draw-controls\" class=\"absolute bottom-3 left-2 right-2 md:right-auto md:left-3 z-[1000] bg-white rounded-lg shadow-lg p-2 md:p-3 md:w-72\">\n                        <p class=\"text-[10px] md:text-xs font-semibold text-gray-500 mb-1 md:mb-2 px-1\">DRAW TOOLS</p>\n                        <div class=\"grid grid-cols-4 md:grid-cols-2 gap-1 md:gap-2\">\n                            <button id=\"btn-start-draw-new\" type=\"button\" class=\"draw-settings-btn bg-orange-600 text-white hover:bg-orange-700 px-1 md:px-3 text-[9px] md:text-xs py-1 md:py-2\">Draw</button>\n                            <button id=\"btn-finish-draw-new\" type=\"button\" class=\"draw-settings-btn bg-blue-600 text-white hover:bg-blue-700 px-1 md:px-3 text-[9px] md:text-xs py-1 md:py-2\">Done</button>\n                            <button id=\"btn-cancel-draw-new\" type=\"button\" class=\"draw-settings-btn bg-amber-600 text-white hover:bg-amber-700 px-1 md:px-3 text-[9px] md:text-xs py-1 md:py-2\">Back</button>\n                            <button id=\"btn-clear-draw-new\" type=\"button\" class=\"draw-settings-btn bg-gray-700 text-white hover:bg-gray-800 px-1 md:px-3 text-[9px] md:text-xs py-1 md:py-2\">Clear</button>\n                        </div>\n                    </div>\n                </div>\n                \n                <!-- Right: Form -->\n                <div class=\"w-full md:w-1/2 flex-1 overflow-y-auto bg-white\">\n                    <div class=\"p-6\">\n                        <h2 class=\"text-xl font-bold text-gray-800 mb-4\" id=\"form-title\">Add New Land Record</h2>\n                        \n                        <form id=\"record-form\" class=\"space-y-4\">\n                            <input type=\"hidden\" id=\"form-record-id\" value=\"\">\n                            <input type=\"hidden\" id=\"form-geometry\" value=\"\">\n\n                            <!-- Step indicator -->\n                            <div id=\"draw-status\" class=\"bg-blue-50 border-l-4 border-blue-500 rounded-r-lg p-3 text-sm text-blue-800\">\n                                <strong>Waiting for map drawing...</strong> Draw a parcel on the map to continue.\n                            </div>\n\n                            <!-- Form Sub-Tabs -->\n                            <div class=\"border-b border-gray-200\">\n                                <nav class=\"flex gap-1\" id=\"form-section-tabs\">\n                                    <button type=\"button\" class=\"form-tab-btn active\" data-form-tab=\"location\">\n                                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\n                                        Location\n                                    </button>\n                                    <button type=\"button\" class=\"form-tab-btn\" data-form-tab=\"parcel\">\n                                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"/></svg>\n                                        Parcel\n                                    </button>\n                                    <button type=\"button\" class=\"form-tab-btn\" data-form-tab=\"owner\">\n                                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"/></svg>\n                                        Owner\n                                    </button>\n                                    <button type=\"button\" class=\"form-tab-btn hidden\" id=\"form-mutation-tab-btn\" data-form-tab=\"mutation\">\n                                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"/></svg>\n                                        Mutation\n                                    </button>\n                                </nav>\n                            </div>\n\n                            <!-- Location Tab -->\n                            <div id=\"form-tab-location\" class=\"form-tab-panel\">\n                                <div class=\"space-y-3\">\n                                    <div class=\"bg-gray-50 rounded-lg p-3\">\n                                        <p class=\"text-sm text-gray-600\">Location auto-fills from map. You can override manually.</p>\n                                        <p id=\"form-location-source\" class=\"text-sm text-blue-700 mt-1 font-medium\">Source: waiting for map...</p>\n                                    </div>\n                                    <!-- Live GPS Detected Location Banner -->\n                                    <div id=\"gps-detected-banner\" class=\"hidden bg-green-50 border border-green-200 rounded-lg px-3 py-2.5 text-sm text-green-700\">\n                                    </div>\n                                    <div class=\"flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 px-3 py-2\">\n                                        <span class=\"text-sm font-medium text-gray-700\">Manual Override</span>\n                                        <input id=\"location-manual-override\" type=\"checkbox\" class=\"h-4 w-4 rounded border-gray-300 text-orange-600 focus:ring-orange-500\">\n                                    </div>\n                                    <div class=\"grid grid-cols-2 gap-3\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">State</label>\n                                            <select id=\"form-state\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                                <option value=\"\">Select State</option>\n                                            </select>\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">District</label>\n                                            <select id=\"form-district\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                                <option value=\"\">Select District</option>\n                                            </select>\n                                        </div>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Village / Ward</label>\n                                        <select id=\"form-village\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                            <option value=\"\">Select Village / Ward</option>\n                                        </select>\n                                    </div>\n                                    <div id=\"location-manual-fields\" class=\"hidden border-l-4 border-orange-500 bg-orange-50 rounded-r-lg p-3 space-y-2\">\n                                        <p class=\"text-sm text-orange-700 font-medium\">Manual override mode</p>\n                                        <input type=\"text\" id=\"form-state-manual\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\" placeholder=\"State\">\n                                        <input type=\"text\" id=\"form-district-manual\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\" placeholder=\"District\">\n                                        <input type=\"text\" id=\"form-village-manual\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\" placeholder=\"Village\">\n                                    </div>\n                                </div>\n                            </div>\n\n                            <!-- Parcel Tab -->\n                            <div id=\"form-tab-parcel\" class=\"form-tab-panel hidden\">\n                                <div class=\"grid grid-cols-2 gap-3\">\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Khasra No.</label>\n                                        <input type=\"text\" id=\"form-khasra\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"e.g. 101/2/A\">\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Khata No.</label>\n                                        <input type=\"text\" id=\"form-khata\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"e.g. KH-88\">\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">ULPIN</label>\n                                        <input type=\"text\" id=\"form-ulpin\" maxlength=\"14\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\">\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Land Use</label>\n                                        <select id=\"form-land-use\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\">\n                                            <option value=\"\">Select</option>\n                                            <option value=\"Agricultural\">Agricultural</option>\n                                            <option value=\"Residential\">Residential</option>\n                                            <option value=\"Commercial\">Commercial</option>\n                                            <option value=\"Industrial\">Industrial</option>\n                                            <option value=\"Government\">Government</option>\n                                            <option value=\"Forest\">Forest</option>\n                                            <option value=\"Wasteland\">Wasteland</option>\n                                        </select>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Area (Ha)</label>\n                                        <input type=\"number\" step=\"0.001\" id=\"form-area\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm bg-gray-100\" placeholder=\"Auto-calculated\" readonly>\n                                        <span class=\"text-green-600 text-xs\" id=\"area-auto\"></span>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Circle Rate (INR/ha)</label>\n                                        <input type=\"number\" id=\"form-circle-rate\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"e.g. 45000\" value=\"0\">\n                                    </div>\n                                </div>\n                                <div id=\"live-valuation-banner\" class=\"mt-4 bg-green-50 border border-green-200 rounded-lg p-3 flex items-center justify-between\">\n                                    <div>\n                                        <p class=\"text-[10px] font-bold text-green-700 uppercase\">Live Estimated Value</p>\n                                        <p class=\"text-lg font-bold text-green-800\" id=\"form-live-value\">Rs. 0</p>\n                                    </div>\n                                    <div class=\"text-right\">\n                                        <p class=\"text-[10px] font-bold text-green-600 uppercase\">Multiplier</p>\n                                        <p class=\"text-xs font-semibold text-green-700\" id=\"form-live-multiplier\">1.0x</p>\n                                    </div>\n                                </div>\n                                <div id=\"area-equivalents\" class=\"hidden mt-3 p-2 bg-green-50 border-l-4 border-green-500 rounded-r-lg text-xs text-green-800 grid grid-cols-3 gap-2\"></div>\n                                <div id=\"geometry-metrics\" class=\"hidden mt-3 p-2 bg-blue-50 border-l-4 border-blue-500 rounded-r-lg text-xs text-blue-800\">\n                                    <div><strong>Perimeter:</strong> <span id=\"metric-perimeter\">--</span></div>\n                                    <div><strong>Centroid:</strong> <span id=\"metric-centroid\">--</span></div>\n                                </div>\n                            </div>\n\n                            <!-- Owner Tab -->\n                            <div id=\"form-tab-owner\" class=\"form-tab-panel hidden\">\n                                <div class=\"space-y-3\">\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Owner Name</label>\n                                        <input type=\"text\" id=\"form-owner-name\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"Full Legal Name\">\n                                    </div>\n                                    <div class=\"grid grid-cols-2 gap-3\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Share (%)</label>\n                                            <input type=\"number\" id=\"form-share\" min=\"1\" max=\"100\" value=\"100\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\">\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Aadhaar (Masked)</label>\n                                            <input type=\"text\" id=\"form-aadhaar\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"XXXX-XXXX-1234\">\n                                        </div>\n                                    </div>\n                                    <div class=\"border-t pt-3 mt-3\">\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Ownership Proof Document (PDF/Image)</label>\n                                        <input type=\"file\" id=\"form-owner-doc\" accept=\"application/pdf,image/*\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm bg-white\">\n                                        <p class=\"text-xs text-gray-500 mt-1\">Optional. Will be securely stored in the database.</p>\n                                    </div>\n                                </div>\n                            </div>\n\n                            <!-- Mutation Tab -->\n                            <div id=\"form-tab-mutation\" class=\"form-tab-panel hidden\">\n                                <fieldset id=\"mutation-section\" class=\"border-l-4 border-orange-500 bg-orange-50 rounded-r-lg p-3 space-y-3\">\n                                    <legend class=\"text-sm font-semibold text-orange-700 px-2\">Mutation (Ownership Transfer)</legend>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">New Owner Name</label>\n                                        <input type=\"text\" id=\"form-new-owner\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\" placeholder=\"New Owner Name\">\n                                    </div>\n                                    <div class=\"grid grid-cols-2 gap-3\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">New Share (%)</label>\n                                            <input type=\"number\" id=\"form-new-share\" min=\"1\" max=\"100\" value=\"100\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\">\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Mutation Type</label>\n                                            <select id=\"form-mutation-type\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\">\n                                                <option value=\"Sale Deed\">Sale Deed</option>\n                                                <option value=\"Inheritance\">Inheritance</option>\n                                                <option value=\"Gift Deed\">Gift Deed</option>\n                                                <option value=\"Partition\">Partition</option>\n                                                <option value=\"Court Order\">Court Order</option>\n                                            </select>\n                                        </div>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Mutation Date</label>\n                                        <input type=\"date\" id=\"form-mutation-date\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm\">\n                                    </div>\n                                    <div class=\"border-t border-orange-200 pt-2 mt-2\">\n                                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Mutation Proof Document (PDF/Image)</label>\n                                        <input type=\"file\" id=\"form-mutation-doc\" accept=\"application/pdf,image/*\" class=\"w-full px-3 py-2 border border-orange-300 rounded-lg text-sm bg-white\">\n                                        <p class=\"text-xs text-orange-600 mt-1\">Optional. Official deed or certificate.</p>\n                                    </div>\n                                </fieldset>\n                            </div>\n\n                            <!-- Action Buttons -->\n                            <div class=\"flex gap-2 pt-3 border-t\">\n                                <button type=\"submit\" id=\"form-submit-btn\" class=\"flex-1 bg-orange-600 hover:bg-orange-700 text-white font-semibold py-2 px-4 rounded-lg transition text-sm\">\n                                    Save Record\n                                </button>\n                                <button type=\"button\" id=\"form-cancel-btn\" class=\"px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition text-sm\">\n                                    Cancel\n                                </button>\n                            </div>\n                        </form>\n                    </div>\n                </div>\n            </div>\n\n            <!-- View Record Tab (Full Screen Detail View) -->\n            <div id=\"main-tab-view-record\" class=\"main-tab-panel hidden h-full flex flex-col md:flex-row\">\n                <!-- Large Map (2/3) -->\n                <div class=\"w-full md:w-2/3 h-[40vh] md:h-full relative flex-shrink-0\">\n                    <div id=\"view-record-map\" class=\"w-full h-full\"></div>\n                    <!-- Layer Switcher - Moved to left side to avoid details panel overlap -->\n                    <div class=\"absolute top-3 left-3 z-[1002] bg-white rounded-lg shadow-lg p-3 hidden md:block\">\n                        <p class=\"text-xs font-semibold text-gray-500 mb-2 px-1\">MAP LAYERS</p>\n                        <div class=\"space-y-2\">\n                            <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                                <input type=\"radio\" name=\"view-basemap\" value=\"osm\" checked>\n                                <span>Street Map (OSM)</span>\n                            </label>\n                            <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                                <input type=\"radio\" name=\"view-basemap\" value=\"google\">\n                                <span>Google Hybrid</span>\n                            </label>\n                            <label class=\"flex items-center gap-2 px-2 py-1.5 rounded-lg hover:bg-gray-50 cursor-pointer text-sm\">\n                                <input type=\"radio\" name=\"view-basemap\" value=\"esri\">\n                                <span>Satellite (ESRI)</span>\n                            </label>\n                        </div>\n                    </div>\n                    <!-- Back Button - Moved to bottom right to avoid overlap -->\n                    <div class=\"absolute bottom-3 right-3 z-[1000]\">\n                        <button id=\"btn-back-to-records\" class=\"bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-lg shadow-lg transition flex items-center gap-2 text-sm font-medium\">\n                            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"/></svg>\n                            Back to Records\n                        </button>\n                    </div>\n                    <!-- Coordinates Display -->\n                    <div class=\"absolute bottom-3 left-3 z-[998] bg-white/90 backdrop-blur rounded-lg shadow px-4 py-2 text-xs text-gray-600 font-mono hidden md:block\">\n                        <span id=\"view-cursor-coords\">Lat: --, Lng: --</span>\n                    </div>\n                </div>\n\n                <!-- Details Panel (1/3) -->\n                <div class=\"w-full md:w-1/3 bg-white border-t md:border-t-0 md:border-l border-gray-200 flex flex-col flex-1 overflow-hidden\">\n                    <!-- Scrollable Content -->\n                    <div class=\"flex-1 overflow-y-auto p-3 sm:p-5 space-y-3 sm:space-y-4\">\n                        <!-- Header -->\n                        <div class=\"bg-white border border-gray-200 rounded-lg p-4\">\n                            <div class=\"flex items-center justify-between mb-2\">\n                                <h2 class=\"text-lg sm:text-xl font-bold text-gray-800\" id=\"view-khasra\">--</h2>\n                                <span id=\"view-land-use-badge\" class=\"land-use-badge badge-agricultural\">--</span>\n                            </div>\n                            <p class=\"text-sm text-gray-500 font-mono\" id=\"view-ulpin\">--</p>\n                        </div>\n\n                        <!-- Location -->\n                        <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                            <h3 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                                <svg class=\"w-4 h-4 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\n                                Location\n                            </h3>\n                            <div class=\"space-y-2 text-sm\">\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">State</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-state\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">District</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-district\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Village / Ward</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-village\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Khata No.</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-khata\">--</span>\n                                </div>\n                            </div>\n                        </div>\n\n                        <!-- Parcel Info -->\n                        <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                            <h3 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                                <svg class=\"w-4 h-4 text-orange-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"/></svg>\n                                Parcel Details\n                            </h3>\n                            <div class=\"space-y-2 text-sm\">\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Area</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-area\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Circle Rate</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-rate\">--</span>\n                                </div>\n                                <div class=\"flex justify-between pt-2 border-t\">\n                                    <span class=\"text-gray-500 font-medium\">Estimated Value</span>\n                                    <span class=\"font-bold text-green-700\" id=\"view-value\">--</span>\n                                </div>\n                            </div>\n                            <div id=\"view-area-equivalents\" class=\"mt-3 p-2 bg-green-100 rounded text-xs text-green-800 hidden\"></div>\n                        </div>\n\n                        <!-- Owner -->\n                        <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                            <h3 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                                <svg class=\"w-4 h-4 text-purple-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"/></svg>\n                                Owner\n                            </h3>\n                            <div class=\"space-y-2 text-sm\">\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Name</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-owner\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Share</span>\n                                    <span class=\"font-medium text-gray-800\" id=\"view-share\">--</span>\n                                </div>\n                                <div class=\"flex justify-between\">\n                                    <span class=\"text-gray-500\">Aadhaar</span>\n                                    <span class=\"font-mono text-gray-800\" id=\"view-aadhaar\">--</span>\n                                </div>\n                                <div class=\"flex justify-between hidden\" id=\"view-owner-doc-container\">\n                                    <span class=\"text-gray-500\">Proof Doc</span>\n                                    <a href=\"#\" id=\"view-owner-doc-link\" download=\"Proof_Document\" class=\"text-blue-600 hover:text-blue-800 text-sm font-medium underline flex items-center gap-1\">\n                                        <svg class=\"w-3 h-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"/></svg>\n                                        Download\n                                    </a>\n                                </div>\n                            </div>\n                        </div>\n\n                        <!-- Mutation History -->\n                        <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                            <h3 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                                <svg class=\"w-4 h-4 text-amber-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"/></svg>\n                                Mutation History\n                            </h3>\n                            <div id=\"view-mutations\" class=\"space-y-2\">\n                                <p class=\"text-sm text-gray-500\">No mutation history</p>\n                            </div>\n                        </div>\n                    </div>\n\n                    <!-- Fixed Footer with Action Buttons -->\n                    <div class=\"bg-gray-50 border-t border-gray-200 p-3 sm:p-4 flex-shrink-0\">\n                        <div class=\"space-y-2\">\n                            <button id=\"btn-view-edit\" class=\"w-full bg-orange-600 hover:bg-orange-700 text-white py-2.5 sm:py-3 rounded-lg transition flex items-center justify-center gap-2 text-sm font-medium\">\n                                <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"/></svg>\n                                Edit / Mutate Record\n                            </button>\n                            <div class=\"grid grid-cols-2 gap-2\">\n                                <button id=\"btn-view-print\" class=\"bg-blue-600 hover:bg-blue-700 text-white py-2.5 rounded-lg transition flex items-center justify-center gap-2 text-sm font-medium\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z\"/></svg>\n                                    Print Card\n                                </button>\n                                <button id=\"btn-view-delete\" class=\"bg-red-600 hover:bg-red-700 text-white py-2.5 rounded-lg transition flex items-center justify-center gap-2 text-sm font-medium\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"/></svg>\n                                    Delete\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Dashboard Tab (Default) -->\n            <div id=\"main-tab-dashboard\" class=\"main-tab-panel overflow-y-auto p-6 h-full\">\n                <div class=\"max-w-7xl mx-auto\">\n                    <!-- Welcome Banner & Quick Actions CTA -->\n                    <div class=\"bg-gradient-to-r from-gray-800 to-gray-900 rounded-xl shadow-lg p-4 sm:p-6 mb-6 text-white flex flex-col md:flex-row justify-between items-center gap-4 sm:gap-6\">\n                        <div>\n                            <h2 class=\"text-lg sm:text-2xl font-bold mb-1\">Welcome, {{ username }}</h2>\n                            <p class=\"text-gray-300 text-[11px] sm:text-sm\">Monitor parcels and process mutations instantly.</p>\n                        </div>\n                        <div class=\"flex flex-wrap gap-3 w-full md:w-auto\">\n                            <button id=\"btn-refresh-dashboard\" class=\"flex-1 md:flex-none bg-white/10 hover:bg-white/20 backdrop-blur border border-white/20 p-2.5 rounded-lg transition\" title=\"Refresh Dashboard Stats\">\n                                <svg class=\"w-5 h-5 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"/></svg>\n                            </button>\n                            <button onclick=\"document.querySelector('[data-tab=\\'add-record\\']').click()\" class=\"flex-1 md:flex-none bg-orange-500 hover:bg-orange-600 px-5 py-2.5 rounded-lg font-bold shadow-lg transition flex items-center justify-center gap-2\">\n                                <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                                Register New Parcel\n                            </button>\n                            <button onclick=\"document.querySelector('[data-tab=\\'map\\']').click()\" class=\"flex-1 md:flex-none bg-white/10 hover:bg-white/20 backdrop-blur border border-white/20 px-5 py-2.5 rounded-lg font-bold transition flex items-center justify-center gap-2\">\n                                <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7\"/></svg>\n                                GIS Overview\n                            </button>\n                        </div>\n                    </div>\n\n                    <!-- KPI Cards -->\n                    <div class=\"grid grid-cols-2 lg:grid-cols-4 gap-3 sm:gap-4 mb-6\">\n                        <div class=\"bg-white rounded-lg shadow-md p-4 border-l-4 border-orange-500\">\n                            <div class=\"flex items-center justify-between\">\n                                <div>\n                                    <p class=\"text-xs font-semibold text-gray-500 uppercase\">Total Parcels</p>\n                                    <p id=\"kpi-total-parcels\" class=\"text-2xl font-bold text-gray-800 mt-1\">0</p>\n                                </div>\n                                <div class=\"w-12 h-12 bg-orange-100 rounded-lg flex items-center justify-center\">\n                                    <svg class=\"w-6 h-6 text-orange-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4\"/></svg>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-4 border-l-4 border-blue-500\">\n                            <div class=\"flex items-center justify-between\">\n                                <div>\n                                    <p class=\"text-xs font-semibold text-gray-500 uppercase\">Total Area (Ha)</p>\n                                    <p id=\"kpi-total-area\" class=\"text-2xl font-bold text-gray-800 mt-1\">0.00</p>\n                                </div>\n                                <div class=\"w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center\">\n                                    <svg class=\"w-6 h-6 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v-4m0 0h4m-4 0l5 5m11-5h-4m4 0l-5 5\"/></svg>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-4 border-l-4 border-green-500\">\n                            <div class=\"flex items-center justify-between\">\n                                <div>\n                                    <p class=\"text-xs font-semibold text-gray-500 uppercase\">Estimated Value (INR)</p>\n                                    <p id=\"kpi-estimated-value\" class=\"text-2xl font-bold text-gray-800 mt-1\">0</p>\n                                </div>\n                                <div class=\"w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center\">\n                                    <svg class=\"w-6 h-6 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>\n                                </div>\n                            </div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-4 border-l-4 border-purple-500\">\n                            <div class=\"flex items-center justify-between\">\n                                <div>\n                                    <p class=\"text-[10px] font-semibold text-gray-500 uppercase\">Mutations</p>\n                                    <p id=\"kpi-mutations\" class=\"text-lg sm:text-2xl font-bold text-gray-800 mt-0.5\">0</p>\n                                </div>\n                                <div class=\"w-10 h-10 sm:w-12 sm:h-12 bg-purple-100 rounded-lg flex items-center justify-center\">\n                                    <svg class=\"w-5 h-5 sm:w-6 sm:h-6 text-purple-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"/></svg>\n                                </div>\n                            </div>\n                        </div>\n                    </div>\n\n                    <!-- Dashboard Content -->\n                    <div class=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n                        <div class=\"bg-white rounded-lg shadow-md p-6\">\n                            <h3 class=\"text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2\">\n                                <svg class=\"w-5 h-5 text-orange-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"/></svg>\n                                Land Type Distribution\n                            </h3>\n                            <div id=\"dashboard-land-use\" class=\"space-y-2\"></div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-6\">\n                            <h3 class=\"text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2\">\n                                <svg class=\"w-5 h-5 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\n                                District Overview\n                            </h3>\n                            <div id=\"dashboard-districts\" class=\"space-y-2\"></div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-6\">\n                            <h3 class=\"text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2\">\n                                <svg class=\"w-5 h-5 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z\"/></svg>\n                                Top Value Parcel\n                            </h3>\n                            <div id=\"dashboard-top-parcel\" class=\"bg-gradient-to-r from-green-50 to-green-100 border border-green-200 rounded-lg p-4 text-sm text-gray-700\">No parcel data yet.</div>\n                        </div>\n                        <div class=\"bg-white rounded-lg shadow-md p-6\">\n                            <h3 class=\"text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2\">\n                                <svg class=\"w-5 h-5 text-purple-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4\"/></svg>\n                                Recent Mutations\n                            </h3>\n                            <div id=\"dashboard-mutations\" class=\"space-y-2\"></div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Profile Tab -->\n            <div id=\"main-tab-profile\" class=\"main-tab-panel hidden h-full overflow-y-auto\">\n                <div class=\"max-w-6xl mx-auto p-6\">\n                    <div class=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n                        <!-- Left: My Profile -->\n                        <div class=\"lg:col-span-1 space-y-4\">\n                            <!-- Profile Card -->\n                            <div class=\"bg-white rounded-lg shadow-md overflow-hidden\">\n                                <div class=\"bg-gradient-to-r from-orange-500 to-orange-600 px-6 py-8 text-center\">\n                                    <div class=\"w-20 h-20 bg-white rounded-full flex items-center justify-center mx-auto shadow-lg\">\n                                        <svg class=\"w-10 h-10 text-orange-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"/></svg>\n                                    </div>\n                                    <h3 class=\"text-xl font-bold text-white mt-3\" id=\"profile-display-name\">--</h3>\n                                    <p class=\"text-orange-100 text-sm\" id=\"profile-display-role\">--</p>\n                                    <span id=\"profile-status-badge\" class=\"inline-block mt-2 px-3 py-1 bg-white/20 text-white text-xs font-semibold rounded-full\">Active</span>\n                                </div>\n                                <div class=\"p-4 space-y-3 text-sm\">\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Username</span><span class=\"font-medium\" id=\"profile-username\">--</span></div>\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Email</span><span class=\"font-medium\" id=\"profile-email\">--</span></div>\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Phone</span><span class=\"font-medium\" id=\"profile-phone\">--</span></div>\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Designation</span><span class=\"font-medium\" id=\"profile-designation\">--</span></div>\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Department</span><span class=\"font-medium\" id=\"profile-department\">--</span></div>\n                                    <div class=\"flex justify-between\"><span class=\"text-gray-500\">Office</span><span class=\"font-medium\" id=\"profile-office\">--</span></div>\n                                    <div class=\"flex justify-between pt-2 border-t\"><span class=\"text-gray-500\">Last Login</span><span class=\"font-medium\" id=\"profile-last-login\">--</span></div>\n                                </div>\n                            </div>\n                        </div>\n\n\n                        <!-- Right: Edit Profile Form / User Management -->\n                        <div class=\"lg:col-span-2 space-y-4\">\n                            <!-- Edit Profile Form (hidden by default) -->\n                            <div id=\"edit-profile-form\" class=\"bg-white rounded-lg shadow-md p-6 hidden\">\n                                <h3 class=\"text-lg font-semibold text-gray-800 mb-4\">Edit My Profile</h3>\n                                <form id=\"profile-form\" class=\"space-y-4\">\n                                    <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Full Name</label>\n                                            <input type=\"text\" id=\"edit-full-name\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" required>\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Email</label>\n                                            <input type=\"email\" id=\"edit-email\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        </div>\n                                    </div>\n                                    <div class=\"grid grid-cols-2 gap-4\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Phone</label>\n                                            <input type=\"text\" id=\"edit-phone\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Designation</label>\n                                            <input type=\"text\" id=\"edit-designation\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        </div>\n                                    </div>\n                                    <div class=\"grid grid-cols-2 gap-4\">\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Department</label>\n                                            <input type=\"text\" id=\"edit-department\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        </div>\n                                        <div>\n                                            <label class=\"block text-sm font-medium text-gray-700 mb-1\">Office Location</label>\n                                            <input type=\"text\" id=\"edit-office\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        </div>\n                                    </div>\n\n                                    <!-- Change Password Section -->\n                                    <div class=\"border-t pt-4\">\n                                        <h4 class=\"text-sm font-semibold text-gray-700 mb-3\">Change Password</h4>\n                                        <div class=\"grid grid-cols-2 gap-4\">\n                                            <div>\n                                                <label class=\"block text-sm font-medium text-gray-700 mb-1\">Current Password</label>\n                                                <input type=\"password\" id=\"edit-current-password\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"Required to changed password\">\n                                            </div>\n                                            <div>\n                                                <label class=\"block text-sm font-medium text-gray-700 mb-1\">New Password</label>\n                                                <input type=\"password\" id=\"edit-new-password\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"New password\">\n                                            </div>\n                                        </div>\n                                    </div>\n\n                                    <div class=\"flex gap-2 pt-2\">\n                                        <button type=\"submit\" class=\"flex-1 bg-orange-600 hover:bg-orange-700 text-white py-2 px-4 rounded-lg transition text-sm\">Save Changes</button>\n                                        <button type=\"button\" id=\"btn-cancel-edit-profile\" class=\"px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition text-sm\">Cancel</button>\n                                    </div>\n                                </form>\n                            </div>\n\n                            <!-- Profile View Only -->\n                            <div id=\"profile-view\" class=\"bg-white rounded-lg shadow-md p-6\">\n                                <h3 class=\"text-lg font-semibold text-gray-800 mb-4\">My Profile Information</h3>\n                                <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-4\">\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Username</label>\n                                        <p id=\"profile-view-username\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Full Name</label>\n                                        <p id=\"profile-view-name\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Email</label>\n                                        <p id=\"profile-view-email\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Phone</label>\n                                        <p id=\"profile-view-phone\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Designation</label>\n                                        <p id=\"profile-view-designation\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Department</label>\n                                        <p id=\"profile-view-department\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Office</label>\n                                        <p id=\"profile-view-office\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                    <div>\n                                        <label class=\"block text-xs font-medium text-gray-500 mb-1\">Last Login</label>\n                                        <p id=\"profile-view-last-login\" class=\"text-sm font-medium text-gray-800\">--</p>\n                                    </div>\n                                </div>\n                                <button id=\"btn-edit-profile-inline\" class=\"mt-4 bg-orange-600 hover:bg-orange-700 text-white py-2 px-4 rounded-lg transition text-sm font-medium flex items-center justify-center gap-2 w-full\">\n                                    <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"/></svg>\n                                    Edit Profile\n                                </button>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- Reports & Feedback Tab -->\n            <div id=\"main-tab-reports\" class=\"main-tab-panel hidden h-full overflow-y-auto\">\n                <div class=\"max-w-4xl mx-auto p-6\">\n                    <div class=\"bg-white rounded-lg shadow-md p-6 mb-6\">\n                        <div class=\"flex items-center justify-between mb-4\">\n                            <div>\n                                <h2 class=\"text-xl font-bold text-gray-800\">Reports & Feedback</h2>\n                                <p class=\"text-sm text-gray-600 mt-1\">Review feedback and issues submitted by viewers.</p>\n                            </div>\n                            <button id=\"btn-refresh-feedback\" class=\"bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-lg transition text-sm font-medium flex items-center gap-2\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"/></svg>\n                                Refresh\n                            </button>\n                        </div>\n                        <div class=\"overflow-x-auto\">\n                            <table class=\"w-full text-left border-collapse\">\n                                <thead>\n                                    <tr class=\"bg-gray-100 text-gray-600 text-xs font-bold uppercase border-y border-gray-200\">\n                                        <th class=\"px-4 py-3\">Date</th>\n                                        <th class=\"px-4 py-3\">Email</th>\n                                        <th class=\"px-4 py-3\">Issue Type</th>\n                                        <th class=\"px-4 py-3\">Message</th>\n                                        <th class=\"px-4 py-3 text-right\">Actions</th>\n                                    </tr>\n                                </thead>\n                                <tbody id=\"feedback-table-body\" class=\"text-sm divide-y divide-gray-100\">\n                                    <tr class=\"hover:bg-gray-50 transition cursor-pointer\">\n                                        <td colspan=\"5\" class=\"px-4 py-4 text-center text-gray-500\">Loading...</td>\n                                    </tr>\n                                </tbody>\n                            </table>\n                        </div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- User Management Tab -->\n            <div id=\"main-tab-users\" class=\"main-tab-panel hidden h-full overflow-y-auto\">\n                <div class=\"max-w-7xl mx-auto p-6\">\n                    <!-- Header -->\n                    <div class=\"bg-white rounded-lg shadow-md p-6 mb-6\">\n                        <div class=\"flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4\">\n                            <div>\n                                <h2 class=\"text-xl font-bold text-gray-800\">User Management</h2>\n                                <p class=\"text-sm text-gray-600 mt-1\">Manage system users, roles, and permissions</p>\n                            </div>\n                            <button id=\"btn-create-user\" class=\"bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-lg transition text-sm font-medium flex items-center justify-center gap-2\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                                Add New User\n                            </button>\n                        </div>\n\n                        <!-- Stats Cards -->\n                        <div class=\"grid grid-cols-2 md:grid-cols-4 gap-4 mb-4\">\n                            <div class=\"bg-gradient-to-br from-blue-50 to-blue-100 border border-blue-200 rounded-lg p-4\">\n                                <p class=\"text-xs font-semibold text-blue-600 uppercase\">Total Users</p>\n                                <p id=\"stat-total-users\" class=\"text-2xl font-bold text-blue-900 mt-1\">0</p>\n                            </div>\n                            <div class=\"bg-gradient-to-br from-green-50 to-green-100 border border-green-200 rounded-lg p-4\">\n                                <p class=\"text-xs font-semibold text-green-600 uppercase\">Active Users</p>\n                                <p id=\"stat-active-users\" class=\"text-2xl font-bold text-green-900 mt-1\">0</p>\n                            </div>\n                            <div class=\"bg-gradient-to-br from-purple-50 to-purple-100 border border-purple-200 rounded-lg p-4\">\n                                <p class=\"text-xs font-semibold text-purple-600 uppercase\">Admins</p>\n                                <p id=\"stat-admin-users\" class=\"text-2xl font-bold text-purple-900 mt-1\">0</p>\n                            </div>\n                            <div class=\"bg-gradient-to-br from-orange-50 to-orange-100 border border-orange-200 rounded-lg p-4\">\n                                <p class=\"text-xs font-semibold text-orange-600 uppercase\">Viewers</p>\n                                <p id=\"stat-viewer-users\" class=\"text-2xl font-bold text-orange-900 mt-1\">0</p>\n                            </div>\n                        </div>\n                    </div>\n\n                    <!-- Create User Form (hidden by default) -->\n                    <div id=\"create-user-form\" class=\"bg-white rounded-lg shadow-md p-6 mb-6 hidden\">\n                        <div class=\"flex items-center justify-between mb-4\">\n                            <h3 class=\"text-lg font-semibold text-gray-800\">Create New User</h3>\n                            <button id=\"btn-cancel-create-user\" class=\"text-gray-400 hover:text-gray-600\">\n                                <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                            </button>\n                        </div>\n                        <form id=\"user-create-form\" class=\"space-y-4\">\n                            <div class=\"grid grid-cols-2 gap-4\">\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Username *</label>\n                                    <input type=\"text\" id=\"create-username\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"Username for login\">\n                                </div>\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Password *</label>\n                                    <input type=\"password\" id=\"create-password\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"Minimum 6 characters\">\n                                </div>\n                            </div>\n                            <div>\n                                <label class=\"block text-sm font-medium text-gray-700 mb-1\">Full Name *</label>\n                                <input type=\"text\" id=\"create-full-name\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"Full legal name\">\n                            </div>\n                            <div class=\"grid grid-cols-2 gap-4\">\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Email</label>\n                                    <input type=\"email\" id=\"create-email\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"user@example.com\">\n                                </div>\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Phone</label>\n                                    <input type=\"text\" id=\"create-phone\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"+91 XXXXX XXXXX\">\n                                </div>\n                            </div>\n                            <div class=\"grid grid-cols-2 gap-4\">\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Designation</label>\n                                    <input type=\"text\" id=\"create-designation\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"e.g. Tehsildar\">\n                                </div>\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Department</label>\n                                    <input type=\"text\" id=\"create-department\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"e.g. Revenue\">\n                                </div>\n                            </div>\n                            <div class=\"grid grid-cols-2 gap-4\">\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Role *</label>\n                                    <select id=\"create-role\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                                        <option value=\"Officer\">Officer - Can create/edit records</option>\n                                        <option value=\"Admin\">Admin - Full access + user management</option>\n                                        <option value=\"SuperAdmin\">Super Admin - All permissions</option>\n                                    </select>\n                                </div>\n                                <div>\n                                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Office Location</label>\n                                    <input type=\"text\" id=\"create-office\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"e.g. Delhi Office\">\n                                </div>\n                            </div>\n                            <div class=\"flex items-center gap-4\">\n                                <div class=\"flex items-center gap-2\">\n                                    <input type=\"checkbox\" id=\"create-active\" checked class=\"h-4 w-4 rounded border-gray-300 text-orange-600 focus:ring-orange-500\">\n                                    <label for=\"create-active\" class=\"text-sm font-medium text-gray-700\">Active</label>\n                                </div>\n                                <div id=\"create-recovery-container\" class=\"hidden flex items-center gap-2\">\n                                    <input type=\"checkbox\" id=\"create-is-recovery\" class=\"h-4 w-4 rounded border-gray-300 text-red-600 focus:ring-red-500\">\n                                    <label for=\"create-is-recovery\" class=\"text-sm font-medium text-red-700\">Recovery Superuser (Ghost Mode)</label>\n                                </div>\n                            </div>\n                            <div class=\"flex gap-2 pt-2\">\n                                <button type=\"submit\" class=\"flex-1 bg-green-600 hover:bg-green-700 text-white py-2.5 px-4 rounded-lg transition text-sm font-semibold\">Create User</button>\n                                <button type=\"button\" id=\"btn-cancel-create-user-2\" class=\"px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition text-sm\">Cancel</button>\n                            </div>\n                        </form>\n                    </div>\n\n                    <!-- Users Table -->\n                    <div class=\"bg-white rounded-lg shadow-md overflow-hidden\">\n                        <div class=\"overflow-x-auto\">\n                            <table class=\"w-full text-sm\">\n                                <thead class=\"bg-gray-50 border-b\">\n                                    <tr>\n                                        <th class=\"text-left px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">User</th>\n                                        <th class=\"text-left px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">Role</th>\n                                        <th class=\"text-left px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">Contact</th>\n                                        <th class=\"text-left px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">Department</th>\n                                        <th class=\"text-center px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">Status</th>\n                                        <th class=\"text-right px-4 py-3 text-xs font-semibold text-gray-500 uppercase\">Actions</th>\n                                    </tr>\n                                </thead>\n                                <tbody id=\"users-table-body\" class=\"divide-y divide-gray-100\">\n                                    <!-- Populated by JS -->\n                                </tbody>\n                            </table>\n                        </div>\n                        <div id=\"no-users\" class=\"hidden p-12 text-center text-gray-400\">\n                            <svg class=\"w-16 h-16 mx-auto mb-3 opacity-50\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.25 2.25 0 11-4.5 0 2.25 2.25 0 014.5 0z\"/></svg>\n                            <p class=\"text-base\">No users found</p>\n                            <p class=\"text-sm mt-1 mb-4\">Add your first user to get started</p>\n                            <button onclick=\"document.getElementById('btn-create-user').click()\" class=\"mx-auto bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-lg transition text-sm font-medium flex items-center justify-center gap-2 shadow-sm\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 4v16m8-8H4\"/></svg>\n                                Add New User\n                            </button>\n                        </div>\n                    </div>\n                </div>\n            </div>\n            \n            <!-- Audit Tab -->\n            <div id=\"main-tab-audit\" class=\"main-tab-panel hidden h-full overflow-y-auto\">\n                <div class=\"max-w-7xl mx-auto p-6\">\n                    <div class=\"bg-white rounded-lg shadow-md p-6 mb-6\">\n                        <div class=\"flex items-center justify-between mb-4\">\n                            <div>\n                                <h2 class=\"text-xl font-bold text-gray-800\">System Audit Log</h2>\n                                <p class=\"text-sm text-gray-600 mt-1\">Review system activities and record changes</p>\n                            </div>\n                        </div>\n                        <div id=\"audit-entries\"></div>\n                    </div>\n                </div>\n            </div>\n\n        </div>\n    </div>\n\n    <!-- Edit User Modal -->\n    <div id=\"edit-user-modal\" class=\"fixed inset-0 z-[9998] hidden bg-black/50 flex items-center justify-center\">\n        <div class=\"bg-white rounded-lg shadow-xl w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto\">\n            <div class=\"flex items-center justify-between p-6 border-b\">\n                <h3 class=\"text-lg font-semibold text-gray-800\">Edit User</h3>\n                <button id=\"btn-close-edit-user\" class=\"text-gray-400 hover:text-gray-600\">\n                    <svg class=\"w-6 h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                </button>\n            </div>\n            <form id=\"user-edit-form\" class=\"p-6 space-y-4\">\n                <input type=\"hidden\" id=\"edit-user-id\">\n                <div class=\"grid grid-cols-2 gap-4\">\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Username</label>\n                        <input type=\"text\" id=\"edit-username\" readonly class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm bg-gray-100\" readonly>\n                    </div>\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Role *</label>\n                        <select id=\"edit-role\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                            <option value=\"Officer\">Officer - Can create/edit records</option>\n                            <option value=\"Admin\">Admin - Full access + user management</option>\n                            <option value=\"SuperAdmin\">Super Admin - All permissions</option>\n                        </select>\n                    </div>\n                </div>\n                <div>\n                    <label class=\"block text-sm font-medium text-gray-700 mb-1\">Full Name *</label>\n                    <input type=\"text\" id=\"edit-fullname\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                </div>\n                <div class=\"grid grid-cols-2 gap-4\">\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Email</label>\n                        <input type=\"email\" id=\"edit-email\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                    </div>\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Phone</label>\n                        <input type=\"text\" id=\"edit-phone-user\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                    </div>\n                </div>\n                <div class=\"grid grid-cols-2 gap-4\">\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Designation</label>\n                        <input type=\"text\" id=\"edit-designation-user\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                    </div>\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Department</label>\n                        <input type=\"text\" id=\"edit-department-user\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                    </div>\n                </div>\n                <div class=\"grid grid-cols-2 gap-4\">\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">Office Location</label>\n                        <input type=\"text\" id=\"edit-office-user\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                    </div>\n                    <div class=\"flex items-end\">\n                        <div class=\"flex items-center gap-4\">\n                            <div class=\"flex items-center gap-2\">\n                                <input type=\"checkbox\" id=\"edit-is-active\" class=\"h-4 w-4 rounded border-gray-300 text-orange-600 focus:ring-orange-500\">\n                                <label for=\"edit-is-active\" class=\"text-sm font-medium text-gray-700\">Active</label>\n                            </div>\n                            <div id=\"edit-recovery-container\" class=\"hidden flex items-center gap-2\">\n                                <input type=\"checkbox\" id=\"edit-is-recovery\" class=\"h-4 w-4 rounded border-gray-300 text-red-600 focus:ring-red-500\">\n                                <label for=\"edit-is-recovery\" class=\"text-sm font-medium text-red-700\">Recovery Status</label>\n                            </div>\n                        </div>\n                    </div>\n                </div>\n                <div class=\"border-t pt-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-3\">Reset Password (optional)</h4>\n                    <div>\n                        <label class=\"block text-sm font-medium text-gray-700 mb-1\">New Password</label>\n                        <input type=\"password\" id=\"edit-new-password\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\" placeholder=\"Leave blank to keep current password\">\n                    </div>\n                </div>\n                <div class=\"flex gap-2 pt-2\">\n                    <button type=\"submit\" class=\"flex-1 bg-orange-600 hover:bg-orange-700 text-white py-2.5 px-4 rounded-lg transition text-sm font-semibold\">Save Changes</button>\n                    <button type=\"button\" id=\"btn-cancel-edit-user\" class=\"px-4 py-2 bg-gray-200 hover:bg-gray-300 text-gray-700 rounded-lg transition text-sm\">Cancel</button>\n                </div>\n            </form>\n        </div>\n    </div>\n\n    <!-- Toast Notification -->\n    <div id=\"toast\" class=\"fixed bottom-5 right-5 z-[9999] hidden transform transition-all duration-300 translate-y-2 opacity-0\">\n        <div class=\"bg-gray-800 text-white px-4 py-3 rounded-lg shadow-xl flex items-center gap-2 text-sm\">\n            <span id=\"toast-icon\"></span>\n            <span id=\"toast-msg\"></span>\n        </div>\n    </div>\n\n    <!-- Scripts -->\n    <script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"></script>\n    <script src=\"https://unpkg.com/@geoman-io/leaflet-geoman-free@2.17.0/dist/leaflet-geoman.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js\"></script>\n    <!-- App Modules -->\n    <script src=\"{{ url_for('static', filename='js/api.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/auth.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/state.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/utils.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/map_engine.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/gis.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/dashboard.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/records.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/forms.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/modules/admin.js') }}?v=2\"></script>\n    <script src=\"{{ url_for('static', filename='js/map.js') }}?v=3\"></script>\n\n    <script>\n        // Mobile Sidebar Drawer Toggle\n        document.addEventListener('DOMContentLoaded', () => {\n            const btnToggle = document.getElementById('btn-toggle-sidebar');\n            const sidebar = document.getElementById('admin-sidebar');\n            const overlay = document.getElementById('sidebar-overlay');\n            const tabBtns = document.querySelectorAll('.main-tab-btn');\n\n            function openSidebar() {\n                sidebar.classList.remove('-translate-x-full');\n                sidebar.classList.add('translate-x-0');\n                overlay.classList.remove('hidden');\n            }\n\n            function closeSidebar() {\n                sidebar.classList.add('-translate-x-full');\n                sidebar.classList.remove('translate-x-0');\n                overlay.classList.add('hidden');\n            }\n\n            if (btnToggle) {\n                btnToggle.addEventListener('click', openSidebar);\n            }\n            if (overlay) {\n                overlay.addEventListener('click', closeSidebar);\n            }\n            \n            // Close sidebar when a tab is clicked on mobile\n            tabBtns.forEach(btn => {\n                btn.addEventListener('click', () => {\n                    if (window.innerWidth < 768 && btn.id !== 'btn-logout') {\n                        closeSidebar();\n                    }\n                });\n            });\n\n            // Close on Escape key\n            document.addEventListener('keydown', (e) => {\n                if (e.key === 'Escape' && !overlay.classList.contains('hidden')) {\n                    closeSidebar();\n                }\n            });\n\n        });\n    </script>\n</body>\n</html>\n"
  },
  {
    "path": "templates/login.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>LIMS - Login</title>\n    \n    <!-- External Dependencies -->\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\">\n    \n    <!-- App Assets -->\n    <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/style.css') }}\">\n    <link rel=\"icon\" type=\"image/png\" href=\"https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-orange.png\">\n    \n    <style>\n        .login-bg-image {\n            background-image: url(\"{{ url_for('static', filename='img/bg-login.png') }}\");\n        }\n    </style>\n</head>\n<body class=\"min-h-screen flex items-center justify-center relative overflow-hidden font-sans\">\n\n    <!-- Background Image with Overlay -->\n    <div class=\"fixed inset-0 z-0\">\n        <div class=\"absolute inset-0 bg-cover bg-center transition-opacity duration-1000 login-bg-image\"></div>\n        <div class=\"absolute inset-0 bg-gradient-to-br from-slate-900/60 via-slate-800/40 to-slate-900/70 backdrop-blur-sm\"></div>\n    </div>\n\n    <div class=\"relative z-10 w-full max-w-md mx-4 py-6\">\n        <!-- Header -->\n        <div class=\"text-center mb-6\">\n            <div class=\"inline-flex flex-col items-center gap-2\">\n                <div class=\"w-16 h-16 bg-white/10 backdrop-blur-md border border-white/20 rounded-2xl flex items-center justify-center shadow-2xl mb-1\">\n                    <svg class=\"w-10 h-10 text-white\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"1.5\" d=\"M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"></path>\n                    </svg>\n                </div>\n                <div class=\"text-center\">\n                    <h1 class=\"text-4xl font-extrabold text-white tracking-tight drop-shadow-lg leading-tight\">LIMS</h1>\n                    <p class=\"text-[10px] font-bold text-white/80 uppercase tracking-[0.2em] mt-0.5\">Land Information Management System</p>\n                </div>\n            </div>\n        </div>\n\n        <!-- Auth Card with Glassmorphism -->\n        <div class=\"bg-white/90 backdrop-blur-xl rounded-3xl shadow-2xl overflow-hidden border border-white/20\">\n            \n            <!-- Tab Navigation -->\n            <div class=\"flex bg-white/50 backdrop-blur-sm border-b border-gray-100\">\n                <button id=\"tab-public\" type=\"button\" class=\"flex-1 py-5 text-sm font-bold text-center text-emerald-700 border-b-2 border-emerald-500 bg-emerald-50/50 transition-all focus:outline-none\">\n                    PUBLIC VIEWER\n                </button>\n                <button id=\"tab-admin\" type=\"button\" class=\"flex-1 py-5 text-sm font-bold text-center text-gray-400 border-b-2 border-transparent hover:text-orange-600 hover:bg-orange-50/30 transition-all focus:outline-none\">\n                    ADMIN LOGIN\n                </button>\n            </div>\n\n            <!-- Public Access (CAPTCHA) Panel -->\n            <div id=\"panel-public\" class=\"p-8 bg-transparent\">\n                <div class=\"mb-6\">\n                    <div class=\"inline-flex items-center gap-2 bg-green-100 text-green-800 px-3 py-1 rounded-full text-xs font-semibold mb-3\">\n                        <svg class=\"w-3 h-3\" fill=\"currentColor\" viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path d=\"M10 12a2 2 0 100-4 2 2 0 000 4z\"></path>\n                            <path fill-rule=\"evenodd\" d=\"M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z\" clip-rule=\"evenodd\"></path>\n                        </svg>\n                        PUBLIC ACCESS\n                    </div>\n                    <h2 class=\"text-2xl font-bold text-gray-800 mb-2\">View Land Records</h2>\n                    <p class=\"text-gray-600 text-sm leading-relaxed\">Search and view property details on the map. No account needed.</p>\n                </div>\n\n                <form id=\"captcha-form\" class=\"space-y-5\">\n                    <input type=\"hidden\" id=\"captcha-token\" name=\"token\" value=\"{{ captcha_token }}\">\n                    <div>\n                        <div class=\"flex justify-between items-center mb-2\">\n                            <label class=\"block text-sm font-medium text-gray-700\">Type the code</label>\n                            <button type=\"button\" id=\"refresh-captcha\" class=\"text-sm text-green-600 hover:text-green-800 flex items-center gap-1 focus:outline-none transition-colors\" title=\"Get a new code\">\n                                <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n                                    <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"></path>\n                                </svg>\n                                Regenerate\n                            </button>\n                        </div>\n                        <div class=\"bg-white border-2 border-green-300 rounded-lg p-4 text-center\">\n                            <p class=\"text-3xl font-mono font-bold text-green-800 tracking-widest\" id=\"captcha-question\">{{ captcha_question }}</p>\n                        </div>\n                    </div>\n\n                    <div>\n                        <label for=\"captcha-answer\" class=\"block text-sm font-medium text-gray-700 mb-1\">Enter Code</label>\n                        <input type=\"text\" id=\"captcha-answer\" name=\"answer\" required\n                            class=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 transition text-lg\"\n                            placeholder=\"Type the letters above\" autocomplete=\"off\">\n                    </div>\n\n                    <button type=\"submit\"\n                        class=\"w-full bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg transition duration-200 shadow-md hover:shadow-lg flex items-center justify-center gap-2\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"></path>\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"></path>\n                        </svg>\n                        View Records\n                    </button>\n                </form>\n\n                <div id=\"captcha-error\" class=\"hidden mt-3 bg-red-50 text-red-700 p-3 rounded-lg text-sm\">Error Message</div>\n\n                <p class=\"mt-4 text-xs text-gray-500\">\n                    Viewers can search parcels and view basic details. Owner information is partially masked.\n                </p>\n            </div>\n\n            <!-- Official Login Panel -->\n            <div id=\"panel-admin\" class=\"p-8 bg-transparent hidden\">\n                <div class=\"mb-6\">\n                    <div class=\"inline-flex items-center gap-2 bg-orange-100 text-orange-800 px-3 py-1 rounded-full text-xs font-semibold mb-3\">\n                        <svg class=\"w-3 h-3\" fill=\"currentColor\" viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path fill-rule=\"evenodd\" d=\"M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z\" clip-rule=\"evenodd\"></path>\n                        </svg>\n                        OFFICIAL ACCESS\n                    </div>\n                    <h2 class=\"text-2xl font-bold text-gray-800 mb-2\">Admin Login</h2>\n                    <p class=\"text-gray-600 text-sm leading-relaxed\">For Administrators and Managers. Access full CRUD operations.</p>\n                </div>\n\n                <form id=\"login-form\" class=\"space-y-5\">\n                    <div>\n                        <label for=\"username\" class=\"block text-sm font-medium text-gray-700 mb-1\">Username</label>\n                        <input type=\"text\" id=\"username\" name=\"username\" required\n                            class=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 transition\"\n                            placeholder=\"Enter your username\" autocomplete=\"username\">\n                    </div>\n\n                    <div class=\"mb-5\">\n                        <label for=\"password\" class=\"block text-sm font-medium text-gray-700 mb-1\">Password</label>\n                        <input type=\"password\" id=\"password\" name=\"password\" required\n                            class=\"w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-orange-500 focus:border-orange-500 transition\"\n                            placeholder=\"Enter your password\" autocomplete=\"current-password\">\n                        <div class=\"flex justify-end mt-1.5\">\n                            <button type=\"button\" id=\"btn-forgot-password\" class=\"text-xs text-orange-600 hover:text-orange-800 font-medium transition focus:outline-none\">\n                                Forgot Password?\n                            </button>\n                        </div>\n                    </div>\n\n                    <button type=\"submit\"\n                        class=\"w-full bg-orange-600 hover:bg-orange-700 text-white font-semibold py-3 px-4 rounded-lg transition duration-200 shadow-md hover:shadow-lg flex items-center justify-center gap-2\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n                            <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1\"></path>\n                        </svg>\n                        Sign In\n                    </button>\n                </form>\n\n                <div id=\"login-error\" class=\"hidden mt-3 bg-red-50 text-red-700 p-3 rounded-lg text-sm\">Error Message</div>\n\n                <!-- Desktop recommended notice (shown only on mobile) -->\n                <p id=\"mobile-warning\" class=\"hidden mt-4 text-center text-xs text-orange-600/80 font-medium\">\n                    Please use a desktop for admin access - mobile is not suitable for data management.\n                </p>\n\n            </div>\n        </div>\n\n        <!-- Footer -->\n        <div class=\"text-center mt-4 text-[10px] text-white/60\">\n            <p>LIMS - Academic Prototype</p>\n            <p class=\"mt-0.5 text-orange-400/90 font-bold uppercase tracking-widest\">Student Project - Demonstration Only</p>\n        </div>\n    </div>\n\n    <!-- Scripts -->\n    <script src=\"{{ url_for('static', filename='js/modules/utils.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/api.js') }}\"></script>\n    <script src=\"{{ url_for('static', filename='js/auth.js') }}\"></script>\n    <script>\n        (function() {\n            const warning = document.getElementById('mobile-warning');\n            if (!warning) return;\n            function check() {\n                if (window.innerWidth < 768) {\n                    warning.classList.remove('hidden');\n                } else {\n                    warning.classList.add('hidden');\n                }\n            }\n            check();\n            window.addEventListener('resize', check);\n        })();\n    </script>\n</body>\n</html>"
  },
  {
    "path": "templates/public_viewer_v2.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, viewport-fit=cover\">\n    <title>LIMS - Public Viewer</title>\n    <script src=\"https://cdn.tailwindcss.com\"></script>\n    <link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\" />\n    <link rel=\"stylesheet\" href=\"{{ url_for('static', filename='css/style.css') }}\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🏛️</text></svg>\">\n</head>\n<body class=\"bg-gray-100 h-[100dvh] flex flex-col overflow-hidden\">\n\n    <!-- Top Navigation Bar -->\n    <nav class=\"bg-gradient-to-r from-green-600 to-green-700 shadow-lg z-50 flex-shrink-0\">\n        <div class=\"flex items-center justify-between px-4 sm:px-6 py-3\">\n            <div class=\"flex items-center gap-2 sm:gap-3\">\n                <!-- Mobile: Filter Toggle Button (Hidden as we use Floating UI now) -->\n                <button id=\"btn-toggle-filters\" class=\"hidden text-white hover:bg-white/20 transition p-2 rounded-lg\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 12h16M4 18h16\"/></svg>\n                </button>\n                <div class=\"w-8 h-8 sm:w-10 sm:h-10 bg-white rounded-lg flex items-center justify-center shadow-md\">\n                    <svg class=\"w-5 h-5 sm:w-6 sm:h-6 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                        <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/>\n                    </svg>\n                </div>\n                <div>\n                    <h1 class=\"text-lg sm:text-xl font-bold text-white\">LIMS</h1>\n                    <p class=\"text-[10px] sm:text-xs text-green-100 hidden sm:block\">Public Land Records Viewer</p>\n                </div>\n            </div>\n            <div class=\"flex items-center gap-2 sm:gap-4\">\n                <div id=\"real-time-clock\" class=\"text-xs sm:text-sm font-medium text-white/90 bg-black/20 px-2 sm:px-3 py-1.5 rounded-lg flex flex-col justify-center items-center\">\n                    <span id=\"rtc-time\" class=\"block font-bold\">--:--:--</span>\n                    <span id=\"rtc-date\" class=\"text-[9px] sm:text-[10px] uppercase tracking-wider hidden sm:block\">--/--/----</span>\n                </div>\n                <div class=\"hidden sm:flex items-center gap-2 bg-white/20 backdrop-blur px-4 py-2 rounded-lg\">\n                    <div class=\"w-2 h-2 bg-green-400 rounded-full\"></div>\n                    <span class=\"text-sm font-medium text-white\">Viewer</span>\n                    <span class=\"text-xs bg-white/30 text-white px-2 py-0.5 rounded-full font-semibold\">READ-ONLY</span>\n                </div>\n                <!-- Primary CTA for Public Viewer -->\n                <button id=\"btn-help-support\" class=\"flex bg-orange-500 hover:bg-orange-600 text-white transition px-3 sm:px-4 py-1.5 sm:py-2 rounded-lg items-center gap-1.5 sm:gap-2 text-xs sm:text-sm font-bold shadow-md\" title=\"Report an Issue\">\n                    <svg class=\"w-4 h-4 sm:w-4 sm:h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>\n                    <span class=\"hidden sm:inline\">Report Issue</span>\n                    <span class=\"sm:hidden\">Report</span>\n                </button>\n                <button id=\"btn-toggle-darkmode\" class=\"text-white/80 hover:text-white hover:bg-white/20 transition p-2 rounded-lg\" title=\"Toggle Dark Mode\">\n                    <svg id=\"sun-icon\" class=\"w-5 h-5 hidden\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707m12.728 0l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707-.707M12 8a4 4 0 110 8 4 4 0 010-8z\"/></svg>\n                    <svg id=\"moon-icon\" class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z\"/></svg>\n                </button>\n                <button id=\"btn-logout\" class=\"text-white/80 hover:text-white hover:bg-white/20 transition p-2 rounded-lg\" title=\"Logout\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1\"/></svg>\n                </button>\n            </div>\n        </div>\n    </nav>\n\n    <!-- Main Layout: Left Sidebar (Filters) + Map -->\n    <div class=\"flex-1 flex overflow-hidden relative\">\n\n        <!-- Mobile Overlay Backdrop -->\n                <div id=\"filter-overlay\" class=\"fixed inset-0 bg-black/60 backdrop-blur-sm z-[1000] hidden md:hidden transition-opacity\"></div>\n\n                <!-- Floating Filter UI (Mobile) / Left Sidebar (Desktop) -->\n                <div id=\"filter-sidebar\" class=\"fixed md:relative inset-x-3 bottom-4 top-auto md:inset-y-0 md:left-0 md:bottom-auto md:top-auto md:w-80 bg-white rounded-2xl md:rounded-none shadow-[0_10px_40px_rgba(0,0,0,0.3)] md:shadow-lg z-[1001] md:z-40 flex flex-col max-h-[85vh] md:max-h-none transition-transform duration-300 ease-in-out overflow-hidden\" style=\"transform: translateY(120%);\">\n                    \n                    <!-- Mobile Drag Handle -->\n                    <div class=\"w-full flex justify-center pt-3 pb-1 md:hidden flex-shrink-0 bg-white\">\n                        <div class=\"w-12 h-1.5 bg-gray-300 rounded-full\"></div>\n                    </div>\n\n                    <div class=\"px-4 pb-3 pt-1 md:pt-4 border-b border-gray-200 flex justify-between items-center flex-shrink-0 bg-white\">\n                        <div class=\"flex items-center gap-2\">\n                            <svg class=\"w-5 h-5 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n                                <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z\"/>\n                            </svg>\n                            <h2 class=\"text-lg font-bold text-gray-800\">Filter Parcels</h2>\n                        </div>\n                        <!-- Mobile Close Button -->\n                        <button id=\"btn-close-filters\" class=\"md:hidden text-gray-500 hover:text-gray-800 transition p-1.5 bg-gray-100 hover:bg-gray-200 rounded-full\">\n                            <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                        </button>\n                    </div>\n\n                    <!-- Scrollable content -->\n                    <div class=\"p-4 space-y-4 overflow-y-auto flex-1 bg-white\">\n\n                <!-- Search -->\n                <div>\n                    <label class=\"block text-sm font-semibold text-gray-700 mb-2\">Search</label>\n                    <div class=\"relative\">\n                        <input type=\"text\" id=\"search-input\" placeholder=\"Khasra / ULPIN / Village...\"\n                            class=\"w-full pl-9 pr-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500\">\n                        <svg class=\"w-4 h-4 text-gray-400 absolute left-3 top-1/2 -translate-y-1/2\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"/></svg>\n                    </div>\n                </div>\n\n                <!-- State -->\n                <div>\n                    <label class=\"block text-sm font-semibold text-gray-700 mb-2\">State</label>\n                    <select id=\"filter-state\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500\">\n                        <option value=\"\">All States</option>\n                    </select>\n                </div>\n\n                <!-- District -->\n                <div>\n                    <label class=\"block text-sm font-semibold text-gray-700 mb-2\">District</label>\n                    <select id=\"filter-district\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500\">\n                        <option value=\"\">All Districts</option>\n                    </select>\n                </div>\n\n                <!-- Village -->\n                <div>\n                    <label class=\"block text-sm font-semibold text-gray-700 mb-2\">Village / Ward</label>\n                    <select id=\"filter-village\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500\">\n                        <option value=\"\">All Villages</option>\n                    </select>\n                </div>\n\n                <!-- Land Use -->\n                <div>\n                    <label class=\"block text-sm font-semibold text-gray-700 mb-2\">Land Use Type</label>\n                    <select id=\"filter-land-use\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-green-500 focus:border-green-500\">\n                        <option value=\"\">All Types</option>\n                        <option value=\"Agricultural\">Agricultural</option>\n                        <option value=\"Residential\">Residential</option>\n                        <option value=\"Commercial\">Commercial</option>\n                        <option value=\"Industrial\">Industrial</option>\n                        <option value=\"Government\">Government</option>\n                        <option value=\"Forest\">Forest</option>\n                        <option value=\"Wasteland\">Wasteland</option>\n                    </select>\n                </div>\n\n                <!-- Action Buttons -->\n                <div class=\"space-y-2 pt-2\">\n                    <button id=\"btn-apply-filters\" class=\"w-full px-4 py-2.5 bg-green-600 hover:bg-green-700 text-white rounded-lg transition text-sm font-semibold flex items-center justify-center gap-2 shadow-sm\">\n                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"/></svg>\n                        Apply Filters\n                    </button>\n                    <button id=\"btn-clear-filters\" class=\"w-full px-4 py-2.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition text-sm font-semibold flex items-center justify-center gap-2\">\n                        <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                        Clear All\n                    </button>\n                </div>\n\n                <!-- Results Counter -->\n                <div id=\"filter-results\" class=\"bg-green-50 border border-green-200 rounded-lg p-3 text-center\">\n                    <p class=\"text-sm text-gray-600\">Showing</p>\n                    <p class=\"text-2xl font-bold text-green-700\" id=\"results-count\">0</p>\n                    <p class=\"text-sm text-gray-600\">parcels</p>\n                </div>\n\n            </div>\n\n            <!-- Bottom: Version -->\n                    <div class=\"border-t p-3 bg-gray-50 flex-shrink-0 md:mt-auto rounded-b-2xl md:rounded-none\">\n                <p class=\"text-xs text-gray-500 text-center\">LIMS v1.0</p>\n            </div>\n        </div>\n\n        <!-- Main Content: Map + Details Side Panel -->\n        <div class=\"flex-1 flex overflow-hidden relative\">\n            <!-- Map Container -->\n            <div id=\"map-wrapper\" class=\"flex-1 relative\" style=\"min-height: 0;\">\n                <div id=\"map\" style=\"position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 100%; height: 100%;\"></div>\n\n                <!-- Layer Switcher: compact pill tabs top-right, always visible -->\n                <div id=\"layer-switcher\" class=\"absolute top-3 right-3 z-[500] flex flex-col items-end gap-2 pointer-events-none\">\n                    <!-- Basemap Tabs -->\n                    <div class=\"bg-white/95 backdrop-blur rounded-xl shadow-md border border-gray-100 flex items-center p-1 gap-0.5 pointer-events-auto\">\n                        <button data-basemap=\"osm\"   class=\"basemap-btn active-basemap px-2.5 py-1 text-[11px] font-semibold rounded-lg transition-all\">Street</button>\n                        <button data-basemap=\"google\" class=\"basemap-btn px-2.5 py-1 text-[11px] font-semibold rounded-lg transition-all text-gray-500 hover:bg-gray-100\">Google</button>\n                        <button data-basemap=\"esri\"   class=\"basemap-btn px-2.5 py-1 text-[11px] font-semibold rounded-lg transition-all text-gray-500 hover:bg-gray-100\">Sat</button>\n                    </div>\n                    <!-- Legend: always visible on desktop, hidden on mobile (use floating toggle instead) -->\n                    <div id=\"legend-desktop\" class=\"hidden sm:block bg-white/95 backdrop-blur rounded-xl shadow-md border border-gray-100 p-3 pointer-events-auto\">\n                        <p class=\"text-[9px] font-bold text-gray-400 uppercase tracking-wider mb-2\">Land Use</p>\n                        <div class=\"space-y-1.5\">\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#22c55e\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Agricultural</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#3b82f6\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Residential</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#f59e0b\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Commercial</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#8b5cf6\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Industrial</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#ef4444\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Government</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#065f46\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Forest</span></div>\n                            <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#9ca3af\"></div><span class=\"text-[11px] text-gray-700 font-medium\">Wasteland</span></div>\n                        </div>\n                    </div>\n                </div>\n                <style>\n                    .basemap-btn.active-basemap { background:#16a34a; color:#fff; }\n                    /* Shift map controls when the right panel is open */\n                    #layer-switcher {\n                        transition: right 0.3s ease-in-out;\n                    }\n                    #layer-switcher.panel-open {\n                        right: 440px; /* Sidebar (420px) + Gap (20px) */\n                    }\n                    /* On mobile, we hide the legend anyway when panel is open */\n                    @media (max-width: 768px) {\n                        #layer-switcher.panel-open {\n                            right: 12px;\n                            opacity: 0;\n                            pointer-events: none;\n                        }\n                    }\n                </style>\n                <style>\n                    /* Dark Mode Transitions */\n                    body { transition: background-color 0.3s ease; }\n                    .dark-mode-transition * { transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease; }\n                    \n                    /* Dark Mode Overrides */\n                    .dark #filter-sidebar, .dark #parcel-panel, .dark #legend-desktop, .dark #legend-mobile-panel, .dark .bg-white {\n                        background-color: #1e293b !important; /* slate-800 */\n                        color: #f1f5f9 !important; /* slate-100 */\n                        border-color: #334155 !important; /* slate-700 */\n                    }\n                    .dark #parcel-body *, .dark #filter-sidebar * {\n                        color: #f1f5f9 !important;\n                    }\n                    .dark .bg-gray-50, .dark .bg-gray-100 {\n                        background-color: #0f172a !important; /* slate-900 */\n                    }\n                    .dark .text-gray-700, .dark .text-gray-800 {\n                        color: #e2e8f0 !important;\n                    }\n                    .dark .border-gray-200, .dark .border-gray-300 {\n                        border-color: #334155 !important;\n                    }\n                    .dark input, .dark select {\n                        background-color: #334155 !important;\n                        color: white !important;\n                        border-color: #475569 !important;\n                    }\n                    .dark #parcel-footer {\n                        background-color: #1e293b !important;\n                        border-top: 1px solid #334155 !important;\n                    }\n                    /* Comprehensive Text Polish */\n                    .dark .text-gray-900, .dark .text-slate-900 { color: #f8fafc !important; }\n                    .dark .text-gray-800, .dark .text-slate-800 { color: #f1f5f9 !important; }\n                    .dark .text-gray-700, .dark .text-slate-700 { color: #e2e8f0 !important; }\n                    .dark .text-gray-600, .dark .text-slate-600, .dark .text-gray-500, .dark .text-slate-500 { color: #cbd5e1 !important; }\n                    .dark .text-gray-400, .dark .text-slate-400 { color: #94a3b8 !important; }\n\n                    /* Backgrounds & Borders */\n                    .dark .bg-gray-200, .dark .bg-slate-200 { background-color: #334155 !important; }\n                    .dark .bg-gray-300, .dark .bg-slate-300 { background-color: #475569 !important; }\n                    .dark .border-gray-300, .dark .border-gray-200, .dark .border-gray-100 {\n                        border-color: #334155 !important;\n                    }\n\n                    /* Map Popups */\n                    .dark .leaflet-popup-content-wrapper, .dark .leaflet-popup-tip {\n                        background-color: #1e293b !important;\n                        color: #f1f5f9 !important;\n                        border: 1px solid #334155;\n                    }\n\n                    .dark .land-use-badge { border: 1px solid rgba(255,255,255,0.1); }\n                    .dark input::placeholder { color: #64748b !important; }\n                    \n                    /* Dark Scrollbar */\n                    .dark ::-webkit-scrollbar { width: 8px; height: 8px; }\n                    .dark ::-webkit-scrollbar-track { background: #0f172a; }\n                    .dark ::-webkit-scrollbar-thumb { background: #334155; border-radius: 4px; }\n                    .dark ::-webkit-scrollbar-thumb:hover { background: #475569; }\n                </style>\n\n                <!-- Mobile Legend: floating toggle button + slide-up panel (bottom-left) -->\n                <button id=\"btn-legend-mobile\" class=\"sm:hidden absolute bottom-20 left-3 z-[500] bg-white/95 backdrop-blur border border-gray-200 shadow-md rounded-full w-10 h-10 flex items-center justify-center pointer-events-auto\" title=\"Map Legend\">\n                    <svg class=\"w-5 h-5 text-gray-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 6h16M4 10h16M4 14h16M4 18h8\"/></svg>\n                </button>\n                <!-- Mobile Legend Panel -->\n                <div id=\"legend-mobile-panel\" class=\"sm:hidden absolute bottom-32 left-3 z-[501] bg-white/97 backdrop-blur rounded-2xl shadow-xl border border-gray-100 p-4 pointer-events-auto\" style=\"transform:translateY(20px);opacity:0;pointer-events:none;transition:transform 0.25s ease,opacity 0.25s ease;\">\n                    <div class=\"flex items-center justify-between mb-3\">\n                        <p class=\"text-xs font-bold text-gray-700 uppercase tracking-wider\">Land Use Legend</p>\n                        <button id=\"btn-legend-mobile-close\" class=\"text-gray-400 hover:text-gray-600 p-0.5 rounded-full hover:bg-gray-100 transition\">\n                            <svg class=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                        </button>\n                    </div>\n                    <div class=\"grid grid-cols-2 gap-x-4 gap-y-2\">\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#22c55e\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Agricultural</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#3b82f6\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Residential</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#f59e0b\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Commercial</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#8b5cf6\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Industrial</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#ef4444\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Government</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#065f46\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Forest</span></div>\n                        <div class=\"flex items-center gap-2\"><div class=\"w-3 h-3 rounded-sm flex-shrink-0\" style=\"background:#9ca3af\"></div><span class=\"text-[12px] text-gray-700 font-medium\">Wasteland</span></div>\n                    </div>\n                </div>\n\n\n                <!-- Mobile Floating Action Button (Search/Filter) -->\n                <button id=\"btn-mobile-fab\" class=\"md:hidden absolute bottom-6 left-1/2 transform -translate-x-1/2 z-[20] bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-full shadow-[0_8px_30px_rgb(0,0,0,0.2)] font-bold text-sm flex items-center gap-2 border border-white/20 transition-transform duration-300 active:scale-95 whitespace-nowrap\">\n                    <svg class=\"w-5 h-5 drop-shadow-md\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"/></svg>\n                    Search Records\n                </button>\n\n\n            </div>\n\n            <!-- Details Side Panel (slides in from right) -->\n            <div id=\"parcel-panel\" class=\"w-full md:w-[420px] bg-white shadow-2xl border-l border-gray-200 flex flex-col transition-transform duration-300 ease-in-out transform translate-x-full absolute right-0 top-0 bottom-0 z-[1000]\">\n                <!-- Panel Header -->\n                <div class=\"bg-gradient-to-r from-green-600 to-green-700 text-white px-4 py-3 sm:py-4 flex items-center gap-3 flex-shrink-0 shadow-md z-10\">\n                    <button id=\"parcel-close\" class=\"text-white hover:bg-white/20 transition p-2 rounded-full flex items-center justify-center -ml-1\">\n                        <svg class=\"w-5 h-5 sm:hidden\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2.5\" d=\"M15 19l-7-7 7-7\"/></svg>\n                        <svg class=\"w-6 h-6 hidden sm:block\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                    </button>\n                    <h3 class=\"font-bold text-base sm:text-lg tracking-wide\" id=\"parcel-title\">Property Details</h3>\n                </div>\n\n                <!-- Panel Body (Scrollable) -->\n                <div id=\"parcel-body\" class=\"flex-1 overflow-y-auto p-5 space-y-4\">\n                    <!-- Populated dynamically -->\n                </div>\n\n                <!-- Panel Footer (Sticky above mobile nav bar) -->\n                <div id=\"parcel-footer\" class=\"p-4 pb-[calc(1rem+env(safe-area-inset-bottom))] bg-gray-50 border-t border-gray-200 flex-shrink-0 sticky bottom-0 left-0 w-full z-[1100]\" style=\"box-shadow: 0 -2px 12px 0 rgba(0,0,0,0.07);\">\n                    <button onclick=\"printCard(document.getElementById('parcel-ulpin').textContent)\" class=\"w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 rounded-lg shadow transition flex items-center justify-center gap-2\">\n                        <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"/></svg>\n                        Download Property Card (PDF)\n                    </button>\n                </div>\n            </div>\n        </div>\n\n    </div>\n    \n    <!-- Help & Support Modal -->\n    <div id=\"help-support-modal\" class=\"fixed inset-0 z-[9999] hidden bg-black/50 flex flex-col items-center justify-center backdrop-blur-sm px-3 sm:px-4\">\n        <div class=\"bg-white rounded-xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col transform transition-all max-h-[90vh]\">\n            <!-- Modal Header -->\n            <div class=\"bg-green-600 text-white px-4 sm:px-6 py-3 sm:py-4 flex items-center justify-between\">\n                <h3 class=\"text-base sm:text-lg font-bold flex items-center gap-2\">\n                    <svg class=\"w-4 h-4 sm:w-5 sm:h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>\n                    <span class=\"hidden sm:inline\">Help & Support</span>\n                    <span class=\"sm:hidden\">Help</span>\n                </h3>\n                <button type=\"button\" id=\"close-help-modal\" class=\"text-white/80 hover:text-white transition\">\n                    <svg class=\"w-5 h-5 sm:w-6 sm:h-6\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                </button>\n            </div>\n            <!-- Modal Body -->\n            <div class=\"p-4 sm:p-6 overflow-y-auto flex-1\">\n                <form id=\"help-support-form\" class=\"space-y-3 sm:space-y-4\">\n                    <div>\n                        <label for=\"help-email\" class=\"block text-xs sm:text-sm font-semibold text-gray-700 mb-1\">Email Address <span class=\"text-red-500\">*</span></label>\n                        <input type=\"email\" id=\"help-email\" required class=\"w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 outline-none text-sm\" placeholder=\"your@email.com\">\n                    </div>\n                    <div>\n                        <label for=\"help-type\" class=\"block text-xs sm:text-sm font-semibold text-gray-700 mb-1\">Issue Type <span class=\"text-red-500\">*</span></label>\n                        <select id=\"help-type\" required class=\"w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 outline-none text-sm\">\n                            <option value=\"Incorrect Record Information\">Incorrect Record Information</option>\n                            <option value=\"Map/Boundary Issue\">Map/Boundary Issue</option>\n                            <option value=\"Technical Bug\">Technical Bug</option>\n                            <option value=\"General Feedback\">General Feedback</option>\n                            <option value=\"Other\">Other</option>\n                        </select>\n                    </div>\n                    <div>\n                        <label for=\"help-message\" class=\"block text-xs sm:text-sm font-semibold text-gray-700 mb-1\">Details/Feedback <span class=\"text-red-500\">*</span></label>\n                        <textarea id=\"help-message\" rows=\"4\" required class=\"w-full px-3 sm:px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500 outline-none resize-none text-sm\" placeholder=\"Please describe the issue or provide feedback in detail...\"></textarea>\n                    </div>\n                    <div id=\"help-message-alert\" class=\"hidden text-sm p-3 rounded-lg\"></div>\n                </form>\n            </div>\n            <!-- Modal Footer -->\n            <div class=\"bg-gray-50 px-4 sm:px-6 py-3 sm:py-4 flex justify-end gap-2 sm:gap-3 border-t border-gray-200\">\n                <button type=\"button\" id=\"cancel-help-modal\" class=\"px-3 sm:px-4 py-2 bg-white border border-gray-300 rounded-lg text-gray-700 text-sm sm:text-sm font-medium hover:bg-gray-50 transition\">Cancel</button>\n                <button type=\"submit\" form=\"help-support-form\" class=\"px-3 sm:px-4 py-2 bg-green-600 hover:bg-green-700 text-white text-sm sm:text-sm rounded-lg font-medium transition shadow-sm flex items-center gap-2\">Submit</button>\n            </div>\n        </div>\n    </div>\n\n    <!-- Scripts -->\n    <script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/html-to-image/1.11.11/html-to-image.min.js\"></script>\n    <script src=\"{{ url_for('static', filename='js/api.js') }}\"></script>\n    <script>\n        // ─── Toast notification (missing from public viewer) ──────────────────\n        (function() {\n            const container = document.createElement('div');\n            container.id = 'toast-container';\n            container.style.cssText = 'position:fixed;top:70px;right:12px;z-index:9999;display:flex;flex-direction:column;gap:8px;max-width:340px;';\n            document.body.appendChild(container);\n        })();\n\n        function showToast(message, type = 'info') {\n            const colors = { success: '#16a34a', error: '#dc2626', warning: '#d97706', info: '#2563eb' };\n            const icons  = { success: '✓', error: '✕', warning: '⚠', info: 'ℹ' };\n            const toast = document.createElement('div');\n            toast.style.cssText = `background:${colors[type]||colors.info};color:#fff;padding:10px 14px;border-radius:10px;font-size:13px;font-weight:600;box-shadow:0 4px 16px rgba(0,0,0,0.18);display:flex;align-items:center;gap:8px;opacity:0;transform:translateX(40px);transition:all 0.3s ease;`;\n            toast.innerHTML = `<span style=\"font-size:15px;\">${icons[type]||icons.info}</span><span>${message}</span>`;\n            document.getElementById('toast-container').appendChild(toast);\n            requestAnimationFrame(() => { toast.style.opacity = '1'; toast.style.transform = 'translateX(0)'; });\n            setTimeout(() => {\n                toast.style.opacity = '0'; toast.style.transform = 'translateX(40px)';\n                setTimeout(() => toast.remove(), 400);\n            }, 4000);\n        }\n\n        // Modal Help & Support Handlers\n        document.addEventListener('DOMContentLoaded', () => {\n            const btnHelp = document.getElementById('btn-help-support');\n            const modalHelp = document.getElementById('help-support-modal');\n            const closeHelp = document.getElementById('close-help-modal');\n            const cancelHelp = document.getElementById('cancel-help-modal');\n            const formHelp = document.getElementById('help-support-form');\n            const alertMsg = document.getElementById('help-message-alert');\n\n            function openHelpModal() {\n                modalHelp.classList.remove('hidden');\n                alertMsg.classList.add('hidden');\n                formHelp.reset();\n            }\n\n            function closeHelpModal() {\n                modalHelp.classList.add('hidden');\n            }\n\n            btnHelp.addEventListener('click', openHelpModal);\n            closeHelp.addEventListener('click', closeHelpModal);\n            cancelHelp.addEventListener('click', closeHelpModal);\n            modalHelp.addEventListener('click', (e) => {\n                if (e.target === modalHelp) closeHelpModal();\n            });\n\n            formHelp.addEventListener('submit', async (e) => {\n                e.preventDefault();\n                const email = document.getElementById('help-email').value;\n                const type = document.getElementById('help-type').value;\n                const message = document.getElementById('help-message').value;\n                \n                try {\n                    const res = await fetch('/api/feedback', {\n                        method: 'POST',\n                        headers: { 'Content-Type': 'application/json' },\n                        body: JSON.stringify({ email, type, message })\n                    });\n                    const data = await res.json();\n                    \n                    alertMsg.classList.remove('hidden', 'bg-red-100', 'text-red-700', 'bg-green-100', 'text-green-700');\n                    if (res.ok) {\n                        alertMsg.classList.add('bg-green-100', 'text-green-700');\n                        alertMsg.textContent = data.message || \"Feedback submitted successfully!\";\n                        setTimeout(closeHelpModal, 2000);\n                    } else {\n                        alertMsg.classList.add('bg-red-100', 'text-red-700');\n                        alertMsg.textContent = data.error || \"Failed to submit feedback.\";\n                    }\n                } catch (error) {\n                    alertMsg.classList.remove('hidden');\n                    alertMsg.classList.add('bg-red-100', 'text-red-700');\n                    alertMsg.textContent = \"Network error. Please try again.\";\n                }\n            });\n        });\n\n        // Mobile Filter Drawer Handlers\n        let closeFilterDrawerGlobal = null;\n        let openFilterDrawerGlobal = null;\n        \n        document.addEventListener('DOMContentLoaded', () => {\n            const btnToggle = document.getElementById('btn-toggle-filters');\n            const btnClose = document.getElementById('btn-close-filters');\n            const sidebar = document.getElementById('filter-sidebar');\n            const overlay = document.getElementById('filter-overlay');\n            const btnMobileFab = document.getElementById('btn-mobile-fab');\n\n            // On desktop (md+), reset inline transform so sidebar is always shown\n            function resetForDesktop() {\n                if (window.innerWidth >= 768) {\n                    sidebar.style.transform = '';\n                }\n            }\n            resetForDesktop();\n            window.addEventListener('resize', resetForDesktop);\n\n            function openFilterDrawer() {\n                sidebar.style.transform = 'translateY(0)';\n                overlay.classList.remove('hidden');\n                if (btnMobileFab) btnMobileFab.style.opacity = '0';\n                if (btnMobileFab) btnMobileFab.style.pointerEvents = 'none';\n            }\n\n            function closeFilterDrawer() {\n                sidebar.style.transform = 'translateY(120%)';\n                overlay.classList.add('hidden');\n                if (btnMobileFab) btnMobileFab.style.opacity = '1';\n                if (btnMobileFab) btnMobileFab.style.pointerEvents = 'auto';\n            }\n            \n            closeFilterDrawerGlobal = closeFilterDrawer;\n            openFilterDrawerGlobal = openFilterDrawer;\n\n            if (btnToggle) {\n                btnToggle.addEventListener('click', openFilterDrawer);\n            }\n            if (btnMobileFab) {\n                btnMobileFab.addEventListener('click', openFilterDrawer);\n            }\n            if (btnClose) {\n                btnClose.addEventListener('click', closeFilterDrawer);\n            }\n            if (overlay) {\n                overlay.addEventListener('click', closeFilterDrawer);\n            }\n\n            // Dark Mode Logic\n            const btnDark = document.getElementById('btn-toggle-darkmode');\n            const sunIcon = document.getElementById('sun-icon');\n            const moonIcon = document.getElementById('moon-icon');\n            \n            function setTheme(isDark) {\n                if (isDark) {\n                    document.documentElement.classList.add('dark');\n                    sunIcon.classList.remove('hidden');\n                    moonIcon.classList.add('hidden');\n                } else {\n                    document.documentElement.classList.remove('dark');\n                    sunIcon.classList.add('hidden');\n                    moonIcon.classList.remove('hidden');\n                }\n                localStorage.setItem('lims-theme', isDark ? 'dark' : 'light');\n            }\n\n            btnDark.addEventListener('click', () => {\n                const isDark = !document.documentElement.classList.contains('dark');\n                setTheme(isDark);\n            });\n\n            // Init theme from storage\n            if (localStorage.getItem('lims-theme') === 'dark') {\n                setTheme(true);\n            }\n        });\n\n        function updateBasemapButtons(activeVal) {\n            document.querySelectorAll('.basemap-btn').forEach(b => {\n                b.classList.remove('active-basemap');\n                b.classList.add('text-gray-500');\n                if (b.dataset.basemap === activeVal) {\n                    b.classList.add('active-basemap');\n                    b.classList.remove('text-gray-500');\n                }\n            });\n        }\n\n        let map = null;\n        let baseLayers = {};\n        let currentBaseLayer = 'osm';\n        let parcelLayers = [];\n        let allRecords = [];\n        let filteredRecords = [];\n        let mapInitialized = false;\n\n        function initMap() {\n            if (mapInitialized) return;\n\n            try {\n                // Region lock: Expanded India bounding box with generous margins\n                const indiaBounds = L.latLngBounds(\n                    L.latLng(4.0, 60.0),   // Southwest (extended into ocean)\n                    L.latLng(40.0, 105.0)  // Northeast (extended into China/Myanmar)\n                );\n\n                map = L.map('map', {\n                    center: [23.5, 77.5],\n                    zoom: 7,\n                    minZoom: 4,\n                    maxZoom: 18,\n                    maxBounds: indiaBounds,\n                    maxBoundsViscosity: 1.0,\n                    zoomControl: true\n                });\n\n                baseLayers.osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n                    attribution: '&copy; OpenStreetMap contributors',\n                    maxZoom: 19\n                });\n\n                baseLayers.google = L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {\n                    attribution: '&copy; Google Maps',\n                    maxZoom: 20\n                });\n\n                baseLayers.esri = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {\n                    attribution: '&copy; Esri',\n                    maxZoom: 18\n                });\n\n                baseLayers.osm.addTo(map);\n\n                // Add India boundary mask\n                addIndiaMask(map);\n\n                // Basemap switcher (button-based pill tabs)\n                document.querySelectorAll('.basemap-btn').forEach(btn => {\n                    btn.addEventListener('click', function() {\n                        const val = this.dataset.basemap;\n                        if (baseLayers[val] && val !== currentBaseLayer) {\n                            map.removeLayer(baseLayers[currentBaseLayer]);\n                            baseLayers[val].addTo(map);\n                            currentBaseLayer = val;\n                        }\n                        document.querySelectorAll('.basemap-btn').forEach(b => {\n                            b.classList.remove('active-basemap');\n                            b.classList.add('text-gray-500');\n                        });\n                        this.classList.add('active-basemap');\n                        this.classList.remove('text-gray-500');\n                    });\n                });\n\n                // Coordinate display\n                map.on('mousemove', function(e) {\n                    const coordsEl = document.getElementById('cursor-coords');\n                    if (coordsEl) {\n                        coordsEl.textContent = `Lat: ${e.latlng.lat.toFixed(5)}, Lng: ${e.latlng.lng.toFixed(5)}`;\n                    }\n                });\n\n\n                mapInitialized = true;\n                console.log('Map initialized with region lock');\n            } catch (error) {\n                console.error('Map initialization failed:', error);\n            }\n        }\n\n        // Add India boundary mask to dim everything outside India\n        async function addIndiaMask(mapInstance) {\n            try {\n                const response = await fetch('/api/boundary');\n                if (!response.ok) {\n                    console.warn('Failed to load India boundary');\n                    return;\n                }\n\n                const indiaGeoJSON = await response.json();\n\n                if (!indiaGeoJSON || !indiaGeoJSON.features || indiaGeoJSON.features.length === 0) {\n                    console.warn('India boundary GeoJSON has no features');\n                    return;\n                }\n\n                // Create a mask: large rectangle with India cut out as a hole\n                const worldBounds = [\n                    [-180, -90],\n                    [180, -90],\n                    [180, 90],\n                    [-180, 90],\n                    [-180, -90]\n                ];\n\n                let maskCoordinates = [worldBounds];\n                const geometry = indiaGeoJSON.features[0].geometry;\n\n                if (geometry.type === 'MultiPolygon') {\n                    geometry.coordinates.forEach(polygon => {\n                        maskCoordinates.push(polygon[0]);\n                    });\n                } else if (geometry.type === 'Polygon') {\n                    maskCoordinates.push(geometry.coordinates[0]);\n                }\n\n                const maskGeoJSON = {\n                    type: 'Feature',\n                    geometry: {\n                        type: 'Polygon',\n                        coordinates: maskCoordinates\n                    }\n                };\n\n                // Add the mask layer\n                const maskLayer = L.geoJSON(maskGeoJSON, {\n                    style: function() {\n                        return {\n                            color: '#1f2937',\n                            weight: 2,\n                            fillColor: '#1f2937',\n                            fillOpacity: 0.7\n                        };\n                    },\n                    interactive: false,\n                    isMask: true\n                });\n\n                maskLayer.addTo(mapInstance);\n\n                // Add India border outline for clarity\n                const indiaBorder = L.geoJSON(indiaGeoJSON, {\n                    style: function() {\n                        return {\n                            color: '#16a34a',  // Green border for public viewer\n                            weight: 3,\n                            fillColor: 'transparent',\n                            fillOpacity: 0,\n                            opacity: 0.9\n                        };\n                    },\n                    interactive: false,\n                    isMask: true\n                });\n\n                indiaBorder.addTo(mapInstance);\n                console.log('India boundary mask loaded');\n            } catch (err) {\n                console.warn('Failed to load India boundary mask:', err);\n            }\n        }\n\n        function addRecordsToMap(records) {\n            if (!map || !mapInitialized) return;\n\n            // Clear existing layers\n            parcelLayers.forEach(layer => map.removeLayer(layer));\n            parcelLayers = [];\n\n            const landUseColors = {\n                Agricultural: '#22c55e',\n                Residential: '#3b82f6',\n                Commercial: '#f59e0b',\n                Industrial: '#8b5cf6',\n                Government: '#ef4444',\n                Forest: '#065f46',\n                Wasteland: '#9ca3af'\n            };\n\n            if (records.length === 0) {\n                showToast('No parcels match your filters', 'warning');\n                return;\n            }\n\n            records.forEach(rec => {\n                if (!rec.geometry) return;\n\n                const landUse = (rec.attributes && rec.attributes.land_use) || 'Unknown';\n                const color = landUseColors[landUse] || '#6b7280';\n\n                const geoJsonLayer = L.geoJSON(rec.geometry, {\n                    style: function() {\n                        return {\n                            color: color,\n                            fillColor: color,\n                            fillOpacity: 0.25,\n                            weight: 2.5,\n                            opacity: 0.9\n                        };\n                    },\n                    onEachFeature: function(_feature, layer) {\n                        layer.on('click', function() {\n                            showParcelDetails(rec);\n                            map.fitBounds(L.geoJSON(rec.geometry).getBounds(), { padding: [50, 50], maxZoom: 16 });\n                        });\n\n                        layer.on('mouseover', function() {\n                            this.setStyle({ fillOpacity: 0.45, weight: 3.5 });\n                        });\n                        layer.on('mouseout', function() {\n                            this.setStyle({ fillOpacity: 0.25, weight: 2.5 });\n                        });\n\n                        const attrs = rec.attributes || {};\n                        layer.bindTooltip(\n                            `<div class=\"font-sans text-sm\">\n                                <div class=\"font-bold mb-1\">${rec.khasra_no || 'N/A'}</div>\n                                <div class=\"text-xs text-gray-600\">ULPIN: ${rec.ulpin || 'N/A'}</div>\n                                <div class=\"text-xs text-gray-600\">Type: ${landUse}</div>\n                                <div class=\"text-xs text-gray-600\">Area: ${attrs.area_ha || '?'} Ha</div>\n                            </div>`,\n                            { sticky: true, direction: 'top', offset: [0, -10] }\n                        );\n                    }\n                });\n\n                geoJsonLayer.addTo(map);\n                parcelLayers.push(geoJsonLayer);\n            });\n\n            // Auto-zoom to fit all filtered parcels\n            if (parcelLayers.length > 0) {\n                const group = L.featureGroup(parcelLayers);\n                map.fitBounds(group.getBounds(), { padding: [50, 50], maxZoom: 15 });\n            }\n        }\n\n        function showParcelDetails(record) {\n            const panel = document.getElementById('parcel-panel');\n            const title = document.getElementById('parcel-title');\n            const body = document.getElementById('parcel-body');\n\n            const loc = record.location || {};\n            const attrs = record.attributes || {};\n            const owner = record.owner || {};\n            const value = (parseFloat(attrs.area_ha) || 0) * (parseFloat(attrs.circle_rate_inr) || 0);\n\n            title.textContent = `Khasra: ${record.khasra_no || 'N/A'}`;\n            body.innerHTML = `\n                <!-- Hidden ULPIN for print button -->\n                <p id=\"parcel-ulpin\" class=\"hidden\">${record.ulpin || ''}</p>\n                \n                <!-- Basic Info Grid -->\n                <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"/></svg>\n                        Parcel Information\n                    </h4>\n                    <div class=\"grid grid-cols-2 gap-3 text-sm\">\n                        <div><p class=\"text-xs text-gray-500\">ULPIN</p><p class=\"font-mono font-semibold\">${record.ulpin || 'N/A'}</p></div>\n                        <div><p class=\"text-xs text-gray-500\">Khata No.</p><p class=\"font-semibold\">${record.khata_no || 'N/A'}</p></div>\n                        <div><p class=\"text-xs text-gray-500\">Area</p><p class=\"font-semibold\">${attrs.area_ha || 'N/A'} Ha</p></div>\n                        <div><p class=\"text-xs text-gray-500\">Land Use</p><p class=\"font-semibold\">${attrs.land_use || 'N/A'}</p></div>\n                    </div>\n                </div>\n\n                <!-- Location -->\n                <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z\"/><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 11a3 3 0 11-6 0 3 3 0 016 0z\"/></svg>\n                        Location\n                    </h4>\n                    <div class=\"space-y-2 text-sm\">\n                        <div class=\"flex justify-between\"><span class=\"text-gray-500\">State</span><span class=\"font-medium\">${loc.state || 'N/A'}</span></div>\n                        <div class=\"flex justify-between\"><span class=\"text-gray-500\">District</span><span class=\"font-medium\">${loc.district || 'N/A'}</span></div>\n                        <div class=\"flex justify-between\"><span class=\"text-gray-500\">Village</span><span class=\"font-medium\">${loc.village || 'N/A'}</span></div>\n                    </div>\n                </div>\n\n                <!-- Valuation -->\n                <div class=\"bg-green-50 border border-green-200 rounded-lg p-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-2 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4 text-green-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"/></svg>\n                        Valuation\n                    </h4>\n                    <p class=\"text-xs text-gray-500 mb-1\">Estimated Value (Area × Circle Rate)</p>\n                    <p class=\"text-2xl font-bold text-green-700\">Rs. ${Math.round(value).toLocaleString('en-IN')}</p>\n                </div>\n\n                <!-- Technical Specs -->\n                <div class=\"bg-blue-50 border border-blue-100 rounded-lg p-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4 text-blue-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7\"/></svg>\n                        Technical Specifications\n                    </h4>\n                    <div class=\"space-y-2 text-sm\">\n                        <div class=\"flex justify-between\"><span class=\"text-gray-500\">Centroid (GPS)</span><span class=\"font-mono font-medium\">${attrs.centroid ? `${attrs.centroid.lat}, ${attrs.centroid.lng}` : 'N/A'}</span></div>\n                        <div class=\"flex justify-between\"><span class=\"text-gray-500\">Perimeter</span><span class=\"font-medium\">${attrs.perimeter_m ? `${Math.round(attrs.perimeter_m)} m` : 'N/A'}</span></div>\n                    </div>\n                </div>\n\n                <!-- Owner -->\n                <div class=\"bg-gray-50 border border-gray-200 rounded-lg p-4\">\n                    <h4 class=\"text-sm font-semibold text-gray-700 mb-3 flex items-center gap-2\">\n                        <svg class=\"w-4 h-4 text-purple-600\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z\"/></svg>\n                        Owner (Partial View)\n                    </h4>\n                    <div class=\"grid grid-cols-2 gap-3 text-sm\">\n                        <div><p class=\"text-xs text-gray-500\">Name</p><p class=\"font-semibold\">${maskName(owner.name || 'N/A')}</p></div>\n                        <div><p class=\"text-xs text-gray-500\">Share</p><p class=\"font-semibold\">${owner.share_pct || 'N/A'}%</p></div>\n                    </div>\n                    <p class=\"text-xs text-gray-400 mt-2 italic\">Full details available to authorized officials only</p>\n                    <p class=\"text-xs text-gray-400 mt-2 italic\">Full details available to authorized administrators only</p>\n                </div>\n            `;\n\n            // Show panel with slide-in animation\n            panel.classList.remove('translate-x-full');\n            panel.classList.add('translate-x-0');\n            \n            // Shift map controls to the left\n            const switcher = document.getElementById('layer-switcher');\n            if (switcher) switcher.classList.add('panel-open');\n            \n            // Resize map after panel animation completes (300ms)\n            if (map && mapInitialized) {\n                setTimeout(() => map.invalidateSize(), 320);\n            }\n        }\n\n        // Add close handler to remove the shift\n        document.addEventListener('DOMContentLoaded', () => {\n            const closeBtn = document.getElementById('parcel-close');\n            const panel = document.getElementById('parcel-panel');\n            const switcher = document.getElementById('layer-switcher');\n            \n            if (closeBtn) {\n                closeBtn.addEventListener('click', () => {\n                    panel.classList.remove('translate-x-0');\n                    panel.classList.add('translate-x-full');\n                    if (switcher) switcher.classList.remove('panel-open');\n                    \n                    // Resize map back\n                    setTimeout(() => { if (map) map.invalidateSize(); }, 320);\n                });\n            }\n        });\n\n        function maskName(name) {\n            if (!name || name === 'N/A') return 'N/A';\n            const parts = name.split(' ');\n            if (parts.length <= 1) return name;\n            return parts[0] + ' ' + parts.slice(1).map(p => p[0] + '.').join(' ');\n        }\n\n        async function capturePolygonMapForPdf(geometry, landUse) {\n            return new Promise((resolve) => {\n                const landUseColors = {\n                    Agricultural: '#22c55e', Residential: '#3b82f6', Commercial: '#f59e0b',\n                    Industrial: '#8b5cf6', Government: '#ef4444', Forest: '#065f46', Wasteland: '#9ca3af'\n                };\n                const color = landUseColors[landUse] || '#16a34a';\n\n                const W = 800, H = 450;\n                const wrap = document.createElement('div');\n                wrap.style.cssText = `position:fixed;left:0;top:0;width:${W}px;height:${H}px;z-index:99999;opacity:0.01;pointer-events:none;`;\n                document.body.appendChild(wrap);\n                const pdfMap = L.map(wrap, { zoomControl: false, attributionControl: false });\n                L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19 }).addTo(pdfMap);\n                if (geometry && geometry.coordinates && geometry.coordinates[0]) {\n                    const latlngs = geometry.coordinates[0].map(c => [c[1], c[0]]);\n                    const poly = L.polygon(latlngs, { color: color, weight: 3, fillColor: color, fillOpacity: 0.35 }).addTo(pdfMap);\n                    pdfMap.fitBounds(poly.getBounds(), { padding: [55, 55] });\n                } else {\n                    pdfMap.setView([23.5, 77.5], 5);\n                }\n                setTimeout(async () => {\n                    try {\n                        wrap.style.opacity = '1';\n                        const dataUrl = await window.htmlToImage.toPng(wrap, { pixelRatio: 1.5, width: W, height: H });\n                        resolve(dataUrl);\n                    } catch (e) { resolve(null); }\n                    finally {\n                        try { pdfMap.remove(); } catch (_) {}\n                        try { document.body.removeChild(wrap); } catch (_) {}\n                    }\n                }, 2500);\n            });\n        }\n\n        async function printCard(ulpin) {\n            if (!ulpin) { showToast('No ULPIN available', 'error'); return; }\n            showToast('Generating PDF — capturing map, please wait...', 'info');\n\n            // Get geometry and land use of the currently open parcel\n            const currentRecord = allRecords.find(r => r.ulpin === ulpin);\n            const geometry = currentRecord && currentRecord.geometry;\n            const landUse = currentRecord && currentRecord.attributes && currentRecord.attributes.land_use;\n\n            let mapImageBase64 = null;\n            if (geometry && typeof window.htmlToImage !== 'undefined') {\n                mapImageBase64 = await capturePolygonMapForPdf(geometry, landUse);\n            }\n\n            try {\n                const res = await fetch(`/api/print-card/${ulpin}`, {\n                    method: 'POST',\n                    headers: { 'Content-Type': 'application/json' },\n                    body: JSON.stringify({ map_image: mapImageBase64 })\n                });\n                if (!res.ok) {\n                    const err = await res.json();\n                    throw new Error(err.error || 'Failed to generate');\n                }\n                const blob = await res.blob();\n                const url = window.URL.createObjectURL(blob);\n                const a = document.createElement('a');\n                a.href = url; a.download = `Property_Card_${ulpin}.pdf`;\n                document.body.appendChild(a); a.click();\n                document.body.removeChild(a);\n                window.URL.revokeObjectURL(url);\n                showToast('PDF downloaded successfully!', 'success');\n            } catch (err) {\n                showToast(err.message, 'error');\n            }\n        }\n\n        // Load records\n        async function loadRecords() {\n            try {\n                const loadingEl = document.getElementById('map-loading');\n                if (loadingEl) loadingEl.classList.remove('hidden');\n\n                // Try api.js fetchRecords, fall back to direct fetch\n                let records = [];\n                try {\n                    records = await fetchRecords();\n                } catch (e) {\n                    console.warn('fetchRecords failed, trying direct fetch:', e);\n                    const res = await fetch('/api/records', { credentials: 'include' });\n                    if (res.ok) records = await res.json();\n                }\n\n                allRecords = Array.isArray(records) ? records : [];\n                console.log('Loaded', allRecords.length, 'records');\n                buildFilterOptions(allRecords);\n                applyFilters();\n\n                if (loadingEl) loadingEl.classList.add('hidden');\n            } catch (err) {\n                console.error('Failed to load records:', err);\n                showToast('Failed to load records: ' + err.message, 'error');\n                const loadingEl = document.getElementById('map-loading');\n                if (loadingEl) loadingEl.classList.add('hidden');\n            }\n        }\n\n        // Cascading filter helpers\n        function getUniqueFromRecords(records, field) {\n            return [...new Set(records.map(r => (r.location || {})[field] || '').filter(Boolean))].sort();\n        }\n\n        function buildFilterOptions(records) {\n            // Populate states from all records\n            const states = getUniqueFromRecords(records, 'state');\n            populateSelect('filter-state', states, 'All States');\n\n            // Districts and villages default to all (no state selected yet)\n            updateDistrictOptions();\n            updateVillageOptions();\n        }\n\n        function updateDistrictOptions() {\n            const stateVal = document.getElementById('filter-state').value;\n            const source = stateVal\n                ? allRecords.filter(r => (r.location || {}).state === stateVal)\n                : allRecords;\n            const districts = getUniqueFromRecords(source, 'district');\n            const districtEl = document.getElementById('filter-district');\n            const currentDistrict = districtEl.value;\n            populateSelect('filter-district', districts, 'All Districts');\n            // Re-select if still valid\n            if (districts.includes(currentDistrict)) districtEl.value = currentDistrict;\n        }\n\n        function updateVillageOptions() {\n            const stateVal = document.getElementById('filter-state').value;\n            const districtVal = document.getElementById('filter-district').value;\n            let source = allRecords;\n            if (stateVal) source = source.filter(r => (r.location || {}).state === stateVal);\n            if (districtVal) source = source.filter(r => (r.location || {}).district === districtVal);\n            const villages = getUniqueFromRecords(source, 'village');\n            const villageEl = document.getElementById('filter-village');\n            const currentVillage = villageEl.value;\n            populateSelect('filter-village', villages, 'All Villages');\n            if (villages.includes(currentVillage)) villageEl.value = currentVillage;\n        }\n\n        function populateSelect(id, values, placeholder) {\n            const el = document.getElementById(id);\n            if (!el) return;\n            el.innerHTML = `<option value=\"\">${placeholder}</option>`;\n            values.forEach(v => {\n                const opt = document.createElement('option');\n                opt.value = v;\n                opt.textContent = v;\n                el.appendChild(opt);\n            });\n        }\n\n        // Wire up cascading: state changes → update districts + villages\n        document.getElementById('filter-state').addEventListener('change', function() {\n            updateDistrictOptions();\n            updateVillageOptions();\n        });\n        // District changes → update villages only\n        document.getElementById('filter-district').addEventListener('change', function() {\n            updateVillageOptions();\n        });\n\n        function applyFilters() {\n            const stateVal = document.getElementById('filter-state').value;\n            const districtVal = document.getElementById('filter-district').value;\n            const villageVal = document.getElementById('filter-village').value;\n            const landUseVal = document.getElementById('filter-land-use').value;\n            const searchVal = document.getElementById('search-input').value.trim();\n\n            filteredRecords = allRecords.filter(rec => {\n                const loc = rec.location || {};\n                const attrs = rec.attributes || {};\n                if (stateVal && loc.state !== stateVal) return false;\n                if (districtVal && loc.district !== districtVal) return false;\n                if (villageVal && loc.village !== villageVal) return false;\n                if (landUseVal && attrs.land_use !== landUseVal) return false;\n                if (searchVal) {\n                    const searchText = [rec.khasra_no, rec.ulpin, loc.village, loc.district].join(' ').toLowerCase();\n                    if (!searchText.includes(searchVal.toLowerCase())) return false;\n                }\n                return true;\n            });\n\n            // Update counter\n            document.getElementById('results-count').textContent = filteredRecords.length;\n\n            // Update map\n            if (mapInitialized) {\n                addRecordsToMap(filteredRecords);\n            }\n        }\n\n        // Event listeners\n        document.getElementById('btn-apply-filters').addEventListener('click', function() {\n            applyFilters();\n            if (window.innerWidth < 768 && closeFilterDrawerGlobal) {\n                closeFilterDrawerGlobal();\n            }\n        });\n\n        document.getElementById('btn-clear-filters').addEventListener('click', function() {\n            document.getElementById('filter-state').value = '';\n            document.getElementById('filter-district').value = '';\n            document.getElementById('filter-village').value = '';\n            document.getElementById('filter-land-use').value = '';\n            document.getElementById('search-input').value = '';\n            applyFilters();\n        });\n\n        // Enter key on search\n        document.getElementById('search-input').addEventListener('keypress', function(e) {\n            if (e.key === 'Enter') applyFilters();\n        });\n\n\n        // Compact layer switcher is always visible — no toggle needed\n\n        // Mobile legend toggle\n        (function() {\n            const btn   = document.getElementById('btn-legend-mobile');\n            const panel = document.getElementById('legend-mobile-panel');\n            const close = document.getElementById('btn-legend-mobile-close');\n            if (!btn || !panel) return;\n\n            let open = false;\n            function showLegend() {\n                open = true;\n                panel.style.transform = 'translateY(0)';\n                panel.style.opacity = '1';\n                panel.style.pointerEvents = 'auto';\n            }\n            function hideLegend() {\n                open = false;\n                panel.style.transform = 'translateY(20px)';\n                panel.style.opacity = '0';\n                panel.style.pointerEvents = 'none';\n            }\n        })();\n    </script>\n    <!-- Feedback Modal -->\n    <div id=\"feedback-modal\" class=\"fixed inset-0 bg-black/50 z-[2000] hidden items-center justify-center p-4\">\n        <div class=\"bg-white rounded-xl shadow-2xl w-full max-w-md overflow-hidden\">\n            <div class=\"bg-orange-600 px-6 py-4 flex items-center justify-between\">\n                <h3 class=\"text-white font-bold flex items-center gap-2\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/></svg>\n                    Report an Issue / Feedback\n                </h3>\n                <button id=\"close-feedback\" class=\"text-white/80 hover:text-white transition\">\n                    <svg class=\"w-5 h-5\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M6 18L18 6M6 6l12 12\"/></svg>\n                </button>\n            </div>\n            <div class=\"p-6\">\n                <form id=\"public-feedback-form\" class=\"space-y-4\">\n                    <div>\n                        <label class=\"block text-sm font-semibold text-gray-700 mb-1\">Issue Type</label>\n                        <select id=\"feedback-type\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-orange-500\">\n                            <option value=\"Data Error\">Data Error (Wrong info)</option>\n                            <option value=\"Map Issue\">Map Issue (Boundaries)</option>\n                            <option value=\"Technical Bug\">Technical Bug</option>\n                            <option value=\"Ownership Dispute\">Ownership Dispute</option>\n                            <option value=\"General Inquiry\">General Inquiry</option>\n                        </select>\n                    </div>\n                    <div>\n                        <label class=\"block text-sm font-semibold text-gray-700 mb-1\">Subject / ULPIN</label>\n                        <input type=\"text\" id=\"feedback-subject\" required class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"e.g. ULPIN 14-digit code\">\n                    </div>\n                    <div>\n                        <label class=\"block text-sm font-semibold text-gray-700 mb-1\">Description</label>\n                        <textarea id=\"feedback-message\" required rows=\"4\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-sm\" placeholder=\"Explain the issue in detail...\"></textarea>\n                    </div>\n                    <div class=\"bg-gray-50 p-3 rounded-lg border border-gray-200\">\n                        <p class=\"text-[10px] text-gray-500 uppercase font-bold mb-1\">Contact (Optional)</p>\n                        <input type=\"text\" id=\"feedback-contact\" class=\"w-full px-3 py-2 border border-gray-300 rounded-lg text-xs\" placeholder=\"Email or Phone for follow-up\">\n                    </div>\n                    <button type=\"submit\" class=\"w-full bg-orange-600 hover:bg-orange-700 text-white font-bold py-2.5 rounded-lg shadow-md transition flex items-center justify-center gap-2\">\n                        Submit Report\n                    </button>\n                </form>\n            </div>\n        </div>\n    </div>\n\n    \n    <script>\n\n        // Close parcel panel\n        document.getElementById('parcel-close').addEventListener('click', function() {\n            const panel = document.getElementById('parcel-panel');\n            panel.classList.remove('translate-x-0');\n            panel.classList.add('translate-x-full');\n            \n            // Resize map after panel animation completes (300ms)\n            if (map && mapInitialized) {\n                setTimeout(() => map.invalidateSize(), 320);\n            }\n        });\n\n        // Logout\n        document.getElementById('btn-logout').addEventListener('click', function() {\n            logout().then(() => { window.location.href = '/login'; }).catch(() => { window.location.href = '/login'; });\n        });\n\n        // Initialize on page load\n        document.addEventListener('DOMContentLoaded', function() {\n            // ─── Prevent back button from exiting to login page ─────────────────────\n            history.pushState({ page: 'viewer' }, '', window.location.href);\n            \n            window.addEventListener('popstate', function(e) {\n                if (e.state && e.state.page === 'viewer') {\n                    history.pushState({ page: 'viewer' }, '', window.location.href);\n                }\n            });\n\n            // Initialize map immediately\n            initMap();\n            // Load records\n            loadRecords();\n        });\n    </script>\n</body>\n</html>\n"
  },
  {
    "path": "tests/Testing_Report_20260425_211328.md",
    "content": "# INDIA LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-25 21:13:58\r\n\r\n## 1. System Test Cases Summary\r\n\r\n### Test Target: `test_logins`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Valid Login (Premithiews) | Submit correct credentials | 200 | 401 | ❌ FAIL |\r\n| Valid Login (admin) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (recovery_sa_20260425_050757.698283Z) | Submit correct credentials | 200 | 401 | ❌ FAIL |\r\n| Invalid Username | Submit non-existent username | 401 | 401 | ✅ PASS |\r\n| Invalid Password | Submit wrong password for Premithiews | 401 | 401 | ✅ PASS |\r\n\r\n\r\n### Test Target: `run_role_tests`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| SuperAdmin Setup | Login as SuperAdmin | 200 | 200 | ✅ PASS |\r\n| SuperAdmin Action | Create Officer account | 201 | 201 | ✅ PASS |\r\n| SuperAdmin Action | Create Admin account | 201 | 201 | ✅ PASS |\r\n| SuperAdmin Security | Attempt create another SuperAdmin | 201 | 409 | ❌ FAIL |\r\n| SuperAdmin Security | Attempt delete SuperAdmin account | 404 | 404 | ✅ PASS |\r\n| Admin Hierarchy | Update Officer details | 200 | 200 | ✅ PASS |\r\n| Admin Hierarchy | Attempt create another Admin | 403 | 403 | ✅ PASS |\r\n| Admin Hierarchy | Attempt delete SuperAdmin | 404 | 404 | ✅ PASS |\r\n| Officer Access | Attempt fetch user list | 403 | 403 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_recovery_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Recovery Login | Login with recovery creds | 200 | 401 | ❌ FAIL |\r\n\r\n\r\n### Test Target: `test_record_soft_delete`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Admin Setup | Login as Admin | 200 | 200 | ✅ PASS |\r\n| Record Setup | Admin POST /api/records | 201 | 201 | ✅ PASS |\r\n| Soft Delete | Officer DELETE record | 200 | 200 | ✅ PASS |\r\n| Data Privacy | Officer GET records | Hidden | Hidden | ✅ PASS |\r\n| Hard Delete | Admin hard delete | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_restore_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Restore Record | Admin POST restore | 200 | 200 | ✅ PASS |\r\n| Verify Restore | Admin GET record | deleted=False | deleted=False | ✅ PASS |\r\n\r\n\r\n### Test Target: `check_users_roles`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Data Integrity | Check user admin | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user temp_off_del | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user new_sa | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user off_519a5f | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user off_3a9b28 | Has Role | Has Role | ✅ PASS |\r\n\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n\r\nTesting valid login for: Premithiews\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting valid login for: admin\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: recovery_sa_20260425_050757.698283Z\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Username\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Password for user: Premithiews\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n```\r\n\r\n---\r\n\r\n### tests/run_role_tests.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n--- PHASE 1: SuperAdmin Setup ---\r\n\r\n--- PHASE 2: SuperAdmin Security Checks ---\r\n\r\n--- PHASE 3: Standard Admin Hierarchy Check ---\r\n\r\n--- PHASE 4: Officer Access Check ---\r\n\r\n--- PHASE 5: Cleanup ---\r\nCleanup completed.\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 2)\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n200\r\nCreate officer...\r\n201\r\nCreate record...\r\n201\r\nOfficer login...\r\nOfficer delete attempt...\r\n200\r\nVerify record is hidden...\r\nRecord hidden: True\r\nAdmin hard delete...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\nOfficer soft delete...\r\nAdmin restore...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nFound User: admin (superadmin)\r\nFound User: temp_off_del (officer)\r\nFound User: new_sa (superadmin)\r\nFound User: off_519a5f (officer)\r\nFound User: off_3a9b28 (officer)\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/Testing_Report_20260426_013232.md",
    "content": "# INDIA LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-26 01:32:54\r\n\r\n## 1. System Test Cases Summary\r\n\r\n### Test Target: `test_logins`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Valid Login (admin) | Submit correct credentials | 200 | 401 | ❌ FAIL |\r\n| Valid Login (recovery_sa_20260425_050757.698283Z) | Submit correct credentials | 200 | 401 | ❌ FAIL |\r\n| Invalid Username | Submit non-existent username | 401 | 401 | ✅ PASS |\r\n| Invalid Password | Submit wrong password for admin | 401 | 401 | ✅ PASS |\r\n\r\n\r\n### Test Target: `run_role_tests`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| SuperAdmin Setup | Login as SuperAdmin | 200 | 401 | ❌ FAIL |\r\n| SuperAdmin Action | Create Officer account | 201 | 403 | ❌ FAIL |\r\n| SuperAdmin Action | Create Admin account | 201 | 403 | ❌ FAIL |\r\n| SuperAdmin Security | Attempt create another SuperAdmin | 201 | 403 | ❌ FAIL |\r\n| SuperAdmin Security | Attempt delete SuperAdmin account | 404 | 403 | ❌ FAIL |\r\n| Admin Hierarchy | Update Officer details | 200 | 403 | ❌ FAIL |\r\n| Admin Hierarchy | Attempt create another Admin | 403 | 403 | ✅ PASS |\r\n| Admin Hierarchy | Attempt delete SuperAdmin | 404 | 403 | ❌ FAIL |\r\n| Officer Access | Attempt fetch user list | 403 | 403 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_recovery_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Recovery Login | Login with recovery creds | 200 | 401 | ❌ FAIL |\r\n\r\n\r\n### Test Target: `test_record_soft_delete`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Admin Setup | Login as Admin | 200 | 401 | ❌ FAIL |\r\n| Record Setup | Admin POST /api/records | 201 | 403 | ❌ FAIL |\r\n\r\n\r\n### Test Target: `check_users_roles`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Data Integrity | Check user recovery_sa_20260425_154817.302101Z | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user admin | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user Premithiews | Has Role | Has Role | ✅ PASS |\r\n\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n\r\nTesting valid login for: admin\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting valid login for: recovery_sa_20260425_050757.698283Z\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Username\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Password for user: admin\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n```\r\n\r\n---\r\n\r\n### tests/run_role_tests.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n--- PHASE 1: SuperAdmin Setup ---\r\n\r\n--- PHASE 2: SuperAdmin Security Checks ---\r\n\r\n--- PHASE 3: Standard Admin Hierarchy Check ---\r\n\r\n--- PHASE 4: Officer Access Check ---\r\n\r\n--- PHASE 5: Cleanup ---\r\nCleanup completed.\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 2)\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n401\r\nCreate officer...\r\n403\r\nCreate record...\r\n403\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nFound User: recovery_sa_20260425_154817.302101Z (superadmin)\r\nFound User: admin (superadmin)\r\nFound User: Premithiews (admin)\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/Testing_Report_20260426_095300.md",
    "content": "# INDIA LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-26 09:53:35\r\n\r\n## 1. System Test Cases Summary\r\n\r\n### Test Target: `test_logins`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Valid Login (Premithiews) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (admin) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (recovery_sa_20260425_154817.302101Z) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Invalid Username | Submit non-existent username | 401 | 401 | ✅ PASS |\r\n| Invalid Password | Submit wrong password for Premithiews | 401 | 401 | ✅ PASS |\r\n\r\n\r\n### Test Target: `robust_role_test`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Discovery | Create temp Admin | 201 | 201 | ✅ PASS |\r\n| Provisioning | Create temp Officer | 201 | 201 | ✅ PASS |\r\n| Hierarchy | Admin edits Officer | 200 | 200 | ✅ PASS |\r\n| Security | Admin attempts delete SuperAdmin | 403/404 | 403 | ✅ PASS |\r\n| Hierarchy | Admin creates peer Admin | 201 | 201 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_recovery_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Recovery Login | Login with recovery creds | 200 | 200 | ✅ PASS |\r\n| Create SuperAdmin | POST /api/users as recovery | 201 | 201 | ✅ PASS |\r\n| Delete SuperAdmin | DELETE /api/users/<id> as recovery | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_record_soft_delete`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Admin Setup | Login as Admin | 200 | 200 | ✅ PASS |\r\n| Record Setup | Admin POST /api/records | 201 | 201 | ✅ PASS |\r\n| Soft Delete | Officer DELETE record | 200 | 200 | ✅ PASS |\r\n| Data Privacy | Officer GET records | Hidden | Hidden | ✅ PASS |\r\n| Hard Delete | Admin hard delete | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_restore_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Restore Record | Admin POST restore | 200 | 200 | ✅ PASS |\r\n| Verify Restore | Admin GET record | deleted=False | deleted=False | ✅ PASS |\r\n\r\n\r\n### Test Target: `check_users_roles`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Data Integrity | Check user recovery_sa_20260425_154817.302101Z | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user Premithiews | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user admin | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_db4f | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_d789 | Has Role | Has Role | ✅ PASS |\r\n\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n\r\nTesting valid login for: Premithiews\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: admin\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: recovery_sa_20260425_154817.302101Z\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting Invalid Username\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Password for user: Premithiews\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n```\r\n\r\n---\r\n\r\n### tests/robust_role_test.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n--- PHASE 1: Account Discovery ---\r\nAttempting login for: Premithiews\r\n  Success! Discovered: Premithiews is superadmin\r\nAttempting login for: admin\r\n  Success! Discovered: admin is superadmin\r\nAttempting login for: recovery_sa_20260425_154817.302101Z\r\n  Success! Discovered: recovery_sa_20260425_154817.302101Z is superadmin\r\n\r\n--- PHASE 2: Dynamic Provisioning ---\r\nNo Admin found. Creating temporary Admin: tmp_admin_a7d8\r\n\r\n--- PHASE 3: Hierarchy Testing ---\r\nTest 1: Admin Managing Officer...\r\nTest 2: Admin cannot delete SuperAdmin...\r\n\r\n--- PHASE 4: Cleanup ---\r\nCleanup completed.\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n```\r\n\r\n**Errors/Warnings:**\r\n```text\r\nC:\\Users\\user\\Desktop\\zz\\tests\\test_recovery_flow.py:43: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).\r\n  ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n200\r\nCreate officer...\r\n201\r\nCreate record...\r\n201\r\nOfficer login...\r\nOfficer delete attempt...\r\n200\r\nVerify record is hidden...\r\nRecord hidden: True\r\nAdmin hard delete...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\nOfficer soft delete...\r\nAdmin restore...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nFound User: recovery_sa_20260425_154817.302101Z (superadmin)\r\nFound User: Premithiews (superadmin)\r\nFound User: admin (superadmin)\r\nFound User: peer_db4f (admin)\r\nFound User: peer_d789 (admin)\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/Testing_Report_20260426_125456.md",
    "content": "# LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-26 12:55:31\r\n\r\n## 1. System Test Cases Summary\r\n\r\n### Test Target: `test_logins`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Valid Login (Premithiews) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (admin) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (recovery_sa_20260425_154817.302101Z) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Invalid Username | Submit non-existent username | 401 | 401 | ✅ PASS |\r\n| Invalid Password | Submit wrong password for Premithiews | 401 | 401 | ✅ PASS |\r\n\r\n\r\n### Test Target: `robust_role_test`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Discovery | Create temp Admin | 201 | 201 | ✅ PASS |\r\n| Provisioning | Create temp Officer | 201 | 201 | ✅ PASS |\r\n| Hierarchy | Admin edits Officer | 200 | 200 | ✅ PASS |\r\n| Security | Admin attempts delete SuperAdmin | 403/404 | 403 | ✅ PASS |\r\n| Hierarchy | Admin creates peer Admin | 201 | 201 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_recovery_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Recovery Login | Login with recovery creds | 200 | 200 | ✅ PASS |\r\n| Create SuperAdmin | POST /api/users as recovery | 201 | 201 | ✅ PASS |\r\n| Delete SuperAdmin | DELETE /api/users/<id> as recovery | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_record_soft_delete`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Admin Setup | Login as Admin | 200 | 200 | ✅ PASS |\r\n| Record Setup | Admin POST /api/records | 201 | 201 | ✅ PASS |\r\n| Soft Delete | Officer DELETE record | 200 | 200 | ✅ PASS |\r\n| Data Privacy | Officer GET records | Hidden | Hidden | ✅ PASS |\r\n| Hard Delete | Admin hard delete | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_restore_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Restore Record | Admin POST restore | 200 | 200 | ✅ PASS |\r\n| Verify Restore | Admin GET record | deleted=False | deleted=False | ✅ PASS |\r\n\r\n\r\n### Test Target: `check_users_roles`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Data Integrity | Check user recovery_sa_20260425_154817.302101Z | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user Premithiews | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user admin | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_db4f | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_d789 | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_05c0 | Has Role | Has Role | ✅ PASS |\r\n\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n\r\nTesting valid login for: Premithiews\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: admin\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: recovery_sa_20260425_154817.302101Z\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting Invalid Username\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Password for user: Premithiews\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n```\r\n\r\n---\r\n\r\n### tests/robust_role_test.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n--- PHASE 1: Account Discovery ---\r\nAttempting login for: Premithiews\r\n  Success! Discovered: Premithiews is superadmin\r\nAttempting login for: admin\r\n  Success! Discovered: admin is superadmin\r\nAttempting login for: recovery_sa_20260425_154817.302101Z\r\n  Success! Discovered: recovery_sa_20260425_154817.302101Z is superadmin\r\n\r\n--- PHASE 2: Dynamic Provisioning ---\r\nNo Admin found. Creating temporary Admin: tmp_admin_0a0b\r\n\r\n--- PHASE 3: Hierarchy Testing ---\r\nTest 1: Admin Managing Officer...\r\nTest 2: Admin cannot delete SuperAdmin...\r\n\r\n--- PHASE 4: Cleanup ---\r\nCleanup completed.\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n```\r\n\r\n**Errors/Warnings:**\r\n```text\r\nC:\\Users\\user\\Desktop\\zz\\tests\\test_recovery_flow.py:43: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).\r\n  ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n200\r\nCreate officer...\r\n201\r\nCreate record...\r\n201\r\nOfficer login...\r\nOfficer delete attempt...\r\n200\r\nVerify record is hidden...\r\nRecord hidden: True\r\nAdmin hard delete...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\nOfficer soft delete...\r\nAdmin restore...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nFound User: recovery_sa_20260425_154817.302101Z (superadmin)\r\nFound User: Premithiews (superadmin)\r\nFound User: admin (superadmin)\r\nFound User: peer_db4f (admin)\r\nFound User: peer_d789 (admin)\r\nFound User: peer_05c0 (admin)\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/Testing_Report_20260426_132557.md",
    "content": "# LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-26 13:25:59\r\n\r\n## 1. System Test Cases Summary\r\n\r\n*No test cases recorded.*\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Standard Output:**\r\n```text\r\nFailed to import app: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n### tests/robust_role_test.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Errors/Warnings:**\r\n```text\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\robust_role_test.py\", line 18, in <module>\r\n    from app import app\r\n  File \"C:\\Users\\user\\Desktop\\zz\\app.py\", line 12, in <module>\r\n    from config import SECRET_KEY, DEBUG, HOST, PORT, MONGO_URI\r\n  File \"C:\\Users\\user\\Desktop\\zz\\config.py\", line 7, in <module>\r\n    from dotenv import load_dotenv\r\nModuleNotFoundError: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Errors/Warnings:**\r\n```text\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\test_recovery_flow.py\", line 10, in <module>\r\n    from app import app\r\n  File \"C:\\Users\\user\\Desktop\\zz\\app.py\", line 12, in <module>\r\n    from config import SECRET_KEY, DEBUG, HOST, PORT, MONGO_URI\r\n  File \"C:\\Users\\user\\Desktop\\zz\\config.py\", line 7, in <module>\r\n    from dotenv import load_dotenv\r\nModuleNotFoundError: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Errors/Warnings:**\r\n```text\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\test_record_soft_delete.py\", line 15, in <module>\r\n    from app import app\r\n  File \"C:\\Users\\user\\Desktop\\zz\\app.py\", line 12, in <module>\r\n    from config import SECRET_KEY, DEBUG, HOST, PORT, MONGO_URI\r\n  File \"C:\\Users\\user\\Desktop\\zz\\config.py\", line 7, in <module>\r\n    from dotenv import load_dotenv\r\nModuleNotFoundError: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Errors/Warnings:**\r\n```text\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\test_restore_flow.py\", line 11, in <module>\r\n    from app import app\r\n  File \"C:\\Users\\user\\Desktop\\zz\\app.py\", line 12, in <module>\r\n    from config import SECRET_KEY, DEBUG, HOST, PORT, MONGO_URI\r\n  File \"C:\\Users\\user\\Desktop\\zz\\config.py\", line 7, in <module>\r\n    from dotenv import load_dotenv\r\nModuleNotFoundError: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ❌ FAIL (Exit Code 1)\r\n\r\n**Errors/Warnings:**\r\n```text\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\check_users_roles.py\", line 6, in <module>\r\n    from config import MONGO_URI\r\nModuleNotFoundError: No module named 'config'\r\n\r\nDuring handling of the above exception, another exception occurred:\r\n\r\nTraceback (most recent call last):\r\n  File \"C:\\Users\\user\\Desktop\\zz\\tests\\check_users_roles.py\", line 10, in <module>\r\n    from config import MONGO_URI\r\n  File \"C:\\Users\\user\\Desktop\\zz\\config.py\", line 7, in <module>\r\n    from dotenv import load_dotenv\r\nModuleNotFoundError: No module named 'dotenv'\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/Testing_Report_20260426_132640.md",
    "content": "# LIMS - AUTOMATED TESTING REPORT\r\n\r\n**Date/Time:** 2026-04-26 13:27:17\r\n\r\n## 1. System Test Cases Summary\r\n\r\n### Test Target: `test_logins`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Valid Login (Premithiews) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (admin) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Valid Login (recovery_sa_20260425_154817.302101Z) | Submit correct credentials | 200 | 200 | ✅ PASS |\r\n| Invalid Username | Submit non-existent username | 401 | 401 | ✅ PASS |\r\n| Invalid Password | Submit wrong password for Premithiews | 401 | 401 | ✅ PASS |\r\n\r\n\r\n### Test Target: `robust_role_test`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Discovery | Create temp Admin | 201 | 201 | ✅ PASS |\r\n| Provisioning | Create temp Officer | 201 | 201 | ✅ PASS |\r\n| Hierarchy | Admin edits Officer | 200 | 200 | ✅ PASS |\r\n| Security | Admin attempts delete SuperAdmin | 403/404 | 403 | ✅ PASS |\r\n| Hierarchy | Admin creates peer Admin | 201 | 201 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_recovery_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Recovery Login | Login with recovery creds | 200 | 200 | ✅ PASS |\r\n| Create SuperAdmin | POST /api/users as recovery | 201 | 201 | ✅ PASS |\r\n| Delete SuperAdmin | DELETE /api/users/<id> as recovery | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_record_soft_delete`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Admin Setup | Login as Admin | 200 | 200 | ✅ PASS |\r\n| Record Setup | Admin POST /api/records | 201 | 201 | ✅ PASS |\r\n| Soft Delete | Officer DELETE record | 200 | 200 | ✅ PASS |\r\n| Data Privacy | Officer GET records | Hidden | Hidden | ✅ PASS |\r\n| Hard Delete | Admin hard delete | 200 | 200 | ✅ PASS |\r\n\r\n\r\n### Test Target: `test_restore_flow`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Restore Record | Admin POST restore | 200 | 200 | ✅ PASS |\r\n| Verify Restore | Admin GET record | deleted=False | deleted=False | ✅ PASS |\r\n\r\n\r\n### Test Target: `check_users_roles`\r\n\r\n| Scenario | Action / Input | Expected | Actual | Result |\r\n|---|---|---|---|---|\r\n| Data Integrity | Check user recovery_sa_20260425_154817.302101Z | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user Premithiews | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user admin | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_db4f | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_d789 | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_05c0 | Has Role | Has Role | ✅ PASS |\r\n| Data Integrity | Check user peer_e39b | Has Role | Has Role | ✅ PASS |\r\n\r\n\r\n---\r\n\r\n## 2. Detailed Execution Logs\r\n\r\n### tests/test_logins.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n\r\nTesting valid login for: Premithiews\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: admin\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting valid login for: recovery_sa_20260425_154817.302101Z\r\nStatus: 200\r\n{'redirect': '/admin', 'success': True}\r\n\r\nTesting Invalid Username\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n\r\nTesting Invalid Password for user: Premithiews\r\nStatus: 401\r\n{'error': 'Invalid username or password.'}\r\n```\r\n\r\n---\r\n\r\n### tests/robust_role_test.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n--- PHASE 1: Account Discovery ---\r\nAttempting login for: Premithiews\r\n  Success! Discovered: Premithiews is superadmin\r\nAttempting login for: admin\r\n  Success! Discovered: admin is superadmin\r\nAttempting login for: recovery_sa_20260425_154817.302101Z\r\n  Success! Discovered: recovery_sa_20260425_154817.302101Z is superadmin\r\n\r\n--- PHASE 2: Dynamic Provisioning ---\r\nNo Admin found. Creating temporary Admin: tmp_admin_0486\r\n\r\n--- PHASE 3: Hierarchy Testing ---\r\nTest 1: Admin Managing Officer...\r\nTest 2: Admin cannot delete SuperAdmin...\r\n\r\n--- PHASE 4: Cleanup ---\r\nCleanup completed.\r\n```\r\n\r\n---\r\n\r\n### tests/test_recovery_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\n```\r\n\r\n**Errors/Warnings:**\r\n```text\r\nC:\\Users\\user\\Desktop\\zz\\tests\\test_recovery_flow.py:43: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).\r\n  ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')\r\n```\r\n\r\n---\r\n\r\n### tests/test_record_soft_delete.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\n200\r\nCreate officer...\r\n201\r\nCreate record...\r\n201\r\nOfficer login...\r\nOfficer delete attempt...\r\n200\r\nVerify record is hidden...\r\nRecord hidden: True\r\nAdmin hard delete...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/test_restore_flow.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nSuccessfully connected to MongoDB Cluster.\r\nAdmin login...\r\nOfficer soft delete...\r\nAdmin restore...\r\n200\r\n```\r\n\r\n---\r\n\r\n### tests/check_users_roles.py\r\n\r\n**Script Exit Status:** ✅ PASS\r\n\r\n**Standard Output:**\r\n```text\r\nFound User: recovery_sa_20260425_154817.302101Z (superadmin)\r\nFound User: Premithiews (superadmin)\r\nFound User: admin (superadmin)\r\nFound User: peer_db4f (admin)\r\nFound User: peer_d789 (admin)\r\nFound User: peer_05c0 (admin)\r\nFound User: peer_e39b (admin)\r\n```\r\n\r\n---\r\n\r\n"
  },
  {
    "path": "tests/check_users_roles.py",
    "content": "r\"\"\"\r\nSimple verifier: list users and their stored roles\r\n\"\"\"\r\nimport os, sys, json\r\ntry:\r\n    from config import MONGO_URI\r\n    from utils import resource_path\r\nexcept Exception:\r\n    sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\r\n    from config import MONGO_URI\r\n    from utils import resource_path\r\n\r\ndef report_case(scenario, action, expected, actual, passed):\r\n    status = \"PASS\" if passed else \"FAIL\"\r\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\r\n\r\ndef check():\r\n    if MONGO_URI:\r\n        from pymongo import MongoClient\r\n        import certifi\r\n        client = MongoClient(MONGO_URI, tlsCAFile=certifi.where(), serverSelectionTimeoutMS=5000)\r\n        users = list(client.get_database(\"indialims\").users.find({}))\r\n        client.close()\r\n    else:\r\n        with open(os.path.join(resource_path(\"data\"), \"users.json\"), \"r\", encoding=\"utf-8\") as f:\r\n            users = json.load(f)\r\n    \r\n    for u in users:\r\n        uid = u.get('user_id') or u.get('_id')\r\n        uname = u.get('username')\r\n        role = u.get('role')\r\n        print(f\"Found User: {uname} ({role})\")\r\n        report_case(\"Data Integrity\", f\"Check user {uname}\", \"Has Role\", \"Has Role\" if role else \"No Role\", bool(role))\r\n\r\nif __name__ == '__main__':\r\n    check()"
  },
  {
    "path": "tests/generate_test_report.py",
    "content": "import os\r\nimport sys\r\nimport subprocess\r\nimport datetime\r\n\r\nTEST_SCRIPTS = [\r\n    \"tests/test_logins.py\",\r\n    \"tests/robust_role_test.py\",\r\n    \"tests/test_recovery_flow.py\",\r\n    \"tests/test_record_soft_delete.py\",\r\n    \"tests/test_restore_flow.py\",\r\n    \"tests/check_users_roles.py\"\r\n]\r\n\r\ndef main():\r\n    report_file = os.path.join(\"tests\", f\"Testing_Report_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.md\")\r\n    print(f\"Generating Test Report: {report_file}\")\r\n\r\n    markdown_tables = []\r\n    detailed_logs = []\r\n\r\n    for script in TEST_SCRIPTS:\r\n        if not os.path.exists(script): continue\r\n            \r\n        print(f\"Running {script}...\")\r\n        try:\r\n            result = subprocess.run([sys.executable, script], capture_output=True, text=True, check=False)\r\n            stdout_lines = result.stdout.split('\\n')\r\n            \r\n            table_rows = []\r\n            clean_stdout = []\r\n            \r\n            for line in stdout_lines:\r\n                if line.startswith(\"__TEST_RESULT__||\"):\r\n                    parts = line.split(\"||\")\r\n                    if len(parts) == 6:\r\n                        _, scenario, action, expected, actual, status = parts\r\n                        icon = \"✅ PASS\" if status == \"PASS\" else \"❌ FAIL\"\r\n                        table_rows.append(f\"| {scenario} | {action} | {expected} | {actual} | {icon} |\")\r\n                else:\r\n                    clean_stdout.append(line)\r\n            \r\n            if table_rows:\r\n                script_name = os.path.basename(script).replace('.py', '')\r\n                markdown_tables.append(f\"### Test Target: `{script_name}`\\n\")\r\n                markdown_tables.append(\"| Scenario | Action / Input | Expected | Actual | Result |\")\r\n                markdown_tables.append(\"|---|---|---|---|---|\")\r\n                markdown_tables.extend(table_rows)\r\n                markdown_tables.append(\"\\n\")\r\n\r\n            detailed_logs.append({\r\n                \"script\": script,\r\n                \"status\": \"✅ PASS\" if result.returncode == 0 else f\"❌ FAIL (Exit Code {result.returncode})\",\r\n                \"stdout\": \"\\n\".join(clean_stdout),\r\n                \"stderr\": result.stderr\r\n            })\r\n\r\n        except Exception as e:\r\n            print(f\"Error running {script}: {e}\")\r\n\r\n    with open(report_file, \"w\", encoding=\"utf-8\") as rf:\r\n        rf.write(\"# LIMS - AUTOMATED TESTING REPORT\\n\\n\")\r\n        rf.write(f\"**Date/Time:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\\n\")\r\n        \r\n        rf.write(\"## 1. System Test Cases Summary\\n\\n\")\r\n        if not markdown_tables:\r\n            rf.write(\"*No test cases recorded.*\\n\\n\")\r\n        for line in markdown_tables:\r\n            rf.write(line + \"\\n\")\r\n            \r\n        rf.write(\"---\\n\\n\")\r\n        rf.write(\"## 2. Detailed Execution Logs\\n\\n\")\r\n        for log in detailed_logs:\r\n            rf.write(f\"### {log['script']}\\n\\n\")\r\n            rf.write(f\"**Script Exit Status:** {log['status']}\\n\\n\")\r\n            \r\n            out_str = log[\"stdout\"].strip()\r\n            if out_str:\r\n                rf.write(\"**Standard Output:**\\n```text\\n\" + out_str + \"\\n```\\n\\n\")\r\n                \r\n            err_str = log[\"stderr\"].strip()\r\n            if err_str:\r\n                rf.write(\"**Errors/Warnings:**\\n```text\\n\" + err_str + \"\\n```\\n\\n\")\r\n            \r\n            rf.write(\"---\\n\\n\")\r\n            \r\n    print(f\"\\nDone! Test report saved to: {report_file}\")\r\n\r\nif __name__ == \"__main__\":\r\n    main()"
  },
  {
    "path": "tests/robust_role_test.py",
    "content": "r\"\"\"\nrobust_role_test.py - Improved Role-Based Access Testing\nFollows the user's logic:\n1. Scan user_id_password.json to identify accounts.\n2. Verify roles via API.\n3. Dynamically create an Admin if none is found.\n4. Test hierarchy: SuperAdmin > Admin > Officer.\n5. Automatic cleanup.\n\"\"\"\nimport os\nimport sys\nimport json\nimport uuid\n\n# Add project root to path\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nfrom app import app\nfrom core import load_users\n\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\n\ndef report_case(scenario, action, expected, actual, passed):\n    status = \"PASS\" if passed else \"FAIL\"\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\n\ndef run_tests():\n    # 1. Load Credentials\n    if not os.path.exists(CRED_FILE):\n        print(f\"Error: {CRED_FILE} not found.\")\n        return\n\n    with open(CRED_FILE, \"r\", encoding=\"utf-8\") as f:\n        creds = json.load(f)\n\n    super_admin_cred = None\n    existing_admin_cred = None\n    \n    with app.test_client() as client:\n        print(\"--- PHASE 1: Account Discovery ---\")\n        for entry in creds:\n            uname = entry.get(\"username\") or entry.get(\"user_id\")\n            pwd = entry.get(\"password\")\n            \n            # Try login\n            print(f\"Attempting login for: {uname}\")\n            resp = client.post(\"/api/login\", json={\"username\": uname, \"password\": pwd})\n            if resp.status_code == 200:\n                # Check role\n                info = client.get(\"/api/session-info\").get_json()\n                role = (info.get(\"role\") or \"\").lower()\n                print(f\"  Success! Discovered: {uname} is {role}\")\n                \n                if role == \"superadmin\" and not super_admin_cred:\n                    super_admin_cred = {\"username\": uname, \"password\": pwd}\n                elif role == \"admin\" and not existing_admin_cred:\n                    existing_admin_cred = {\"username\": uname, \"password\": pwd}\n                \n                client.post(\"/api/logout\")\n            else:\n                err_msg = \"Unknown error\"\n                try:\n                    err_msg = resp.get_json().get('error', 'No error msg')\n                except: pass\n                print(f\"  Failed login for {uname}: {resp.status_code} - {err_msg}\")\n\n        if not super_admin_cred:\n            print(\"Error: No SuperAdmin account found in user_id_password.json\")\n            return\n\n        # 2. Dynamic Provisioning\n        print(\"\\n--- PHASE 2: Dynamic Provisioning ---\")\n        client.post(\"/api/login\", json=super_admin_cred)\n        \n        temp_admin_uname = f\"tmp_admin_{uuid.uuid4().hex[:4]}\"\n        temp_admin_pwd = \"password123\"\n        temp_admin_id = None\n        \n        if not existing_admin_cred:\n            print(f\"No Admin found. Creating temporary Admin: {temp_admin_uname}\")\n            r_create = client.post(\"/api/users\", json={\n                \"username\": temp_admin_uname,\n                \"password\": temp_admin_pwd,\n                \"full_name\": \"Temporary Test Admin\",\n                \"role\": \"admin\"\n            })\n            if r_create.status_code in (200, 201):\n                temp_admin_id = r_create.get_json().get(\"user\", {}).get(\"user_id\")\n                existing_admin_cred = {\"username\": temp_admin_uname, \"password\": temp_admin_pwd}\n                report_case(\"Discovery\", \"Create temp Admin\", \"201\", r_create.status_code, True)\n        else:\n            print(f\"Using existing Admin: {existing_admin_cred['username']}\")\n\n        # Create a temp Officer for the Admin to manage\n        temp_off_uname = f\"tmp_off_{uuid.uuid4().hex[:4]}\"\n        temp_off_id = None\n        r_off = client.post(\"/api/users\", json={\n            \"username\": temp_off_uname,\n            \"password\": \"password123\",\n            \"full_name\": \"Temporary Test Officer\",\n            \"role\": \"officer\"\n        })\n        if r_off.status_code in (200, 201):\n            temp_off_id = r_off.get_json().get(\"user\", {}).get(\"user_id\")\n            report_case(\"Provisioning\", \"Create temp Officer\", \"201\", r_off.status_code, True)\n\n        client.post(\"/api/logout\")\n\n        # 3. Hierarchy Testing\n        print(\"\\n--- PHASE 3: Hierarchy Testing ---\")\n        \n        # Test 1: Admin Power\n        print(\"Test 1: Admin Managing Officer...\")\n        client.post(\"/api/login\", json=existing_admin_cred)\n        r_edit = client.put(f\"/api/users/{temp_off_id}\", json={\"full_name\": \"Officer Renamed By Admin\"})\n        report_case(\"Hierarchy\", \"Admin edits Officer\", \"200\", r_edit.status_code, r_edit.status_code == 200)\n        \n        # Test 2: Admin Restrictions\n        print(\"Test 2: Admin cannot delete SuperAdmin...\")\n        # Get SuperAdmin ID first\n        client.post(\"/api/logout\")\n        client.post(\"/api/login\", json=super_admin_cred)\n        users = client.get(\"/api/users\").get_json()\n        sa_obj = next((u for u in users if u['role'].lower() == 'superadmin'), None)\n        sa_id = sa_obj['user_id'] if sa_obj else \"bootstrap-admin-01\"\n        \n        client.post(\"/api/logout\")\n        client.post(\"/api/login\", json=existing_admin_cred)\n        r_del_sa = client.delete(f\"/api/users/{sa_id}\")\n        report_case(\"Security\", \"Admin attempts delete SuperAdmin\", \"403/404\", r_del_sa.status_code, r_del_sa.status_code in (403, 404))\n        \n        # Test 3: Admin creating peer Admin (Should be allowed based on current code)\n        r_peer = client.post(\"/api/users\", json={\"username\": f\"peer_{uuid.uuid4().hex[:4]}\", \"password\": \"123\", \"full_name\": \"Peer\", \"role\": \"admin\"})\n        report_case(\"Hierarchy\", \"Admin creates peer Admin\", \"201\", r_peer.status_code, r_peer.status_code == 201)\n        if r_peer.status_code == 201:\n            client.delete(f\"/api/users/{r_peer.get_json().get('user', {}).get('user_id')}\")\n\n        client.post(\"/api/logout\")\n\n        # 4. Cleanup\n        print(\"\\n--- PHASE 4: Cleanup ---\")\n        client.post(\"/api/login\", json=super_admin_cred)\n        if temp_off_id:\n            client.delete(f\"/api/users/{temp_off_id}\")\n        if temp_admin_id:\n            client.delete(f\"/api/users/{temp_admin_id}\")\n        print(\"Cleanup completed.\")\n\nif __name__ == \"__main__\":\n    run_tests()\n"
  },
  {
    "path": "tests/test_advanced_gis.py",
    "content": "import os\nimport sys\nimport json\nimport uuid\nfrom datetime import datetime\n\n# Ensure project root is importable\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\nfrom app import app\n\ndef report_case(scenario, action, expected, actual, passed):\n    status = \"PASS\" if passed else \"FAIL\"\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\n\n# 1. Setup credentials\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\nwith open(CRED_FILE, \"r\", encoding=\"utf-8\") as f:\n    creds = json.load(f)\n# Use Premithiews as the test user\nadmin = next((c for c in creds if c.get('username') == 'Premithiews' or c.get('user_id') == 'Premithiews'), creds[0])\n\n# 2. Test Data\n# A 1km x 1km square in Delhi area\nbase_poly = {\n    \"type\": \"Polygon\",\n    \"coordinates\": [[[77.100, 28.600], [77.110, 28.600], [77.110, 28.610], [77.100, 28.610], [77.100, 28.600]]]\n}\n# An overlapping square\noverlap_poly = {\n    \"type\": \"Polygon\",\n    \"coordinates\": [[[77.105, 28.605], [77.115, 28.605], [77.115, 28.615], [77.105, 28.615], [77.105, 28.605]]]\n}\n# Invalid: Too few points\nbad_poly = {\n    \"type\": \"Polygon\",\n    \"coordinates\": [[[77.100, 28.600], [77.110, 28.600]]]\n}\n\nbase_record_id = None\n\nwith app.test_client() as client:\n    print(\"--- STARTING ADVANCED GIS SUITE ---\")\n    \n    # STEP 1: LOGIN\n    r_login = client.post('/api/login', json={\"username\": admin.get('username') or admin.get('user_id'), \"password\": admin.get('password')})\n    report_case(\"Auth\", \"Admin Login\", \"200\", r_login.status_code, r_login.status_code == 200)\n\n    # STEP 2: INSERT BASE RECORD\n    print(\"\\nInserting Base Parcel...\")\n    base_data = {\n        \"khasra_no\": \"TEST-BASE-001\", \"khata_no\": \"KH-99\",\n        \"location\": {\"state\": \"Delhi\", \"district\": \"New Delhi\", \"village\": \"Chanakyapuri\"},\n        \"geometry\": base_poly, \"land_use\": \"Institutional\", \"owner_name\": \"Initial Owner\"\n    }\n    r_base = client.post('/api/records', json=base_data)\n    if r_base.status_code == 201:\n        base_record_id = r_base.get_json().get('record', {}).get('_id')\n    report_case(\"Insertion\", \"Insert Base Parcel\", \"201\", r_base.status_code, r_base.status_code == 201)\n\n    # STEP 3: TEST OVERLAP DETECTION\n    if base_record_id:\n        print(\"\\nTesting Overlap Detection (should be blocked)...\")\n        overlap_data = base_data.copy()\n        overlap_data[\"khasra_no\"] = \"TEST-OVERLAP-ERR\"\n        overlap_data[\"geometry\"] = overlap_poly\n        r_overlap = client.post('/api/records', json=overlap_data)\n        report_case(\"Security\", \"Detect Parcel Overlap\", \"409\", r_overlap.status_code, r_overlap.status_code == 409)\n\n    # STEP 4: TEST GEOMETRY VALIDATION\n    print(\"\\nTesting Bad Geometry (should be blocked)...\")\n    bad_data = base_data.copy()\n    bad_data[\"khasra_no\"] = \"TEST-BAD-GEOM\"\n    bad_data[\"geometry\"] = bad_poly\n    r_bad = client.post('/api/records', json=bad_data)\n    report_case(\"Security\", \"Validate Geometry\", \"400\", r_bad.status_code, r_bad.status_code == 400)\n\n    # STEP 5: TEST OWNERSHIP MUTATION\n    if base_record_id:\n        print(\"\\nTesting Ownership Mutation (Sale Deed)...\")\n        mutation_data = {\n            \"mutation\": True,\n            \"new_owner_name\": \"Purchaser Name\",\n            \"mutation_type\": \"Sale Deed\",\n            \"new_share_pct\": 100\n        }\n        r_mut = client.put(f'/api/records/{base_record_id}', json=mutation_data)\n        report_case(\"Logic\", \"Perform Mutation\", \"200\", r_mut.status_code, r_mut.status_code == 200)\n\n    # STEP 6: CLEANUP\n    if base_record_id:\n        print(\"\\nCleaning up test data...\")\n        r_del = client.delete(f'/api/records/{base_record_id}')\n        report_case(\"Cleanup\", \"Delete Base Parcel\", \"200\", r_del.status_code, r_del.status_code == 200)\n\n    print(\"\\n--- ADVANCED GIS SUITE COMPLETE ---\")\n"
  },
  {
    "path": "tests/test_logins.py",
    "content": "r\"\"\"\r\nTest login flow using Flask test client and credentials in user_id_password.json\r\nUsage: .venv\\Scripts\\python tests\\test_logins.py\r\n\"\"\"\r\nimport os\r\nimport sys\r\nimport json\r\nfrom pprint import pprint\r\n\r\n# Ensure project root is importable\r\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\r\n\r\ntry:\r\n    from app import app\r\nexcept Exception as e:\r\n    print(f\"Failed to import app: {e}\")\r\n    sys.exit(1)\r\n\r\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\r\nif not os.path.exists(CRED_FILE):\r\n    print(f\"Credentials file not found: {CRED_FILE}\")\r\n    sys.exit(1)\r\n\r\nwith open(CRED_FILE, \"r\", encoding=\"utf-8\") as f:\r\n    try:\r\n        creds = json.load(f)\r\n    except Exception as e:\r\n        print(f\"Failed to parse credentials file: {e}\")\r\n        sys.exit(1)\r\n\r\nif not isinstance(creds, list):\r\n    print(\"Expected JSON array of credentials objects\")\r\n    sys.exit(1)\r\n\r\ndef report_case(scenario, action, expected, actual, passed):\r\n    status = \"PASS\" if passed else \"FAIL\"\r\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\r\n\r\nwith app.test_client() as client:\r\n    # 1. Test Valid Logins\r\n    for entry in creds:\r\n        username = entry.get(\"username\") or entry.get(\"user_id\")\r\n        password = entry.get(\"password\")\r\n        if not username or not password:\r\n            continue\r\n        print(f\"\\nTesting valid login for: {username}\")\r\n        resp = client.post(\"/api/login\", json={\"username\": username, \"password\": password})\r\n        print(f\"Status: {resp.status_code}\")\r\n        try: pprint(resp.get_json(force=True))\r\n        except: print(resp.data.decode()[:200])\r\n        report_case(f\"Valid Login ({username})\", \"Submit correct credentials\", \"200\", resp.status_code, resp.status_code == 200)\r\n\r\n    # 2. Test Invalid Username\r\n    print(\"\\nTesting Invalid Username\")\r\n    resp = client.post(\"/api/login\", json={\"username\": \"doesnotexist_user\", \"password\": \"password123\"})\r\n    print(f\"Status: {resp.status_code}\")\r\n    try: pprint(resp.get_json(force=True))\r\n    except: print(resp.data.decode()[:200])\r\n    report_case(\"Invalid Username\", \"Submit non-existent username\", \"401\", resp.status_code, resp.status_code == 401)\r\n\r\n    # 3. Test Invalid Password\r\n    if creds and len(creds) > 0:\r\n        valid_user = creds[0].get(\"username\") or creds[0].get(\"user_id\")\r\n        print(f\"\\nTesting Invalid Password for user: {valid_user}\")\r\n        resp = client.post(\"/api/login\", json={\"username\": valid_user, \"password\": \"wrongpassword!\"})\r\n        print(f\"Status: {resp.status_code}\")\r\n        try: pprint(resp.get_json(force=True))\r\n        except: print(resp.data.decode()[:200])\r\n        report_case(\"Invalid Password\", f\"Submit wrong password for {valid_user}\", \"401\", resp.status_code, resp.status_code == 401)\r\n"
  },
  {
    "path": "tests/test_record_soft_delete.py",
    "content": "r\"\"\"\r\nTest soft-delete flow for records:\r\n- Login as admin, create a record\r\n- Login as officer, soft-delete the record\r\n- Verify viewer/officer cannot see soft-deleted record\r\n- Login as admin, hard-delete the record\r\n\"\"\"\r\nimport os\r\nimport sys\r\nimport json\r\nimport uuid\r\nfrom pprint import pprint\r\n\r\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\r\nfrom app import app\r\n\r\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\r\nwith open(CRED_FILE, \"r\", encoding=\"utf-8\") as f:\r\n    creds = json.load(f)\r\n\r\nadmin = None\r\nfor e in creds:\r\n    name = e.get('username') or e.get('user_id')\r\n    if name and name.lower() == 'admin':\r\n        admin = {'username': name, 'password': e.get('password')}\r\n        break\r\nif not admin:\r\n    admin = {'username': creds[0].get('username') or creds[0].get('user_id'), 'password': creds[0].get('password')}\r\n\r\ndef report_case(scenario, action, expected, actual, passed):\r\n    status = \"PASS\" if passed else \"FAIL\"\r\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\r\n\r\nrecord_id = None\r\nofficer_id = None\r\nunique_id = uuid.uuid4().hex[:6]\r\n\r\nwith app.test_client() as client:\r\n    print(\"Admin login...\")\r\n    r = client.post('/api/login', json=admin)\r\n    print(r.status_code)\r\n    report_case(\"Admin Setup\", \"Login as Admin\", \"200\", r.status_code, r.status_code == 200)\r\n\r\n    temp_officer = {'username': f'temp_off_del_{unique_id}', 'password': 'op', 'full_name': 'Temp Officer', 'role': 'officer'}\r\n    print(\"Create officer...\")\r\n    resp = client.post('/api/users', json=temp_officer)\r\n    if resp.is_json:\r\n        officer_id = resp.get_json().get('user', {}).get('user_id') or resp.get_json().get('user_id')\r\n    print(resp.status_code)\r\n    \r\n    sample = {\r\n        'khasra_no': f'K-{unique_id}',\r\n        'khata_no': 'KH-1',\r\n        'location': {'state': 'TestState', 'district': 'TestDistrict', 'village': 'TestVillage'},\r\n        'geometry': {'type': 'Polygon', 'coordinates': [[[78.0, 20.0],[78.001,20.0],[78.001,20.001],[78.0,20.001],[78.0,20.0]]]},\r\n        'land_use': 'Agricultural',\r\n        'owner_name': 'Record Owner'\r\n    }\r\n    print(\"Create record...\")\r\n    r2 = client.post('/api/records', json=sample)\r\n    print(r2.status_code)\r\n    if r2.is_json and r2.get_json().get('record'):\r\n        record_id = r2.get_json()['record'].get('_id')\r\n    report_case(\"Record Setup\", \"Admin POST /api/records\", \"201\", r2.status_code, r2.status_code == 201)\r\n\r\nif record_id:\r\n    with app.test_client() as c2:\r\n        print(\"Officer login...\")\r\n        c2.post('/api/login', json=temp_officer)\r\n        \r\n        print(\"Officer delete attempt...\")\r\n        del_resp = c2.delete(f'/api/records/{record_id}')\r\n        print(del_resp.status_code)\r\n        report_case(\"Soft Delete\", \"Officer DELETE record\", \"200\", del_resp.status_code, del_resp.status_code == 200)\r\n\r\n        print(\"Verify record is hidden...\")\r\n        list_resp = c2.get('/api/records')\r\n        is_hidden = True\r\n        if list_resp.is_json:\r\n            data = list_resp.get_json()\r\n            if isinstance(data, list):\r\n                is_hidden = not any(rec.get('_id') == record_id for rec in data)\r\n            elif isinstance(data, dict) and \"records\" in data:\r\n                is_hidden = not any(rec.get('_id') == record_id for rec in data[\"records\"])\r\n        print(f\"Record hidden: {is_hidden}\")\r\n        report_case(\"Data Privacy\", \"Officer GET records\", \"Hidden\", \"Hidden\" if is_hidden else \"Visible\", is_hidden)\r\n\r\nwith app.test_client() as adminc:\r\n    adminc.post('/api/login', json=admin)\r\n    if record_id:\r\n        print(\"Admin hard delete...\")\r\n        del_hard = adminc.delete(f'/api/records/{record_id}')\r\n        print(del_hard.status_code)\r\n        report_case(\"Hard Delete\", \"Admin hard delete\", \"200\", del_hard.status_code, del_hard.status_code == 200)\r\n\r\n    if officer_id:\r\n        adminc.delete(f'/api/users/{officer_id}')"
  },
  {
    "path": "tests/test_recovery_flow.py",
    "content": "r\"\"\"\r\nRecovery account integration smoke test (create + delete superadmin)\r\nRun: .venv\\Scripts\\python tests\\test_recovery_flow.py\r\n\"\"\"\r\nimport os, sys, json\r\nimport uuid\r\nfrom datetime import datetime\r\n\r\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\r\nfrom app import app\r\n\r\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\r\nwith open(CRED_FILE, \"r\", encoding=\"utf-8\") as f:\r\n    creds = json.load(f)\r\n\r\n# find recovery creds (explicit or by username prefix)\r\nrecov = None\r\nfor c in creds:\r\n    uname = c.get('username') or c.get('user_id')\r\n    if uname and uname.startswith('recovery_sa_'):\r\n        recov = c\r\n        break\r\nif not recov:\r\n    print(\"No recovery credentials in user_id_password.json — add them and re-run.\")\r\n    raise SystemExit(1)\r\n\r\nrecov_username = recov.get('username') or recov.get('user_id')\r\nrecov_password = recov.get('password')\r\n\r\ndef report_case(scenario, action, expected, actual, passed):\r\n    status = \"PASS\" if passed else \"FAIL\"\r\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\r\n\r\n\r\nwith app.test_client() as client:\r\n    # Login as recovery\r\n    r = client.post('/api/login', json={\"username\": recov_username, \"password\": recov_password})\r\n    report_case(\"Recovery Login\", \"Login with recovery creds\", \"200\", r.status_code, r.status_code == 200)\r\n    if r.status_code != 200:\r\n        raise SystemExit(2)\r\n\r\n    # Create a new superadmin (unique username)\r\n    ts = datetime.utcnow().strftime('%Y%m%d%H%M%S')\r\n    new_username = f\"sa_test_create_{ts}_{str(uuid.uuid4())[:6]}\"\r\n    new_sa = {\"username\": new_username, \"password\": \"TempP@ss123!\", \"full_name\": \"SA Test\", \"role\": \"superadmin\"}\r\n    resp = client.post('/api/users', json=new_sa)\r\n    created = resp.status_code in (200, 201)\r\n    report_case(\"Create SuperAdmin\", \"POST /api/users as recovery\", \"201\", resp.status_code, created)\r\n\r\n    created_id = None\r\n    if resp.is_json:\r\n        created_id = resp.get_json().get('user', {}).get('user_id') or resp.get_json().get('user_id')\r\n\r\n    # Delete created superadmin (cleanup)\r\n    if created_id:\r\n        d = client.delete(f\"/api/users/{created_id}\")\r\n        report_case(\"Delete SuperAdmin\", \"DELETE /api/users/<id> as recovery\", \"200\", d.status_code, d.status_code == 200)\r\n    else:\r\n        print(\"No created_id returned; skipping delete step.\")\r\n"
  },
  {
    "path": "tests/test_report_gen.py",
    "content": "\nimport os\nimport sys\nimport json\n\n# Add project root to path\nsys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))\n\nfrom report_generator import generate_property_card_pdf, generate_village_excel\n\ndef test_report_generation():\n    print(\"--- Starting Report Generation Test ---\")\n    \n    # Ensure test_output directory exists\n    output_dir = os.path.join(os.path.dirname(__file__), 'test_output')\n    os.makedirs(output_dir, exist_ok=True)\n    \n    # 1. Sample Data with different Land Uses to test Multipliers\n    test_records = [\n        {\n            \"_id\": \"rec-001\",\n            \"ulpin\": \"AS1801KAM001\",\n            \"khasra_no\": \"101\",\n            \"khata_no\": \"202\",\n            \"location\": {\"state\": \"Assam\", \"district\": \"Kamrup\", \"village\": \"Amingaon\"},\n            \"attributes\": {\"area_ha\": 2.5, \"land_use\": \"Commercial\", \"circle_rate_inr\": 500000},\n            \"owner\": {\"name\": \"Arjun Sharma\", \"share_pct\": 100, \"aadhaar_mask\": \"XXXX-XXXX-1234\"},\n            \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[91.70, 26.10], [91.71, 26.10], [91.71, 26.11], [91.70, 26.11], [91.70, 26.10]]]},\n            \"mutation_history\": []\n        },\n        {\n            \"_id\": \"rec-002\",\n            \"ulpin\": \"AS1801KAM002\",\n            \"khasra_no\": \"102\",\n            \"khata_no\": \"203\",\n            \"location\": {\"state\": \"Assam\", \"district\": \"Kamrup\", \"village\": \"Amingaon\"},\n            \"attributes\": {\"area_ha\": 1.2, \"land_use\": \"Agricultural\", \"circle_rate_inr\": 100000},\n            \"owner\": {\"name\": \"Laxmi Devi\", \"share_pct\": 100, \"aadhaar_mask\": \"XXXX-XXXX-5678\"},\n            \"geometry\": {\"type\": \"Polygon\", \"coordinates\": [[[91.72, 26.12], [91.73, 26.12], [91.73, 26.13], [91.72, 26.13], [91.72, 26.12]]]},\n            \"mutation_history\": []\n        }\n    ]\n    \n    # 2. Generate PDF for each record\n    for rec in test_records:\n        print(f\"Generating PDF for {rec['ulpin']} ({rec['attributes']['land_use']})...\")\n        try:\n            pdf_bytes = generate_property_card_pdf(rec)\n            filename = f\"Property_Card_{rec['ulpin']}.pdf\"\n            filepath = os.path.join(output_dir, filename)\n            with open(filepath, 'wb') as f:\n                f.write(pdf_bytes)\n            print(f\"DONE Saved to {filepath}\")\n        except Exception as e:\n            print(f\"FAIL Error generating PDF: {e}\")\n            \n    # 3. Generate Village Ledger (Excel)\n    print(\"Generating Village Ledger (Excel) for Amingaon...\")\n    try:\n        excel_bytes = generate_village_excel(test_records)\n        filename = \"Village_Ledger_Amingaon.xlsx\"\n        filepath = os.path.join(output_dir, filename)\n        with open(filepath, 'wb') as f:\n            f.write(excel_bytes)\n        print(f\"DONE Saved to {filepath}\")\n    except Exception as e:\n        print(f\"FAIL Error generating Excel: {e}\")\n        \n    print(\"\\n--- Report Generation Test Complete ---\")\n    print(f\"Results are in: {output_dir}\")\n\nif __name__ == \"__main__\":\n    test_report_generation()\n"
  },
  {
    "path": "tests/test_restore_flow.py",
    "content": "r\"\"\"\r\nTest restore flow:\r\n- Login as admin, create temp officer and a record\r\n- Login as officer, soft-delete the record\r\n- Login as admin, restore the record\r\n- Verify record is present after restore\r\n- Cleanup created users and record\r\n\"\"\"\r\nimport os, sys, json, uuid\r\nsys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\r\nfrom app import app\r\n\r\nCRED_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), \"user_id_password.json\")\r\nwith open(CRED_FILE, \"r\", encoding=\"utf-8\") as f: creds = json.load(f)\r\n\r\nadmin = None\r\nfor e in creds:\r\n    name = e.get('username') or e.get('user_id')\r\n    if name and name.lower() == 'admin':\r\n        admin = {'username': name, 'password': e.get('password')}\r\n        break\r\nif not admin: admin = creds[0]\r\n\r\ndef report_case(scenario, action, expected, actual, passed):\r\n    status = \"PASS\" if passed else \"FAIL\"\r\n    print(f\"__TEST_RESULT__||{scenario}||{action}||{expected}||{actual}||{status}\")\r\n\r\nrecord_id = None\r\nofficer_id = None\r\nunique_id = uuid.uuid4().hex[:6]\r\n\r\nwith app.test_client() as c_admin:\r\n    print(\"Admin login...\")\r\n    r = c_admin.post('/api/login', json=admin)\r\n\r\n    temp_officer = {'username': f'temp_off_res_{unique_id}', 'password': 'pw', 'full_name': 'Restore Officer', 'role': 'officer'}\r\n    resp = c_admin.post('/api/users', json=temp_officer)\r\n    if resp.is_json:\r\n        officer_id = resp.get_json().get('user', {}).get('user_id') or resp.get_json().get('user_id')\r\n\r\n    sample = {\r\n        'khasra_no': f'REST-{unique_id}', 'khata_no': 'REST-K',\r\n        'location': {'state': 'TS', 'district': 'TD', 'village': 'TV'},\r\n        'geometry': {'type': 'Polygon', 'coordinates': [[[78.0,20.0],[78.001,20.0],[78.001,20.001],[78.0,20.001],[78.0,20.0]]]},\r\n        'land_use': 'Agricultural', 'owner_name': 'Restore Owner'\r\n    }\r\n    r2 = c_admin.post('/api/records', json=sample)\r\n    if r2.is_json and r2.get_json().get('record'):\r\n        record_id = r2.get_json()['record'].get('_id')\r\n\r\nif record_id:\r\n    with app.test_client() as c_off:\r\n        c_off.post('/api/login', json=temp_officer)\r\n        print(\"Officer soft delete...\")\r\n        c_off.delete(f'/api/records/{record_id}')\r\n\r\n    with app.test_client() as c_admin2:\r\n        c_admin2.post('/api/login', json=admin)\r\n        print(\"Admin restore...\")\r\n        restore = c_admin2.post(f'/api/records/{record_id}/restore')\r\n        print(restore.status_code)\r\n        report_case(\"Restore Record\", \"Admin POST restore\", \"200\", restore.status_code, restore.status_code == 200)\r\n\r\n        getr = c_admin2.get(f'/api/records/{record_id}')\r\n        data = getr.get_json() if getr.is_json else {}\r\n        is_restored = getr.is_json and data.get('deleted') is False\r\n        report_case(\"Verify Restore\", \"Admin GET record\", \"deleted=False\", f\"deleted={not is_restored}\", is_restored)\r\n\r\n        if officer_id: c_admin2.delete(f'/api/users/{officer_id}')\r\n        c_admin2.delete(f'/api/records/{record_id}')"
  },
  {
    "path": "utils.py",
    "content": "\"\"\"\nutils.py - Shared Utilities for India LIMS\nCommon functions used across the application.\n\"\"\"\n\nimport os\nimport sys\n\n\ndef resource_path(relative_path):\n    \"\"\"Get absolute path to resource, works for dev and for PyInstaller.\"\"\"\n    try:\n        # PyInstaller creates a temp folder and stores path in _MEIPASS\n        base_path = sys._MEIPASS\n    except Exception:\n        base_path = os.path.abspath(\".\")\n    return os.path.join(base_path, relative_path)\n\n\ndef safe_divide(numerator, denominator, default=0):\n    \"\"\"Safely divide two numbers, returning default on division by zero.\"\"\"\n    try:\n        return numerator / denominator if denominator else default\n    except (TypeError, ZeroDivisionError):\n        return default\n"
  }
]