<?php
session_start();
// ======================================================
// =================== BACKEND PHP & DB ==================
// ======================================================

// Set headers & error reporting
error_reporting(E_ALL);
ini_set('display_errors', 0); // Sembunyikan detail error dari user demi keamanan

// Fungsi mem-parsing file CSV IPCC-style dengan column-mapping cerdas & fallback
function parse_ipcc_csv($file_path, $start_year = null, $end_year = null) {
    $data = [];
    if (!file_exists($file_path)) return $data;
    if (($handle = fopen($file_path, "r")) !== FALSE) {
        $header = fgetcsv($handle, 1000, ",");
        if ($header !== FALSE) {
            $header = array_map('trim', $header);
            
            $yearIdx = FALSE;
            $ssp126Idx = FALSE;
            $ssp245Idx = FALSE;
            $ssp370Idx = FALSE;
            $ssp585Idx = FALSE;

            foreach ($header as $idx => $colName) {
                $colLower = strtolower($colName);
                if ($colLower === 'year' || $colLower === 'tahun') $yearIdx = $idx;
                else if ($colLower === 'ssp126' || $colLower === 'ssp1-2.6' || $colLower === 'ssp1') $ssp126Idx = $idx;
                else if ($colLower === 'ssp245' || $colLower === 'ssp2-4.5' || $colLower === 'ssp2') $ssp245Idx = $idx;
                else if ($colLower === 'ssp370' || $colLower === 'ssp3-7.0' || $colLower === 'ssp3') $ssp370Idx = $idx;
                else if ($colLower === 'ssp585' || $colLower === 'ssp5-8.5' || $colLower === 'ssp5') $ssp585Idx = $idx;
            }
            
            if ($yearIdx !== FALSE) {
                while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) {
                    $year = intval(trim($row[$yearIdx]));
                    if ($start_year && $year < $start_year) continue;
                    if ($end_year && $year > $end_year) continue;
                    
                    // Gunakan data dari kolom ssp126 sebagai fallback jika kolom lain kosong
                    $data[$year] = [
                        'year' => $year,
                        'ssp126' => ($ssp126Idx !== FALSE && isset($row[$ssp126Idx]) && trim($row[$ssp126Idx]) !== '') ? floatval(trim($row[$ssp126Idx])) : null,
                        'ssp245' => ($ssp245Idx !== FALSE && isset($row[$ssp245Idx]) && trim($row[$ssp245Idx]) !== '') ? floatval(trim($row[$ssp245Idx])) : null,
                        'ssp370' => ($ssp370Idx !== FALSE && isset($row[$ssp370Idx]) && trim($row[$ssp370Idx]) !== '') ? floatval(trim($row[$ssp370Idx])) : null,
                        'ssp585' => ($ssp585Idx !== FALSE && isset($row[$ssp585Idx]) && trim($row[$ssp585Idx]) !== '') ? floatval(trim($row[$ssp585Idx])) : null,
                    ];
                }
            }
        }
        fclose($handle);
    }
    return $data;
}

// Fungsi membuat 4 file CSV simulasi IPCC (historical, ensmean, ensmin, ensmax) jika belum tersedia
function ensure_sample_csv_exists_ipcc($location_sanitized, $variable_sanitized) {
    $dir = __DIR__ . "/data/{$location_sanitized}/{$variable_sanitized}";
    if (!is_dir($dir)) {
        mkdir($dir, 0777, true);
    }

    $base = 26.5; // default baseline (suhu)
    if (stripos($variable_sanitized, 'precip') !== false || stripos($variable_sanitized, 'hujan') !== false) {
        $base = 2300; // default baseline (curah hujan)
    } else if (stripos($variable_sanitized, 'kelembaban') !== false || stripos($variable_sanitized, 'humidity') !== false) {
        $base = 82.0;
    } else if (stripos($variable_sanitized, 'kecepatan') !== false || stripos($variable_sanitized, 'angin') !== false || stripos($variable_sanitized, 'wind') !== false) {
        $base = 2.4;
    } else if (stripos($variable_sanitized, 'radiasi') !== false || stripos($variable_sanitized, 'matahari') !== false || stripos($variable_sanitized, 'solar') !== false) {
        $base = 178.0;
    }

    // 1. historical_timeseries.csv (1980 - 2020)
    $hist_path = "{$dir}/historical_timeseries.csv";
    if (!file_exists($hist_path)) {
        $handle = fopen($hist_path, "w");
        if ($handle !== FALSE) {
            fputcsv($handle, ['year', 'ssp126', 'ssp245', 'ssp370', 'ssp585']);
            for ($y = 1980; $y <= 2020; $y++) {
                $idx = $y - 1980;
                $val = $base + sin($y * 0.5) * ($base > 1000 ? 120 : 0.2) + cos($y) * ($base > 1000 ? 50 : 0.1);
                fputcsv($handle, [$y, round($val, 2), round($val, 2), round($val, 2), round($val, 2)]);
            }
            fclose($handle);
        }
    }

    // 2. areal_averaged_ensmean.csv (2021 - 2100)
    $mean_path = "{$dir}/areal_averaged_ensmean.csv";
    if (!file_exists($mean_path)) {
        $handle = fopen($mean_path, "w");
        if ($handle !== FALSE) {
            fputcsv($handle, ['year', 'ssp126', 'ssp245', 'ssp370', 'ssp585']);
            for ($y = 2021; $y <= 2100; $y++) {
                $idx = $y - 2021;
                if ($base > 1000) {
                    $ssp126 = $base + ($idx * 0.4) + sin($y) * 90;
                    $ssp245 = $base + ($idx * 1.2) + cos($y) * 110;
                    $ssp370 = $base - ($idx * 1.6) + sin($y * 1.5) * 140;
                    $ssp585 = $base + ($idx * 4.2) + cos($y * 2.0) * 180;
                } else {
                    $ssp126 = $base + ($idx * 0.006) + sin($y) * 0.15;
                    $ssp245 = $base + ($idx * 0.016) + cos($y) * 0.2;
                    $ssp370 = $base + ($idx * 0.036) + sin($y * 1.5) * 0.25;
                    $ssp585 = $base + ($idx * 0.052) + cos($y * 2.0) * 0.3;
                }
                fputcsv($handle, [$y, round($ssp126, 2), round($ssp245, 2), round($ssp370, 2), round($ssp585, 2)]);
            }
            fclose($handle);
        }
    }

    // 3. areal_averaged_ensmin.csv (2021 - 2100)
    $min_path = "{$dir}/areal_averaged_ensmin.csv";
    if (!file_exists($min_path)) {
        $handle = fopen($min_path, "w");
        if ($handle !== FALSE) {
            fputcsv($handle, ['year', 'ssp126', 'ssp245', 'ssp370', 'ssp585']);
            for ($y = 2021; $y <= 2100; $y++) {
                $idx = $y - 2021;
                $spread = ($base > 1000) ? (200 + $idx * 3) : (0.25 + $idx * 0.005);
                
                if ($base > 1000) {
                    $ssp126 = ($base + ($idx * 0.4) + sin($y) * 90) - $spread;
                    $ssp245 = ($base + ($idx * 1.2) + cos($y) * 110) - $spread;
                    $ssp370 = ($base - ($idx * 1.6) + sin($y * 1.5) * 140) - $spread;
                    $ssp585 = ($base + ($idx * 4.2) + cos($y * 2.0) * 180) - $spread;
                } else {
                    $ssp126 = ($base + ($idx * 0.006) + sin($y) * 0.15) - $spread;
                    $ssp245 = ($base + ($idx * 0.016) + cos($y) * 0.2) - $spread;
                    $ssp370 = ($base + ($idx * 0.036) + sin($y * 1.5) * 0.25) - $spread;
                    $ssp585 = ($base + ($idx * 0.052) + cos($y * 2.0) * 0.3) - $spread;
                }
                fputcsv($handle, [$y, round($ssp126, 2), round($ssp245, 2), round($ssp370, 2), round($ssp585, 2)]);
            }
            fclose($handle);
        }
    }

    // 4. areal_averaged_ensmax.csv (2021 - 2100)
    $max_path = "{$dir}/areal_averaged_ensmax.csv";
    if (!file_exists($max_path)) {
        $handle = fopen($max_path, "w");
        if ($handle !== FALSE) {
            fputcsv($handle, ['year', 'ssp126', 'ssp245', 'ssp370', 'ssp585']);
            for ($y = 2021; $y <= 2100; $y++) {
                $idx = $y - 2021;
                $spread = ($base > 1000) ? (200 + $idx * 3.5) : (0.25 + $idx * 0.006);
                
                if ($base > 1000) {
                    $ssp126 = ($base + ($idx * 0.4) + sin($y) * 90) + $spread;
                    $ssp245 = ($base + ($idx * 1.2) + cos($y) * 110) + $spread;
                    $ssp370 = ($base - ($idx * 1.6) + sin($y * 1.5) * 140) + $spread;
                    $ssp585 = ($base + ($idx * 4.2) + cos($y * 2.0) * 180) + $spread;
                } else {
                    $ssp126 = ($base + ($idx * 0.006) + sin($y) * 0.15) + $spread;
                    $ssp245 = ($base + ($idx * 0.016) + cos($y) * 0.2) + $spread;
                    $ssp370 = ($base + ($idx * 0.036) + sin($y * 1.5) * 0.25) + $spread;
                    $ssp585 = ($base + ($idx * 0.052) + cos($y * 2.0) * 0.3) + $spread;
                }
                fputcsv($handle, [$y, round($ssp126, 2), round($ssp245, 2), round($ssp370, 2), round($ssp585, 2)]);
            }
            fclose($handle);
        }
    }
}

$db_file = __DIR__ . '/climate.db';
$db_exists = file_exists($db_file);

try {
    // Hubungkan ke SQLite Database
    $db = new PDO("sqlite:" . $db_file);
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

    // Self-migrating database schema updates for dynamic graph parameters
    $cols_to_add = [
        'graph_base_value' => 'REAL DEFAULT NULL',
        'graph_uncertainty' => 'REAL DEFAULT NULL',
        'graph_ssp1_slope' => 'REAL DEFAULT NULL',
        'graph_ssp2_slope' => 'REAL DEFAULT NULL',
        'graph_ssp3_slope' => 'REAL DEFAULT NULL',
        'graph_ssp5_slope' => 'REAL DEFAULT NULL',
        'csv_data' => 'TEXT DEFAULT NULL',
        'image_climate_map_url_2' => "TEXT DEFAULT ''",
        'image_stripe_ssp126_url' => "TEXT DEFAULT ''",
        'image_stripe_ssp245_url' => "TEXT DEFAULT ''",
        'image_stripe_ssp370_url' => "TEXT DEFAULT ''",
        'image_stripe_ssp585_url' => "TEXT DEFAULT ''"
    ];
    foreach ($cols_to_add as $col_name => $col_def) {
        try {
            $db->exec("ALTER TABLE future_climate ADD COLUMN {$col_name} {$col_def}");
        } catch (PDOException $ex) {
            // Kolom sudah ada, abaikan saja
        }
    }
} catch (PDOException $e) {
    header('Content-Type: application/json');
    echo json_encode(['error' => 'Gagal terhubung ke database server.']);
    exit;
}

// Inisialisasi Tabel jika database baru dibuat atau masih kosong
if (!$db_exists || filesize($db_file) === 0) {
    try {
        $db->exec("
            CREATE TABLE IF NOT EXISTS locations (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT UNIQUE NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            );

            CREATE TABLE IF NOT EXISTS variables (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT UNIQUE NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            );

            CREATE TABLE IF NOT EXISTS periods (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                range TEXT UNIQUE NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            );

            CREATE TABLE IF NOT EXISTS historical_climate (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                location_id INTEGER NOT NULL,
                variable_id INTEGER NOT NULL,
                description TEXT DEFAULT '',
                image_annual_url TEXT DEFAULT '',
                image_trend_url TEXT DEFAULT '',
                image_time_series_url TEXT DEFAULT '',
                image_monthly_map_url TEXT DEFAULT '',
                image_monthly_graph_url TEXT DEFAULT '',
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE CASCADE,
                FOREIGN KEY (variable_id) REFERENCES variables(id) ON DELETE CASCADE,
                UNIQUE (location_id, variable_id)
            );

            CREATE TABLE IF NOT EXISTS future_climate (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                location_id INTEGER NOT NULL,
                variable_id INTEGER NOT NULL,
                period_id INTEGER NOT NULL,
                description TEXT DEFAULT '',
                image_climate_map_url TEXT DEFAULT '',
                image_climate_map_url_2 TEXT DEFAULT '',
                image_time_series_url TEXT DEFAULT '',
                image_climate_stripe_url TEXT DEFAULT '',
                image_stripe_ssp126_url TEXT DEFAULT '',
                image_stripe_ssp245_url TEXT DEFAULT '',
                image_stripe_ssp370_url TEXT DEFAULT '',
                image_stripe_ssp585_url TEXT DEFAULT '',
                csv_data TEXT DEFAULT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                FOREIGN KEY (location_id) REFERENCES locations(id) ON DELETE CASCADE,
                FOREIGN KEY (variable_id) REFERENCES variables(id) ON DELETE CASCADE,
                FOREIGN KEY (period_id) REFERENCES periods(id) ON DELETE CASCADE,
                UNIQUE (location_id, variable_id, period_id)
            );
        ");

        // Seed Data Master Lokasi
        $locations = ['Indonesia'];
        $stmt = $db->prepare("INSERT OR IGNORE INTO locations (name) VALUES (:name)");
        foreach ($locations as $loc) {
            $stmt->execute([':name' => $loc]);
        }

        // Seed Data Master Variabel
        $variables = [
            'Precipitation', 'Suhu Udara Rata-rata', 'Suhu Udara Maksimum', 'Suhu Udara Minimum',
            'Kelembaban Relatif', 'Kecepatan Angin', 'Radiasi Matahari', 'Suhu Permukaan Laut',
            'Anomali Tinggi Muka Laut', 'Salinitas Permukaan Laut', 'pH Permukaan Laut'
        ];
        $stmt = $db->prepare("INSERT OR IGNORE INTO variables (name) VALUES (:name)");
        foreach ($variables as $var) {
            $stmt->execute([':name' => $var]);
        }

        // Seed Data Master Periode
        $periods = ['2021-2050', '2031-2060', '2041-2070', '2051-2080', '2061-2090', '2071-2100'];
        $stmt = $db->prepare("INSERT OR IGNORE INTO periods (range) VALUES (:range)");
        foreach ($periods as $per) {
            $stmt->execute([':range' => $per]);
        }
    } catch (PDOException $e) {
        // Gagal menginisialisasi database
        header('Content-Type: application/json');
        echo json_encode(['error' => 'Gagal menginisialisasi skema database.']);
        exit;
    }
}

// Router API Endpoint
$action = isset($_GET['action']) ? $_GET['action'] : '';

if (!empty($action)) {
    header('Content-Type: application/json');

    try {
        // A. Cek Otentikasi Sesi Admin untuk Aksi Modifikasi Database
        $protected_actions = ['save_historical', 'save_future', 'add_location', 'add_variable', 'add_period'];
        if (in_array($action, $protected_actions)) {
            if (!isset($_SESSION['admin_logged_in']) || $_SESSION['admin_logged_in'] !== true) {
                http_response_code(401);
                echo json_encode(['success' => false, 'error' => 'Unauthorized: Sesi admin tidak ditemukan. Silakan login terlebih dahulu.']);
                exit;
            }
        }

        // B. Endpoint Otentikasi Admin
        if ($action === 'login') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }
            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);
            $username = isset($input['username']) ? trim($input['username']) : '';
            $password = isset($input['password']) ? trim($input['password']) : '';

            if ($username === 'admin' && $password === 'admin') {
                $_SESSION['admin_logged_in'] = true;
                echo json_encode(['success' => true, 'message' => 'Login Admin berhasil!']);
            } else {
                echo json_encode(['success' => false, 'error' => 'Username atau Password salah.']);
            }
            exit;
        }

        if ($action === 'logout') {
            unset($_SESSION['admin_logged_in']);
            session_destroy();
            echo json_encode(['success' => true, 'message' => 'Logout Admin berhasil!']);
            exit;
        }

        if ($action === 'check_auth') {
            $is_logged_in = isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true;
            echo json_encode(['success' => true, 'authenticated' => $is_logged_in]);
            exit;
        }

        // Action Baru: get_future_csv untuk membaca berkas CSV berdasarkan lokasi, variabel, & statistik
        if ($action === 'get_future_csv') {
            $location = isset($_GET['location']) ? trim($_GET['location']) : '';
            $variable = isset($_GET['variable']) ? trim($_GET['variable']) : '';
            $statistic = isset($_GET['statistic']) ? trim($_GET['statistic']) : 'Original';
            $start_year = isset($_GET['start_year']) && $_GET['start_year'] !== '' ? intval($_GET['start_year']) : null;
            $end_year = isset($_GET['end_year']) && $_GET['end_year'] !== '' ? intval($_GET['end_year']) : null;

            if (empty($location) || empty($variable)) {
                echo json_encode(['success' => false, 'error' => 'Parameter lokasi dan variabel tidak boleh kosong.']);
                exit;
            }

            // Sanitasi input untuk mencegah Directory Traversal
            $location_sanitized = str_replace(array('..', '/', '\\'), '', $location);
            $variable_sanitized = str_replace(array('..', '/', '\\'), '', $variable);

            // Auto-generate sample CSV jika belum ada demi pengalaman tanpa hambatan
            ensure_sample_csv_exists_ipcc($location_sanitized, $variable_sanitized);

            // Tentukan file utama berdasarkan filter Statistik
            $stat_map = [
                'mean' => 'areal_averaged_ensmean.csv',
                'median' => 'areal_averaged_ensmedian.csv',
                'max' => 'areal_averaged_ensmax.csv',
                'min' => 'areal_averaged_ensmin.csv',
                'original' => 'areal_averaged.csv'
            ];
            $stat_lower = strtolower($statistic);
            $main_filename = isset($stat_map[$stat_lower]) ? $stat_map[$stat_lower] : 'areal_averaged_ensmean.csv';

            $dir = __DIR__ . "/data/{$location_sanitized}/{$variable_sanitized}";

            // Copy file ensmean jika main file kustom belum ada untuk mencegah error
            if (!file_exists("{$dir}/{$main_filename}") && file_exists("{$dir}/areal_averaged_ensmean.csv")) {
                copy("{$dir}/areal_averaged_ensmean.csv", "{$dir}/{$main_filename}");
            }

            // Parse keempat berkas CSV IPCC
            $historical_data = parse_ipcc_csv("{$dir}/historical_timeseries.csv", $start_year, $end_year);
            $mean_data = parse_ipcc_csv("{$dir}/{$main_filename}", $start_year, $end_year);
            $min_data = parse_ipcc_csv("{$dir}/areal_averaged_ensmin.csv", $start_year, $end_year);
            $max_data = parse_ipcc_csv("{$dir}/areal_averaged_ensmax.csv", $start_year, $end_year);

            echo json_encode([
                'success' => true,
                'historical' => array_values($historical_data),
                'mean' => array_values($mean_data),
                'min' => array_values($min_data),
                'max' => array_values($max_data),
                'location' => $location,
                'variable' => $variable,
                'statistic' => $statistic
            ]);
            exit;
        }

        // 1. Ambil data Historical Climate
        if ($action === 'get_historical') {
            $location = isset($_GET['location']) ? trim($_GET['location']) : '';
            $variable = isset($_GET['variable']) ? trim($_GET['variable']) : '';

            if (empty($location) || empty($variable)) {
                echo json_encode(['error' => 'Parameter tidak lengkap.']);
                exit;
            }

            $stmt = $db->prepare("
                SELECT h.* FROM historical_climate h
                JOIN locations l ON h.location_id = l.id
                JOIN variables v ON h.variable_id = v.id
                WHERE l.name = :location AND v.name = :variable
            ");
            $stmt->execute([':location' => $location, ':variable' => $variable]);
            $data = $stmt->fetch();

            if ($data) {
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'description' => $data['description'],
                        'imageAnnual' => $data['image_annual_url'],
                        'imageTrend' => $data['image_trend_url'],
                        'imageTimeSeries' => $data['image_time_series_url'],
                        'imageMonthlyMap' => $data['image_monthly_map_url'],
                        'imageMonthlyGraph' => $data['image_monthly_graph_url']
                    ]
                ]);
            } else {
                echo json_encode(['success' => false, 'message' => 'Data tidak ditemukan.']);
            }
            exit;
        }

        // 2. Simpan Data Historical Climate (Settings)
        if ($action === 'save_historical') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }

            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);

            $location = isset($input['location']) ? trim($input['location']) : '';
            $variable = isset($input['variable']) ? trim($input['variable']) : '';
            $description = isset($input['description']) ? trim($input['description']) : '';
            $imageAnnual = isset($input['imageAnnual']) ? trim($input['imageAnnual']) : '';
            $imageTrend = isset($input['imageTrend']) ? trim($input['imageTrend']) : '';
            $imageTimeSeries = isset($input['imageTimeSeries']) ? trim($input['imageTimeSeries']) : '';
            $imageMonthlyMap = isset($input['imageMonthlyMap']) ? trim($input['imageMonthlyMap']) : '';
            $imageMonthlyGraph = isset($input['imageMonthlyGraph']) ? trim($input['imageMonthlyGraph']) : '';

            if (empty($location) || empty($variable)) {
                echo json_encode(['error' => 'Lokasi dan Variable wajib diisi.']);
                exit;
            }

            // Dapatkan atau buat Lokasi
            $stmt = $db->prepare("SELECT id FROM locations WHERE name = :name");
            $stmt->execute([':name' => $location]);
            $loc = $stmt->fetch();
            if (!$loc) {
                $stmt_add = $db->prepare("INSERT INTO locations (name) VALUES (:name)");
                $stmt_add->execute([':name' => $location]);
                $location_id = $db->lastInsertId();
            } else {
                $location_id = $loc['id'];
            }

            // Dapatkan atau buat Variabel
            $stmt = $db->prepare("SELECT id FROM variables WHERE name = :name");
            $stmt->execute([':name' => $variable]);
            $var = $stmt->fetch();
            if (!$var) {
                $stmt_add = $db->prepare("INSERT INTO variables (name) VALUES (:name)");
                $stmt_add->execute([':name' => $variable]);
                $variable_id = $db->lastInsertId();
            } else {
                $variable_id = $var['id'];
            }

            // Simpan (UPSERT)
            $stmt = $db->prepare("
                INSERT INTO historical_climate (
                    location_id, variable_id, description, 
                    image_annual_url, image_trend_url, image_time_series_url, 
                    image_monthly_map_url, image_monthly_graph_url, updated_at
                ) 
                VALUES (:location_id, :variable_id, :description, :image_annual, :image_trend, :image_time_series, :image_monthly_map, :image_monthly_graph, CURRENT_TIMESTAMP)
                ON CONFLICT (location_id, variable_id) 
                DO UPDATE SET 
                    description = EXCLUDED.description,
                    image_annual_url = CASE WHEN EXCLUDED.image_annual_url != '' THEN EXCLUDED.image_annual_url ELSE historical_climate.image_annual_url END,
                    image_trend_url = CASE WHEN EXCLUDED.image_trend_url != '' THEN EXCLUDED.image_trend_url ELSE historical_climate.image_trend_url END,
                    image_time_series_url = CASE WHEN EXCLUDED.image_time_series_url != '' THEN EXCLUDED.image_time_series_url ELSE historical_climate.image_time_series_url END,
                    image_monthly_map_url = CASE WHEN EXCLUDED.image_monthly_map_url != '' THEN EXCLUDED.image_monthly_map_url ELSE historical_climate.image_monthly_map_url END,
                    image_monthly_graph_url = CASE WHEN EXCLUDED.image_monthly_graph_url != '' THEN EXCLUDED.image_monthly_graph_url ELSE historical_climate.image_monthly_graph_url END,
                    updated_at = CURRENT_TIMESTAMP
            ");

            $stmt->execute([
                ':location_id' => $location_id,
                ':variable_id' => $variable_id,
                ':description' => $description,
                ':image_annual' => $imageAnnual,
                ':image_trend' => $imageTrend,
                ':image_time_series' => $imageTimeSeries,
                ':image_monthly_map' => $imageMonthlyMap,
                ':image_monthly_graph' => $imageMonthlyGraph
            ]);

            echo json_encode(['success' => true, 'message' => 'Data Historical berhasil disimpan!']);
            exit;
        }

        // 3. Ambil data Future Climate
        if ($action === 'get_future') {
            $location = isset($_GET['location']) ? trim($_GET['location']) : '';
            $variable = isset($_GET['variable']) ? trim($_GET['variable']) : '';
            $period = isset($_GET['period']) ? trim($_GET['period']) : '';

            if (empty($location) || empty($variable)) {
                echo json_encode(['error' => 'Parameter tidak lengkap.']);
                exit;
            }

            $periodData = null;
            if (!empty($period)) {
                // A. Cari data spesifik untuk Periode ini
                $stmt = $db->prepare("
                    SELECT f.* FROM future_climate f
                    JOIN locations l ON f.location_id = l.id
                    JOIN variables v ON f.variable_id = v.id
                    JOIN periods p ON f.period_id = p.id
                    WHERE l.name = :location AND v.name = :variable AND p.range = :period
                ");
                $stmt->execute([':location' => $location, ':variable' => $variable, ':period' => $period]);
                $periodData = $stmt->fetch();
            }

            // B. Cari data global (dari periode mana saja yang tersedia untuk variabel ini)
            $stmtGlobal = $db->prepare("
                SELECT f.* FROM future_climate f
                JOIN locations l ON f.location_id = l.id
                JOIN variables v ON f.variable_id = v.id
                WHERE l.name = :location AND v.name = :variable
                ORDER BY 
                    CASE WHEN f.image_time_series_url != '' OR f.graph_base_value IS NOT NULL THEN 0 ELSE 1 END ASC,
                    f.id ASC
            ");
            $stmtGlobal->execute([':location' => $location, ':variable' => $variable]);
            $globalData = $stmtGlobal->fetch();

            if ($periodData || $globalData) {
                echo json_encode([
                    'success' => true,
                    'data' => [
                        'description' => $periodData ? $periodData['description'] : ($globalData ? $globalData['description'] : ''),
                        'imageClimateMap' => $periodData ? $periodData['image_climate_map_url'] : '',
                        'imageClimateMap2' => $periodData ? $periodData['image_climate_map_url_2'] : '',
                        'imageTimeSeries' => ($periodData && !empty($periodData['image_time_series_url'])) ? $periodData['image_time_series_url'] : ($globalData ? $globalData['image_time_series_url'] : ''),
                        'imageClimateStripe' => ($periodData && !empty($periodData['image_climate_stripe_url'])) ? $periodData['image_climate_stripe_url'] : ($globalData ? $globalData['image_climate_stripe_url'] : ''),
                        'imageStripeSSP126' => ($periodData && !empty($periodData['image_stripe_ssp126_url'])) ? $periodData['image_stripe_ssp126_url'] : ($globalData ? $globalData['image_stripe_ssp126_url'] : ''),
                        'imageStripeSSP245' => ($periodData && !empty($periodData['image_stripe_ssp245_url'])) ? $periodData['image_stripe_ssp245_url'] : ($globalData ? $globalData['image_stripe_ssp245_url'] : ''),
                        'imageStripeSSP370' => ($periodData && !empty($periodData['image_stripe_ssp370_url'])) ? $periodData['image_stripe_ssp370_url'] : ($globalData ? $globalData['image_stripe_ssp370_url'] : ''),
                        'imageStripeSSP585' => ($periodData && !empty($periodData['image_stripe_ssp585_url'])) ? $periodData['image_stripe_ssp585_url'] : ($globalData ? $globalData['image_stripe_ssp585_url'] : ''),
                        'graphBaseValue' => ($periodData && $periodData['graph_base_value'] !== null) ? $periodData['graph_base_value'] : ($globalData ? $globalData['graph_base_value'] : null),
                        'graphUncertainty' => ($periodData && $periodData['graph_uncertainty'] !== null) ? $periodData['graph_uncertainty'] : ($globalData ? $globalData['graph_uncertainty'] : null),
                        'graphSSP1Slope' => ($periodData && $periodData['graph_ssp1_slope'] !== null) ? $periodData['graph_ssp1_slope'] : ($globalData ? $globalData['graph_ssp1_slope'] : null),
                        'graphSSP2Slope' => ($periodData && $periodData['graph_ssp2_slope'] !== null) ? $periodData['graph_ssp2_slope'] : ($globalData ? $globalData['graph_ssp2_slope'] : null),
                        'graphSSP3Slope' => ($periodData && $periodData['graph_ssp3_slope'] !== null) ? $periodData['graph_ssp3_slope'] : ($globalData ? $globalData['graph_ssp3_slope'] : null),
                        'graphSSP5Slope' => ($periodData && $periodData['graph_ssp5_slope'] !== null) ? $periodData['graph_ssp5_slope'] : ($globalData ? $globalData['graph_ssp5_slope'] : null),
                        'csvData' => ($periodData && !empty($periodData['csv_data'])) ? $periodData['csv_data'] : ($globalData ? $globalData['csv_data'] : null)
                    ]
                ]);
            } else {
                echo json_encode(['success' => false, 'message' => 'Data tidak ditemukan.']);
            }
            exit;
        }

        // 4. Simpan Data Future Climate (Settings)
        if ($action === 'save_future') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }

            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);

            $location = isset($input['location']) ? trim($input['location']) : '';
            $variable = isset($input['variable']) ? trim($input['variable']) : '';
            $period = isset($input['period']) ? trim($input['period']) : '';
            $description = isset($input['description']) ? trim($input['description']) : '';
            $imageClimateMap = isset($input['imageClimateMap']) ? trim($input['imageClimateMap']) : '';
            $imageClimateMap2 = isset($input['imageClimateMap2']) ? trim($input['imageClimateMap2']) : '';
            $imageTimeSeries = isset($input['imageTimeSeries']) ? trim($input['imageTimeSeries']) : '';
            $imageClimateStripe = isset($input['imageClimateStripe']) ? trim($input['imageClimateStripe']) : '';
            $imageStripeSSP126 = isset($input['imageStripeSSP126']) ? trim($input['imageStripeSSP126']) : '';
            $imageStripeSSP245 = isset($input['imageStripeSSP245']) ? trim($input['imageStripeSSP245']) : '';
            $imageStripeSSP370 = isset($input['imageStripeSSP370']) ? trim($input['imageStripeSSP370']) : '';
            $imageStripeSSP585 = isset($input['imageStripeSSP585']) ? trim($input['imageStripeSSP585']) : '';

            $graphBaseValue = isset($input['graphBaseValue']) && $input['graphBaseValue'] !== '' ? floatval($input['graphBaseValue']) : null;
            $graphUncertainty = isset($input['graphUncertainty']) && $input['graphUncertainty'] !== '' ? floatval($input['graphUncertainty']) : null;
            $graphSSP1Slope = isset($input['graphSSP1Slope']) && $input['graphSSP1Slope'] !== '' ? floatval($input['graphSSP1Slope']) : null;
            $graphSSP2Slope = isset($input['graphSSP2Slope']) && $input['graphSSP2Slope'] !== '' ? floatval($input['graphSSP2Slope']) : null;
            $graphSSP3Slope = isset($input['graphSSP3Slope']) && $input['graphSSP3Slope'] !== '' ? floatval($input['graphSSP3Slope']) : null;
            $graphSSP5Slope = isset($input['graphSSP5Slope']) && $input['graphSSP5Slope'] !== '' ? floatval($input['graphSSP5Slope']) : null;
            $csvData = isset($input['csvData']) ? trim($input['csvData']) : null;

            if (empty($location) || empty($variable)) {
                echo json_encode(['error' => 'Lokasi dan Variable wajib diisi.']);
                exit;
            }

            // Dapatkan atau buat Lokasi
            $stmt = $db->prepare("SELECT id FROM locations WHERE name = :name");
            $stmt->execute([':name' => $location]);
            $loc = $stmt->fetch();
            if (!$loc) {
                $stmt_add = $db->prepare("INSERT INTO locations (name) VALUES (:name)");
                $stmt_add->execute([':name' => $location]);
                $location_id = $db->lastInsertId();
            } else {
                $location_id = $loc['id'];
            }

            // Dapatkan atau buat Variabel
            $stmt = $db->prepare("SELECT id FROM variables WHERE name = :name");
            $stmt->execute([':name' => $variable]);
            $var = $stmt->fetch();
            if (!$var) {
                $stmt_add = $db->prepare("INSERT INTO variables (name) VALUES (:name)");
                $stmt_add->execute([':name' => $variable]);
                $variable_id = $db->lastInsertId();
            } else {
                $variable_id = $var['id'];
            }

            $period_ids = [];
            if (!empty($period)) {
                // Dapatkan atau buat Periode spesifik
                $stmt = $db->prepare("SELECT id FROM periods WHERE range = :range");
                $stmt->execute([':range' => $period]);
                $per = $stmt->fetch();
                if (!$per) {
                    $stmt_add = $db->prepare("INSERT INTO periods (range) VALUES (:range)");
                    $stmt_add->execute([':range' => $period]);
                    $period_ids[] = $db->lastInsertId();
                } else {
                    $period_ids[] = $per['id'];
                }
            } else {
                // Ambil semua periode dari DB jika period kosong (stripe update untuk semua periode)
                $period_ids = $db->query("SELECT id FROM periods")->fetchAll(PDO::FETCH_COLUMN);
            }

            // Simpan (UPSERT)
            $stmt = $db->prepare("
                INSERT INTO future_climate (
                    location_id, variable_id, period_id, description, 
                    image_climate_map_url, image_climate_map_url_2, image_time_series_url, image_climate_stripe_url,
                    image_stripe_ssp126_url, image_stripe_ssp245_url, image_stripe_ssp370_url, image_stripe_ssp585_url,
                    graph_base_value, graph_uncertainty, graph_ssp1_slope, graph_ssp2_slope, graph_ssp3_slope, graph_ssp5_slope,
                    csv_data,
                    updated_at
                ) 
                VALUES (
                    :location_id, :variable_id, :period_id, :description, 
                    :image_climate_map, :image_climate_map_2, :image_time_series, :image_climate_stripe,
                    :stripe_ssp126, :stripe_ssp245, :stripe_ssp370, :stripe_ssp585,
                    :graph_base_value, :graph_uncertainty, :graph_ssp1_slope, :graph_ssp2_slope, :graph_ssp3_slope, :graph_ssp5_slope,
                    :csv_data,
                    CURRENT_TIMESTAMP
                )
                ON CONFLICT (location_id, variable_id, period_id) 
                DO UPDATE SET 
                    description = CASE WHEN EXCLUDED.description != '' THEN EXCLUDED.description ELSE future_climate.description END,
                    image_climate_map_url = CASE WHEN EXCLUDED.image_climate_map_url != '' THEN EXCLUDED.image_climate_map_url ELSE future_climate.image_climate_map_url END,
                    image_climate_map_url_2 = CASE WHEN EXCLUDED.image_climate_map_url_2 != '' THEN EXCLUDED.image_climate_map_url_2 ELSE future_climate.image_climate_map_url_2 END,
                    image_time_series_url = CASE WHEN EXCLUDED.image_time_series_url != '' THEN EXCLUDED.image_time_series_url ELSE future_climate.image_time_series_url END,
                    image_climate_stripe_url = CASE WHEN EXCLUDED.image_climate_stripe_url != '' THEN EXCLUDED.image_climate_stripe_url ELSE future_climate.image_climate_stripe_url END,
                    image_stripe_ssp126_url = CASE WHEN EXCLUDED.image_stripe_ssp126_url != '' THEN EXCLUDED.image_stripe_ssp126_url ELSE future_climate.image_stripe_ssp126_url END,
                    image_stripe_ssp245_url = CASE WHEN EXCLUDED.image_stripe_ssp245_url != '' THEN EXCLUDED.image_stripe_ssp245_url ELSE future_climate.image_stripe_ssp245_url END,
                    image_stripe_ssp370_url = CASE WHEN EXCLUDED.image_stripe_ssp370_url != '' THEN EXCLUDED.image_stripe_ssp370_url ELSE future_climate.image_stripe_ssp370_url END,
                    image_stripe_ssp585_url = CASE WHEN EXCLUDED.image_stripe_ssp585_url != '' THEN EXCLUDED.image_stripe_ssp585_url ELSE future_climate.image_stripe_ssp585_url END,
                    graph_base_value = CASE WHEN EXCLUDED.graph_base_value IS NOT NULL THEN EXCLUDED.graph_base_value ELSE future_climate.graph_base_value END,
                    graph_uncertainty = CASE WHEN EXCLUDED.graph_uncertainty IS NOT NULL THEN EXCLUDED.graph_uncertainty ELSE future_climate.graph_uncertainty END,
                    graph_ssp1_slope = CASE WHEN EXCLUDED.graph_ssp1_slope IS NOT NULL THEN EXCLUDED.graph_ssp1_slope ELSE future_climate.graph_ssp1_slope END,
                    graph_ssp2_slope = CASE WHEN EXCLUDED.graph_ssp2_slope IS NOT NULL THEN EXCLUDED.graph_ssp2_slope ELSE future_climate.graph_ssp2_slope END,
                    graph_ssp3_slope = CASE WHEN EXCLUDED.graph_ssp3_slope IS NOT NULL THEN EXCLUDED.graph_ssp3_slope ELSE future_climate.graph_ssp3_slope END,
                    graph_ssp5_slope = CASE WHEN EXCLUDED.graph_ssp5_slope IS NOT NULL THEN EXCLUDED.graph_ssp5_slope ELSE future_climate.graph_ssp5_slope END,
                    csv_data = CASE WHEN EXCLUDED.csv_data IS NOT NULL AND EXCLUDED.csv_data != '' THEN EXCLUDED.csv_data ELSE future_climate.csv_data END,
                    updated_at = CURRENT_TIMESTAMP
            ");

            foreach ($period_ids as $p_id) {
                $stmt->execute([
                    ':location_id' => $location_id,
                    ':variable_id' => $variable_id,
                    ':period_id' => $p_id,
                    ':description' => $description,
                    ':image_climate_map' => $imageClimateMap,
                    ':image_climate_map_2' => $imageClimateMap2,
                    ':image_time_series' => $imageTimeSeries,
                    ':image_climate_stripe' => $imageClimateStripe,
                    ':stripe_ssp126' => $imageStripeSSP126,
                    ':stripe_ssp245' => $imageStripeSSP245,
                    ':stripe_ssp370' => $imageStripeSSP370,
                    ':stripe_ssp585' => $imageStripeSSP585,
                    ':graph_base_value' => $graphBaseValue,
                    ':graph_uncertainty' => $graphUncertainty,
                    ':graph_ssp1_slope' => $graphSSP1Slope,
                    ':graph_ssp2_slope' => $graphSSP2Slope,
                    ':graph_ssp3_slope' => $graphSSP3Slope,
                    ':graph_ssp5_slope' => $graphSSP5Slope,
                    ':csv_data' => $csvData
                ]);
            }

            echo json_encode(['success' => true, 'message' => 'Data Future berhasil disimpan!']);
            exit;
        }

        // 5. Ambil data Dropdown Filter dinamis
        if ($action === 'get_filters') {
            $locs = $db->query("SELECT name FROM locations ORDER BY name ASC")->fetchAll(PDO::FETCH_COLUMN);
            $vars = $db->query("
                SELECT name FROM variables 
                WHERE name IN (
                    'Precipitation', 'Suhu Udara Rata-rata', 'Suhu Udara Maksimum', 'Suhu Udara Minimum',
                    'Kelembaban Relatif', 'Kecepatan Angin', 'Radiasi Matahari', 'Suhu Permukaan Laut',
                    'Anomali Tinggi Muka Laut', 'Salinitas Permukaan Laut', 'pH Permukaan Laut'
                )
                ORDER BY CASE name 
                    WHEN 'Precipitation' THEN 1
                    WHEN 'Suhu Udara Rata-rata' THEN 2
                    WHEN 'Suhu Udara Maksimum' THEN 3
                    WHEN 'Suhu Udara Minimum' THEN 4
                    WHEN 'Kelembaban Relatif' THEN 5
                    WHEN 'Kecepatan Angin' THEN 6
                    WHEN 'Radiasi Matahari' THEN 7
                    WHEN 'Suhu Permukaan Laut' THEN 8
                    WHEN 'Anomali Tinggi Muka Laut' THEN 9
                    WHEN 'Salinitas Permukaan Laut' THEN 10
                    WHEN 'pH Permukaan Laut' THEN 11
                    ELSE 12
                END ASC, name ASC
            ")->fetchAll(PDO::FETCH_COLUMN);
            $pers = $db->query("SELECT range FROM periods ORDER BY range ASC")->fetchAll(PDO::FETCH_COLUMN);

            echo json_encode([
                'success' => true,
                'locations' => $locs,
                'variables' => $vars,
                'periods' => $pers
            ]);
            exit;
        }

        // 6. Tambah Lokasi Baru
        if ($action === 'add_location') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }
            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);
            $name = isset($input['name']) ? trim($input['name']) : '';
            if (empty($name)) {
                echo json_encode(['error' => 'Nama lokasi wajib diisi.']);
                exit;
            }
            $stmt = $db->prepare("INSERT OR IGNORE INTO locations (name) VALUES (:name)");
            $stmt->execute([':name' => $name]);
            echo json_encode(['success' => true, 'message' => 'Lokasi baru berhasil ditambahkan!']);
            exit;
        }

        // 7. Tambah Variabel Baru
        if ($action === 'add_variable') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }
            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);
            $name = isset($input['name']) ? trim($input['name']) : '';
            if (empty($name)) {
                echo json_encode(['error' => 'Nama variabel wajib diisi.']);
                exit;
            }
            $stmt = $db->prepare("INSERT OR IGNORE INTO variables (name) VALUES (:name)");
            $stmt->execute([':name' => $name]);
            echo json_encode(['success' => true, 'message' => 'Variabel baru berhasil ditambahkan!']);
            exit;
        }

        // 8. Tambah Periode Baru
        if ($action === 'add_period') {
            if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
                echo json_encode(['error' => 'Metode request tidak valid.']);
                exit;
            }
            $raw_input = file_get_contents('php://input');
            $input = json_decode($raw_input, true);
            $range = isset($input['range']) ? trim($input['range']) : '';
            if (empty($range)) {
                echo json_encode(['error' => 'Rentang periode wajib diisi.']);
                exit;
            }
            $stmt = $db->prepare("INSERT OR IGNORE INTO periods (range) VALUES (:range)");
            $stmt->execute([':range' => $range]);
            echo json_encode(['success' => true, 'message' => 'Periode baru berhasil ditambahkan!']);
            exit;
        }

        echo json_encode(['error' => 'Aksi tidak valid.']);
        exit;

    } catch (Exception $e) {
        // Sembunyikan pesan kesalahan sistem mentah demi alasan keamanan web backend
        echo json_encode(['error' => 'Gagal memproses data di server.']);
        exit;
    }
}
?>
<!DOCTYPE html>
<html lang="id">

<head>
  <meta charset="utf-8" />
  <meta content="width=device-width, initial-scale=1.0" name="viewport" />
  <title>Climate Information System Indonesia</title>
  <link href="https://fonts.googleapis.com" rel="preconnect" />
  <link crossorigin="" href="https://fonts.gstatic.com" rel="preconnect" />
  <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&amp;display=swap"
    rel="stylesheet" />
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  <style>
    /* ====================================================== */
    /* ======================= GLOBAL ======================== */
    /* ====================================================== */

    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      font-family: 'Poppins', sans-serif;
      background: #f4f7fb;
      color: #1e293b;
    }

    html {
      scroll-behavior: smooth;
    }

    .container {
      width: 90%;
      max-width: 1400px;
      margin: auto;
    }

    /* ====================================================== */
    /* ======================= NAVBAR ======================== */
    /* ====================================================== */

    .logo-list-navbar {
      display: flex;
      align-items: center;
      gap: 15px;
    }
    
    .logo-list-navbar img {
      height: 38px;
      width: auto;
      object-fit: contain;
      filter: grayscale(10%) opacity(90%);
      transition: all 0.3s ease;
    }
    
    .logo-list-navbar img:hover {
      filter: grayscale(0%) opacity(100%);
      transform: scale(1.05);
    }

    .navbar {
      width: 100%;
      background: white;
      position: sticky;
      top: 0;
      z-index: 1000;
      box-shadow: 0 2px 15px rgba(0, 0, 0, 0.08);
    }

    .nav-wrapper {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 18px 0;
      flex-wrap: wrap;
      gap: 20px;
    }

    .logo {
      display: flex;
      align-items: center;
      gap: 14px;
    }

    .logo-circle {
      width: 55px;
      height: 55px;
      border-radius: 50%;
      background: linear-gradient(135deg, #2563eb, #0ea5e9);
      display: flex;
      justify-content: center;
      align-items: center;
      color: white;
      font-weight: 700;
      font-size: 20px;
    }

    .logo h2 {
      font-size: 24px;
    }

    .nav-menu {
      display: flex;
      gap: 12px;
      flex-wrap: wrap;
    }

    .nav-menu a {
      text-decoration: none;
      color: #0f172a;
      padding: 12px 18px;
      border-radius: 12px;
      font-weight: 500;
      transition: 0.3s;
    }

    .nav-menu a:hover,
    .nav-menu a.active {
      background: #2563eb;
      color: white;
    }

    /* ====================================================== */
    /* ========================= HERO ======================== */
    /* ====================================================== */

    .hero {
      margin-top: 35px;
      background: transparent;
      border-radius: 0;
      padding: 20px 0 40px 0;
      display: flex;
      justify-content: space-between;
      align-items: center;
      flex-wrap: wrap;
      gap: 40px;
      color: #1e293b;
    }

    .hero-text {
      flex: 1;
      min-width: 280px;
    }

    .hero-text h1 {
      font-size: 52px;
      margin-bottom: 20px;
      color: #0f172a;
    }

    .hero-text p {
      line-height: 1.9;
      color: #475569;
    }
    
    body.dark-theme .hero {
      color: #e2e8f0;
    }
    
    body.dark-theme .hero-text h1 {
      color: #f8fafc;
    }
    
    body.dark-theme .hero-text p {
      color: #94a3b8;
    }

    .card-detail-beranda .hero {
      margin-top: 0;
      padding: 0 0 35px 0;
      border-bottom: 1px solid rgba(226, 232, 240, 0.8);
      margin-bottom: 35px;
    }
    body.dark-theme .card-detail-beranda .hero {
      border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    }

    .hero-image {
      flex: 1;
      min-width: 280px;
    }

    .hero-image img {
      width: 100%;
      height: 350px;
      object-fit: cover;
      border-radius: 20px;
    }

    /* ====================================================== */
    /* ======================= SECTION ======================= */
    /* ====================================================== */

    .section-title {
      margin: 70px 0 18px;
      font-size: 38px;
      color: #0f172a;
    }

    .selected-info {
      color: #64748b;
      line-height: 1.9;
      margin-bottom: 35px;
    }

    /* ====================================================== */
    /* ========================= FORM ======================== */
    /* ====================================================== */

    .form-section {
      background: white;
      padding: 28px;
      border-radius: 22px;
      box-shadow: 0 4px 18px rgba(0, 0, 0, 0.06);
      margin-bottom: 35px;
    }

    .form-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
      gap: 20px;
    }

    .form-box {
      display: flex;
      flex-direction: column;
      gap: 10px;
    }

    .form-box label {
      font-weight: 600;
    }

    .form-box select,
    .form-box input {
      padding: 14px;
      border-radius: 12px;
      border: 1px solid #cbd5e1;
      font-size: 15px;
    }

    /* ====================================================== */
    /* ======================== BUTTON ======================= */
    /* ====================================================== */

    .save-btn {
      margin-top: 25px;
      background: linear-gradient(135deg, #2563eb, #0ea5e9);
      color: white;
      border: none;
      padding: 14px 24px;
      border-radius: 12px;
      cursor: pointer;
      font-size: 16px;
      font-weight: 600;
      transition: 0.2s ease-in-out;
    }

    .save-btn:hover {
      opacity: 0.9;
      transform: translateY(-2px);
    }

    /* ====================================================== */
    /* ========================= CARD ======================== */
    /* ====================================================== */

    .category-grid {
      display: flex;
      flex-direction: column;
      gap: 30px;
    }

    .category-card {
      background: white;
      border-radius: 24px;
      overflow: hidden;
      box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
    }

    .category-card img {
      width: 100%;
      height: 320px;
      object-fit: cover;
    }

    .category-content {
      padding: 30px;
    }

    .category-content h3 {
      font-size: 30px;
      margin-bottom: 15px;
    }

    .category-content p {
      line-height: 1.9;
      color: #64748b;
    }

    /* ====================================================== */
    /* ========================= GRAPH ======================= */
    /* ====================================================== */

    .graph-placeholder {
      margin-top: 20px;
      height: 240px;
      border-radius: 18px;
      background: linear-gradient(135deg, #dbeafe, #eff6ff);
      display: flex;
      justify-content: center;
      align-items: center;
      color: #2563eb;
      font-weight: 600;
      font-size: 18px;
    }

    /* ====================================================== */
    /* ==================== CLIMATE STRIPE ================== */
    /* ====================================================== */

    .climate-stripe {
      margin-top: 20px;
      height: 110px;
      border-radius: 15px;
      background:
        linear-gradient(to right,
          #08306b,
          #08519c,
          #2171b5,
          #4292c6,
          #6baed6,
          #9ecae1,
          #fcbba1,
          #fc9272,
          #fb6a4a,
          #ef3b2c,
          #cb181d,
          #99000d);
    }

    /* ====================================================== */
    /* ======================= TABLE ========================= */
    /* ====================================================== */
    .premium-table-container {
      width: 100%;
      overflow-x: auto;
      margin-top: 25px;
      border-radius: 12px;
      box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
      background: rgba(255, 255, 255, 0.8);
      backdrop-filter: blur(10px);
      border: none;
    }
    
    .premium-table {
      width: 100%;
      border-collapse: collapse;
      text-align: left;
      font-size: 14px;
      font-family: 'Poppins', sans-serif;
    }
    
    .premium-table th {
      background-color: #1e3a8a;
      color: #ffffff;
      padding: 12px 16px;
      font-weight: 600;
      font-size: 13px;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      border-bottom: 2px solid #1e293b;
    }
    
    .premium-table td {
      padding: 14px 16px;
      border-bottom: 1px solid #e2e8f0;
      color: #334155;
      line-height: 1.5;
    }
    
    .premium-table tr:last-child td {
      border-bottom: none;
    }
    
    .premium-table tr:nth-child(even) td {
      background-color: rgba(248, 250, 252, 0.6);
    }
    
    .premium-table tr:hover td {
      background-color: rgba(239, 246, 255, 0.8);
      transition: background-color 0.2s ease;
    }

    body.dark-theme .premium-table-container {
      background: rgba(30, 41, 59, 0.7);
      border: none;
    }
    
    body.dark-theme .premium-table th {
      background-color: #0f172a;
      border-bottom-color: #334155;
    }
    
    body.dark-theme .premium-table td {
      color: #cbd5e1;
      border-bottom-color: rgba(71, 85, 105, 0.4);
    }
    
    body.dark-theme .premium-table tr:nth-child(even) td {
      background-color: rgba(15, 23, 42, 0.3);
    }
    
    body.dark-theme .premium-table tr:hover td {
      background-color: rgba(30, 41, 59, 0.9);
    }

    .card-detail-beranda {
      background: rgba(255, 255, 255, 0.85);
      border-radius: 20px;
      padding: 40px;
      box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
      border: none;
      backdrop-filter: blur(10px);
      color: #334155;
    }
    .card-detail-beranda h3 {
      color: #1e3a8a;
      font-size: 20px;
      font-weight: 600;
    }
    body.dark-theme .card-detail-beranda {
      background: rgba(30, 41, 59, 0.7);
      border: none;
      color: #cbd5e1;
    }
    body.dark-theme .card-detail-beranda h3 {
      color: #3b82f6;
    }

    /* ====================================================== */
    /* ===================== EXPERT MODE ===================== */
    /* ====================================================== */

    .expert-grid {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
      gap: 25px;
    }

    .expert-card {
      background: white;
      border-radius: 22px;
      overflow: hidden;
      box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
    }

    .expert-card img {
      width: 100%;
      height: 230px;
      object-fit: cover;
    }

    .expert-content {
      padding: 25px;
    }

    .expert-title {
      font-size: 24px;
      margin-bottom: 15px;
      font-weight: 600;
    }

    .expert-description {
      line-height: 1.8;
      color: #64748b;
      margin-bottom: 20px;
    }

    .expert-link {
      display: flex;
      flex-direction: column;
      gap: 12px;
    }

    .expert-link a {
      text-decoration: none;
      background: linear-gradient(135deg, #2563eb, #0ea5e9);
      color: white;
      padding: 12px;
      border-radius: 12px;
      text-align: center;
      transition: 0.2s;
    }

    .expert-link a:hover {
      opacity: 0.9;
    }

    /* ====================================================== */
    /* ======================== FOOTER ======================= */
    /* ====================================================== */

    footer {
      margin-top: 70px;
      background: #0f172a;
      color: white;
      text-align: center;
      padding: 35px;
    }

    /* ====================================================== */
    /* ====================== RESPONSIVE ===================== */
    /* ====================================================== */

    @media(max-width:768px) {

      .hero {
        padding: 20px 0;
      }

      .hero-text h1 {
        font-size: 38px;
      }

      .hero-image img {
        height: 250px;
      }

    }

    body.dark-theme {
      background: #0f172a;
      color: #f1f5f9;
    }

    body.dark-theme .navbar {
      background: #1e293b;
      box-shadow: 0 2px 15px rgba(0, 0, 0, 0.3);
    }

    body.dark-theme .nav-menu a {
      color: #cbd5e1;
    }

    body.dark-theme .nav-menu a:hover,
    body.dark-theme .nav-menu a.active {
      background: #3b82f6;
      color: white;
    }

    body.dark-theme .logo h2 {
      color: white;
    }

    body.dark-theme .form-section,
    body.dark-theme .category-card,
    body.dark-theme .expert-card {
      background: #1e293b;
      color: #f1f5f9;
      box-shadow: 0 4px 18px rgba(0, 0, 0, 0.2);
    }

    body.dark-theme .section-title {
      color: white;
    }

    body.dark-theme .selected-info {
      color: #94a3b8;
    }

    body.dark-theme .form-box select,
    body.dark-theme .form-box input {
      background: #334155;
      color: white;
      border-color: #475569;
    }

    body.dark-theme .expert-title {
      color: white;
    }

    body.dark-theme .expert-description {
      color: #94a3b8;
    }

    /* ====================================================== */
    /* ====================== HIDE PAGE ====================== */
    /* ====================================================== */

    #historical-climate,
    #future-climate,
    #expert-mode,
    #settings {
      display: none;
    }

    .card-grid {
      background: white;
      border-radius: 24px;
      padding: 30px 20px;
      box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
      margin-bottom: 20px;
    }

    body.dark-theme .card-grid {
      background: #1e293b;
      color: #f1f5f9;
    }

    /* ====================================================== */
    /* =================== ADMIN LOGIN MODAL ================ */
    /* ====================================================== */
    .login-modal-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(15, 23, 42, 0.6);
      backdrop-filter: blur(10px);
      -webkit-backdrop-filter: blur(10px);
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 9999;
      opacity: 0;
      animation: fadeInModal 0.3s forwards;
    }

    .login-modal-content {
      background: rgba(255, 255, 255, 0.85);
      backdrop-filter: blur(20px);
      -webkit-backdrop-filter: blur(20px);
      border: 1px solid rgba(255, 255, 255, 0.3);
      padding: 40px;
      border-radius: 24px;
      width: 90%;
      max-width: 420px;
      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2);
      text-align: center;
      transform: scale(0.9);
      animation: scaleUpModal 0.3s forwards;
    }

    body.dark-theme .login-modal-content {
      background: rgba(30, 41, 59, 0.85);
      border-color: rgba(255, 255, 255, 0.1);
      box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4);
    }

    .login-logo-circle {
      width: 70px;
      height: 70px;
      border-radius: 50%;
      background: linear-gradient(135deg, #2563eb, #0ea5e9);
      display: flex;
      justify-content: center;
      align-items: center;
      margin: 0 auto 20px auto;
      font-size: 32px;
      color: white;
      box-shadow: 0 8px 16px rgba(37, 99, 235, 0.2);
    }

    .login-modal-header h3 {
      font-size: 22px;
      margin-bottom: 8px;
      font-weight: 700;
    }

    .login-modal-header p {
      font-size: 13px;
      color: #64748b;
      line-height: 1.5;
    }

    body.dark-theme .login-modal-header p {
      color: #94a3b8;
    }

    @keyframes fadeInModal {
      to { opacity: 1; }
    }

    @keyframes scaleUpModal {
      to { transform: scale(1); }
    }

    .interactive-chart-card {
      background: white;
      border: 1px solid #cbd5e1;
    }
    body.dark-theme .interactive-chart-card {
      background: #1e293b;
      border: 1px solid #334155;
    }
    .custom-chart-legend div,
    .custom-chart-legend span {
      color: #475569;
    }
    body.dark-theme .custom-chart-legend div,
    body.dark-theme .custom-chart-legend span {
      color: #cbd5e1;
    }
    .map-label {
      margin-top: 10px;
      font-weight: 600;
      font-size: 14px;
      color: #475569;
    }
    body.dark-theme .map-label {
      color: #cbd5e1;
    }
    
    .map-toggle-wrapper {
      display: inline-flex;
      gap: 6px;
      background: #f1f5f9;
      padding: 5px;
      border-radius: 30px;
      margin-bottom: 20px;
      border: 1px solid #cbd5e1;
    }
    body.dark-theme .map-toggle-wrapper {
      background: #1e293b;
      border-color: #334155;
    }
    .map-toggle-btn {
      background: transparent;
      color: #475569;
      border: none;
      padding: 8px 18px;
      font-size: 13px;
      font-weight: 600;
      border-radius: 20px;
      cursor: pointer;
      transition: all 0.3s ease;
      font-family: 'Poppins', sans-serif;
    }
    .map-toggle-btn:hover {
      color: #0f172a;
    }
    .map-toggle-btn.active {
      background: #2563eb;
      color: #ffffff;
      box-shadow: 0 4px 10px rgba(37, 99, 235, 0.2);
    }
    body.dark-theme .map-toggle-btn {
      color: #cbd5e1;
    }
    body.dark-theme .map-toggle-btn:hover {
      color: #ffffff;
    }
    body.dark-theme .map-toggle-btn.active {
      background: #2563eb;
      color: #ffffff;
    }

    .map-slider-nav {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      background: rgba(255, 255, 255, 0.85);
      border: none;
      width: 42px;
      height: 42px;
      border-radius: 50%;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      transition: all 0.3s ease;
      z-index: 5;
      color: #0f172a;
      font-size: 18px;
      font-weight: bold;
      line-height: 1;
    }
    .map-slider-nav:hover {
      background: #ffffff;
      transform: translateY(-50%) scale(1.1);
      box-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
    }
    body.dark-theme .map-slider-nav {
      background: rgba(30, 41, 59, 0.85);
      color: #f1f5f9;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
    }
    body.dark-theme .map-slider-nav:hover {
      background: #334155;
    }
    .map-slider-nav.prev {
      left: 15px;
    }
    .map-slider-nav.next {
      right: 15px;
    }
  </style>
</head>

<body>
  <!-- ====================================================== -->
  <!-- ======================= NAVBAR ======================= -->
  <!-- ====================================================== -->
  <nav class="navbar">
    <div class="container nav-wrapper">
      <div class="logo-list-navbar">
        <img src="assets/klh_logo.png" alt="KLH" title="Kementerian Lingkungan Hidup / Badan Pengendalian Lingkungan Hidup" style="height: 38px;" />
        <img src="assets/gef_logo.png" alt="GEF" title="Global Environment Facility" style="height: 38px;" />
        <img src="assets/undp_logo.png" alt="UNDP" title="United Nations Development Programme" style="height: 39px;" />
        <img src="assets/bmkg_logo.png" alt="BMKG" title="Badan Meteorologi, Klimatologi, dan Geofisika" style="height: 35px;" />
        <img src="assets/ipb_logo.png" alt="IPB University" title="IPB University" style="height: 38px;" />
      </div>
      <div class="nav-menu">
        <a href="javascript:void(0)" id="nav-beranda" onclick="showPage('beranda')" class="active">
          Beranda
        </a>
        <a href="javascript:void(0)" id="nav-historical-climate" onclick="showPage('historical-climate')">
          Historical Climate
        </a>
        <a href="javascript:void(0)" id="nav-future-climate" onclick="showPage('future-climate')">
          Future Climate
        </a>
        <a href="javascript:void(0)" id="nav-expert-mode" onclick="showPage('expert-mode')">
          Expert Mode
        </a>
        <a href="javascript:void(0)" id="nav-settings" onclick="showPage('settings')">
          Settings
        </a>
      </div>
    </div>
  </nav>

  <!-- ====================================================== -->
  <!-- ========================= HERO ======================= -->
  <!-- ====================================================== -->
  <section id="beranda">
    <div class="container" style="margin-top: 35px; line-height: 1.8; font-size: 15px;">
      <div class="card-detail-beranda">
        <!-- Hero Section (Disatukan ke dalam satu card) -->
        <div class="hero">
          <div class="hero-image">
            <img
              src="assets/ssp_matrix.jpg" alt="Shared Socioeconomic Pathways" />
          </div>
          <div class="hero-text">
            <h1>
              Climate Information System Indonesia
            </h1>
            <p style="text-align: justify; font-size: 15px; line-height: 1.8;">
              Climate Information System merupakan portal yang dibangun oleh Kementrian Lingkungan Hidup bekerjasama dengan IPB University dan dengan dukungan dari United Nations Development Programme (UNDP). Portal ini bertujuan untuk menyediakan informasi terkait iklim historis dan proyeksi hasil kajian the 4th National Communication (NC4) yang dapat digunakan oleh pemerintah pada tingkat nasional maupun daerah. Portal ini juga menjadi bagian dari support tim adaptasi NC4 untuk peningkatan literasi iklim dan perubahan iklim di Indonesia. Beberapa informasi dasar yang ada di dalam portal ini adalah peta rata-rata klimatologis, peta rata-rata bulanan, pola iklim, tren iklim, perubahan iklim dan tren iklim di masa mendatang.
            </p>
          </div>
        </div>
        <p style="text-align: justify; margin-bottom: 25px;">
          Variabel iklim yang tersedia pada portal ini meliputi curah hujan, suhu udara rata-rata, suhu udara maksimum, suhu udara minimum, radiasi matahari, kecepatan angin, kelembaban relatif serta parameter iklim laut seperti suhu permukaan laut, anomali tinggi muka laut, pH permukaan laut dan salinitas.
        </p>

        <h3 style="margin-top: 0; margin-bottom: 15px;">Sumber Data Historical Climate</h3>
        <p style="text-align: justify; margin-bottom: 15px;">
          Sumber data yang digunakan pada Historical Climate meliputi CHIRPS dan ERA5.
        </p>
        <p style="text-align: justify; margin-left: 20px; margin-bottom: 15px;">
          • <strong>CHIRPS</strong> merupakan data curah hujan kombinasi dari data observasi stasiun dan data observasi satelit yang kemudian dijadikan data grid. Data CHIRPS memiliki resolusi spasial 0,05° × 0,05° (~5 km). Informasi detail mengenai data CHIRPS dapat dilihat pada situs web berikut: <a href="http://chg.geog.ucsb.edu/data/chirps/" target="_blank" style="color: #2563eb; text-decoration: underline;">http://chg.geog.ucsb.edu/data/chirps/</a>
        </p>
        <p style="text-align: justify; margin-left: 20px; margin-bottom: 25px;">
          • <strong>ERA5</strong> merupakan data reanalysis wilayah atmosfer dan lautan yang disusun dari berbagai sumber data yang kemudian diinterpolasi menjadi resolusi spasial 0,1° × 0,1° (~11 km). Data ERA5 yang digunakan dalam system ini meliputi data suhu udara rata-rata, suhu udara maksimum, suhu udara minimum, kecepatan angin, kelembaban relative, radiasi matahari dan data parameter iklim laut. Untuk mengunduh data ini, dapat mengunjungi situs web berikut: <a href="https://cds.climate.copernicus.eu/datasets/reanalysis-era5-land?tab=download" target="_blank" style="color: #2563eb; text-decoration: underline;">https://cds.climate.copernicus.eu/datasets/reanalysis-era5-land?tab=download</a>
        </p>

        <h3 style="margin-top: 0; margin-bottom: 15px;">Data Proyeksi</h3>
        <p style="text-align: justify; margin-bottom: 25px;">
          Data proyeksi yang tersedia dalam sistem ini merupakan hasil analisis Statistical Downscaling dari data CMIP6 yang telah dituangkan dalam the 4th National Communication (NC4). Skenario proyeksi iklim yang tersedia dalam sistem ini adalah skenario SSP1-2.6, SSP2-4.5, SSP3-7.0 dan SSP5-8.5. Periode proyeksi iklim dibagi menjadi 6 (enam) periode, yaitu 2021-2050, 2031-2060, 2041-2070, 2051-2080, 2061-2090 dan 2071-2100 dengan baseline tahun 1991-2020.
        </p>

        <h3 style="margin-top: 0; margin-bottom: 15px;">Skenario Proyeksi SSPs (Shared Socioeconomic Pathways)</h3>
        <p style="text-align: justify; margin-bottom: 20px;">
          Skenario proyeksi iklim Shared Socioeconomic Pathways (SSPs) merupakan serangkaian narasi yang menggambarkan kemungkinan skenario Pembangunan masa depan bagi masyarakat manusia, khususnya terkait dengan penggunaan bahan bakar fosil. Skenario ini mengekplorasi berbagai masa depan teknologi, sosio-ekonomi, dan kebijakan serta mempertimbangkan tantangan terhadap mitigasi dan adaptasi (Riahi et al. 2017). Karakter setiap skenario SSP dapat dilihat pada tabel berikut.
        </p>

        <div class="premium-table-container">
          <table class="premium-table">
            <thead>
              <tr>
                <th style="width: 30%;">Skenario</th>
                <th style="width: 30%;">Emisi</th>
                <th>Karakter Dunia</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td><strong>SSP1-2.6</strong></td>
                <td>Rendah</td>
                <td>Berkelanjutan, Kolaboratif</td>
              </tr>
              <tr>
                <td><strong>SSP2-4.5</strong></td>
                <td>Sedang</td>
                <td>Menengah, Kompromi</td>
              </tr>
              <tr>
                <td><strong>SSP3-7.0</strong></td>
                <td>Tinggi</td>
                <td>Terfragmentasi, Proteksionis</td>
              </tr>
              <tr>
                <td><strong>SSP5-8.5</strong></td>
                <td>Sangat Tinggi</td>
                <td>Konsumerisme, Pertumbuhan Cepat</td>
              </tr>
            </tbody>
          </table>
        </div>

        <!-- SSP Diagrams Container -->
        <div style="margin-top: 40px; display: flex; flex-direction: column; gap: 40px; align-items: center;">
          <!-- SSP Perbedaan Diagram -->
          <div style="width: 100%; max-width: 800px; text-align: center;">
            <div style="background: white; border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); border: none; display: inline-block; width: 100%;">
              <img src="assets/ssp_perbedaan.png" alt="Deskripsi perbedaan setiap skenario SSP" style="width: 100%; height: auto; border-radius: 8px;" />
            </div>
            <p style="margin-top: 10px; font-weight: 600; font-size: 14px; color: var(--text-color, #475569);">Deskripsi perbedaan setiap skenario SSP</p>
          </div>

          <!-- SSP Family Diagram -->
          <div style="width: 100%; max-width: 800px; text-align: center;">
            <div style="background: white; border-radius: 12px; padding: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.05); border: none; display: inline-block; width: 100%;">
              <img src="assets/ssp_family.png" alt="Famili scenario SSP" style="width: 100%; height: auto; border-radius: 8px;" />
            </div>
            <p style="margin-top: 10px; font-weight: 600; font-size: 14px; color: var(--text-color, #475569);">Famili scenario SSP (Sumber: Meinshausen et al. (2020))</p>
          </div>
        </div>

      </div>
    </div>
  </section>

  <!-- ====================================================== -->
  <!-- ================= HISTORICAL CLIMATE ================= -->
  <!-- ====================================================== -->
  <section class="container" id="historical-climate">
    <h2 class="section-title" id="historicalTitle">
      Historical Climate
    </h2>
    <p class="selected-info" id="historicalDescription">
      Visualisasi data iklim historis Indonesia.
    </p>
    <div id="historicalDataInfo" style="margin-top:20px;padding:20px;background:#eff6ff;
    border-radius:15px;color:#0f172a;font-weight:500;line-height:1.8;">
      Silakan terapkan filter untuk menampilkan informasi data iklim historis.
    </div>

    <div class="form-section">
      <h3 style="margin-bottom:25px;">
        Filter Historical Climate
      </h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Pilih Lokasi</label>
          <select id="historicalLocation">
            <!-- Diisi secara dinamis dari DB -->
          </select>
        </div>
        <div class="form-box">
          <label>Pilih Variabel</label>
          <select id="historicalVariable">
            <!-- Diisi secara dinamis dari DB -->
          </select>
        </div>
      </div>
      <button class="save-btn" onclick="applyHistoricalFilter()">
        Terapkan Filter Historical Climate
      </button>
    </div>

    <div class="category-grid">
      <div class="card-grid">
        <div style="text-align:center;">
          <img id="historicalImageAnnual"
            src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
            onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
            referrerpolicy="no-referrer"
            style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          <div class="category-content">
            <h3 id="historicalCategoryTitleAnnual">Annual Mean Area</h3>
          </div>
        </div>
      </div>
      <div class="card-grid">
        <div style="text-align:center;">
          <img id="historicalImageTrend"
            src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
            onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
            referrerpolicy="no-referrer"
            style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          <div class="category-content">
            <h3 id="historicalCategoryTitleTrend">Trend Map</h3>
          </div>
        </div>
      </div>
      <div class="card-grid">
        <div style="text-align:center;">
          <img id="historicalImageTimeSeries"
            src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
            onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
            referrerpolicy="no-referrer"
            style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          <div class="category-content">
            <h3 id="historicalCategoryTitleTimeSeries">Time Series</h3>
          </div>
        </div>
      </div>
      <div class="card-grid">
        <div style="text-align:center;">
          <img id="historicalImageMonthlyMap"
            src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
            onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
            referrerpolicy="no-referrer"
            style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          <div class="category-content">
            <h3 id="historicalCategoryTitleMonthlyMap">Monthly Map</h3>
          </div>
        </div>
      </div>
      <div class="card-grid">
        <div style="text-align:center;">
          <img id="historicalImageMonthlyGraph"
            src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
            onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
            referrerpolicy="no-referrer"
            style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          <div class="category-content">
            <h3 id="historicalCategoryTitleMonthlyGraph">Monthly Graph</h3>
          </div>
        </div>
      </div>
    </div>
  </section>

  <!-- ====================================================== -->
  <!-- ================= FUTURE CLIMATE ===================== -->
  <!-- ====================================================== -->
  <section class="container" id="future-climate">
    <h2 class="section-title">
      Future Climate Projection
    </h2>
    <p class="selected-info" id="futureDescription">
      Visualisasi proyeksi perubahan iklim masa depan.
    </p>
    <div id="futureDataInfo" style="margin-top:20px;padding:20px;background:#eff6ff;
    border-radius:15px;color:#0f172a;font-weight:500;line-height:1.8;">
      Silakan terapkan filter untuk menampilkan informasi proyeksi masa depan.
    </div>

    <div class="form-section">
      <h3 style="margin-bottom:25px;">
        Filter Future Climate
      </h3>
      <div class="form-grid" style="grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));">
        <div class="form-box">
          <label>Pilih Lokasi</label>
          <select id="futureLocation">
            <!-- Diisi secara dinamis -->
          </select>
        </div>
        <div class="form-box">
          <label>Pilih Variabel</label>
          <select id="futureVariable">
            <!-- Diisi secara dinamis -->
          </select>
        </div>
        <div class="form-box">
          <label>Pilih Periode (Map & Stripe)</label>
          <select id="futurePeriod">
            <!-- Diisi secara dinamis -->
          </select>
        </div>

      </div>
      <button class="save-btn" onclick="applyFutureFilter()">
        Terapkan Filter Future Climate
      </button>
    </div>

    <div class="category-grid">
      <div class="card-grid">
        <div style="text-align:center;">
          <!-- Tab Selector Buttons -->
          <div class="map-toggle-wrapper" id="mapSelectorContainer">
            <button id="btnMap1" class="map-toggle-btn active" onclick="switchClimateMap(1)">Peta Proyeksi 1</button>
            <button id="btnMap2" class="map-toggle-btn" onclick="switchClimateMap(2)">Peta Proyeksi 2</button>
          </div>
          
          <!-- Image Slider Wrapper -->
          <div style="position: relative; overflow: hidden; max-width: 100%; margin: 0 auto 20px auto; border-radius: 15px;">
            <!-- Left Navigation Button -->
            <button class="map-slider-nav prev" onclick="prevClimateMap()">&#10094;</button>
            
            <!-- Image 1 Container -->
            <div id="containerMap1" style="display: block; transition: opacity 0.3s ease;">
              <img id="futureImageClimateMap"
                src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar 1%3C/text%3E%3C/svg%3E"
                onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
                referrerpolicy="no-referrer"
                style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
              <div class="map-label">Peta Proyeksi 1</div>
            </div>
            
            <!-- Image 2 Container -->
            <div id="containerMap2" style="display: none; transition: opacity 0.3s ease;">
              <img id="futureImageClimateMap2"
                src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar 2%3C/text%3E%3C/svg%3E"
                onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
                referrerpolicy="no-referrer"
                style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
              <div class="map-label">Peta Proyeksi 2</div>
            </div>
            
            <!-- Right Navigation Button -->
            <button class="map-slider-nav next" onclick="nextClimateMap()">&#10095;</button>
          </div>
          <div class="category-content">
            <h3 id="futureCategoryTitleMap">Climate Change Map</h3>
            <p id="futureVariableText">Variabel: -</p>
            <p id="futurePeriodText">Periode: -</p>
          </div>
        </div>
      </div>
      <div class="card-grid">

        <div style="text-align:center;">
          <!-- Interactive Chart.js Canvas -->
          <div id="futureInteractiveChartContainer" class="interactive-chart-card" style="width: 100%; max-width: 100%; margin: 0 auto 20px auto; position: relative; height: 480px; border-radius: 15px; padding: 25px 25px 15px 25px; display: block; text-align: left; transition: all 0.3s ease;">
            <h3 id="futureChartTitle" style="font-family: 'Poppins', sans-serif; font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 12px; line-height: 1.3;">Projected Precipitation Amount until 2100</h3>
            <div id="futureChartLegendOverlay" class="custom-chart-legend" style="position: absolute; top: 65px; left: 80px; z-index: 10; font-size: 13px; pointer-events: none; font-family: 'Poppins', sans-serif; display: flex; flex-direction: column; gap: 6px; text-align: left; background: rgba(255,255,255,0.75); padding: 8px 12px; border-radius: 6px;">
              <div style="display: flex; flex-direction: column; gap: 4px;">
                <div style="display: flex; align-items: center; gap: 8px;">
                  <span style="display: inline-block; width: 25px; height: 3px; background-color: #000000; border-radius: 1px;"></span>
                  <span style="color: #000000; font-weight: bold; font-family: 'Poppins', sans-serif;">Hist</span>
                </div>
                <div style="display: flex; align-items: center; gap: 8px;">
                  <span style="display: inline-block; width: 25px; height: 3px; background-color: #b30000; border-radius: 1px;"></span>
                  <span style="color: #b30000; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP5-8.5</span>
                </div>
                <div style="display: flex; align-items: center; gap: 8px;">
                  <span style="display: inline-block; width: 25px; height: 3px; background-color: #e67e22; border-radius: 1px;"></span>
                  <span style="color: #e67e22; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP3-7.0</span>
                </div>
                <div style="display: flex; align-items: center; gap: 8px;">
                  <span style="display: inline-block; width: 25px; height: 3px; background-color: #f1c40f; border-radius: 1px;"></span>
                  <span style="color: #f1c40f; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP2-4.5</span>
                </div>
                <div style="display: flex; align-items: center; gap: 8px;">
                  <span style="display: inline-block; width: 25px; height: 3px; background-color: #2980b9; border-radius: 1px;"></span>
                  <span style="color: #2980b9; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP1-2.6</span>
                </div>
              </div>
            </div>
            <div style="position: relative; width: 100%; height: 360px;">
              <canvas id="futureChartCanvas"></canvas>
            </div>
          </div>
          <!-- Static Image Graph (hidden by default) -->
          <div id="futureStaticTSContainer" style="display: none; text-align: center; margin-bottom: 20px;">
            <img id="futureImageTimeSeries"
              src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
              onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
              referrerpolicy="no-referrer"
              style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
          </div>
          <div class="category-content">
            <h3 id="futureCategoryTitleTimeSeries">Future Time Series Graph</h3>
            <p id="futureVariableTextTS">Variabel: -</p>
            <p id="futurePeriodTextTS">Periode: -</p>
          </div>
        </div>
      </div>
      <div class="card-grid">
        <div class="stripe-filter-wrapper" style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; margin-bottom: 20px; text-align: left; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
          <div style="display: flex; flex-direction: column; gap: 8px;">
            <label style="font-size: 13px; font-weight: 600; color: #475569;">Pilih Skenario Climate Stripe</label>
            <div class="scenario-slider-container" style="background: #e2e8f0; border-radius: 10px; padding: 4px; display: flex; position: relative; width: 100%; box-sizing: border-box; min-height: 44px; align-items: center;">
              <!-- Sliding Indicator -->
              <div id="stripeScenarioIndicator" style="position: absolute; height: calc(100% - 8px); top: 4px; left: 4px; width: calc(25% - 6px); background: #ffffff; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 1;"></div>
              
              <button type="button" onclick="setStripeScenario('SSP1-2.6', 0)" id="btnStripeSSP1" class="scenario-slide-btn" style="flex: 1; border: none; background: transparent; padding: 8px 0; font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 600; color: #2563eb; cursor: pointer; position: relative; z-index: 2; text-align: center; border-radius: 8px; transition: color 0.3s ease; outline: none;">SSP1-2.6</button>
              <button type="button" onclick="setStripeScenario('SSP2-4.5', 1)" id="btnStripeSSP2" class="scenario-slide-btn" style="flex: 1; border: none; background: transparent; padding: 8px 0; font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 600; color: #64748b; cursor: pointer; position: relative; z-index: 2; text-align: center; border-radius: 8px; transition: color 0.3s ease; outline: none;">SSP2-4.5</button>
              <button type="button" onclick="setStripeScenario('SSP3-7.0', 2)" id="btnStripeSSP3" class="scenario-slide-btn" style="flex: 1; border: none; background: transparent; padding: 8px 0; font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 600; color: #64748b; cursor: pointer; position: relative; z-index: 2; text-align: center; border-radius: 8px; transition: color 0.3s ease; outline: none;">SSP3-7.0</button>
              <button type="button" onclick="setStripeScenario('SSP5-8.5', 3)" id="btnStripeSSP5" class="scenario-slide-btn" style="flex: 1; border: none; background: transparent; padding: 8px 0; font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 600; color: #64748b; cursor: pointer; position: relative; z-index: 2; text-align: center; border-radius: 8px; transition: color 0.3s ease; outline: none;">SSP5-8.5</button>
            </div>
          </div>
        </div>
        
        <div style="text-align:center;">
          <div style="position: relative; display: inline-block; max-width: 100%;">
            <!-- Left Slide Button -->
            <button type="button" onclick="prevStripeScenario()" style="position: absolute; left: 15px; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.85); border: none; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.15); transition: all 0.2s ease; z-index: 5;" onmouseover="this.style.background='#ffffff'; this.style.transform='translateY(-50%) scale(1.1)';" onmouseout="this.style.background='rgba(255,255,255,0.85)'; this.style.transform='translateY(-50%)';">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="color: #1e293b;"><polyline points="15 18 9 12 15 6"></polyline></svg>
            </button>
            
            <img id="futureImageClimateStripe"
              src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E"
              onerror="if (typeof PLACEHOLDER_IMG !== 'undefined' && this.src !== PLACEHOLDER_IMG) this.src = PLACEHOLDER_IMG;"
              referrerpolicy="no-referrer"
              style="max-width:100%; border-radius:15px; box-shadow:0 4px 15px rgba(0,0,0,0.15);" />
              
            <!-- Right Slide Button -->
            <button type="button" onclick="nextStripeScenario()" style="position: absolute; right: 15px; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.85); border: none; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: 0 4px 10px rgba(0,0,0,0.15); transition: all 0.2s ease; z-index: 5;" onmouseover="this.style.background='#ffffff'; this.style.transform='translateY(-50%) scale(1.1)';" onmouseout="this.style.background='rgba(255,255,255,0.85)'; this.style.transform='translateY(-50%)';">
              <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="color: #1e293b;"><polyline points="9 18 15 12 9 6"></polyline></svg>
            </button>
          </div>
          <div class="category-content">
            <h3 id="futureCategoryTitleStripe">Future Climate Stripe</h3>
            <p id="futureVariableTextStripe">Variabel: -</p>
            <p id="futurePeriodTextStripe">Periode: -</p>
          </div>
        </div>
      </div>
    </div>
  </section>

  <!-- ====================================================== -->
  <!-- ==================== EXPERT MODE ===================== -->
  <!-- ====================================================== -->
  <section class="container" id="expert-mode">
    <h2 class="section-title">
      Expert Mode
    </h2>
    <p class="selected-info">
      Halaman ini memberikan keleluasaan bagi para pengguna untuk melakukan analisis tingkat Kabupaten/Kota.
    </p>
    <div class="expert-grid">
      <div class="expert-card">
        <div class="expert-content">
          <div class="expert-title">
            🌧️ Analisis Curah Hujan
          </div>
          <div class="expert-description">
            Analisis curah hujan detail untuk wilayah Indonesia menggunakan Google Colab.
          </div>
          <div class="expert-link">
            <a href="https://colab.research.google.com/drive/1UDse-nkZ5HZAcUImxQEmp70n5gHJg8Dy" target="_blank">
              Buka GCollab
            </a>
            <a href="https://drive.google.com/file/d/165k1nPch-NgeJ-C2BXjHmqhU-eU37gZK/view?usp=drive_link">
              Download Modul
            </a>
          </div>
        </div>
      </div>
      <div class="expert-card">
        <div class="expert-content">
          <div class="expert-title">
            🌡️ Analisis Suhu Udara
          </div>
          <div class="expert-description">
            Analisis suhu udara detail dengan visualisasi dan pemodelan klimatologi.
          </div>
          <div class="expert-link">
            <a href="https://colab.research.google.com/drive/1JZwyADVKhDTzv3kNspxaufq7J-_Aqc1w?usp=sharing"
              target="_blank">
              Buka GCollab
            </a>
            <a href="https://drive.google.com/file/d/1fkThdHxBglLxN9VPpqFfmIfGj5eezuEf/view?usp=drive_link">
              Download Modul
            </a>
          </div>
        </div>
      </div>
      <div class="expert-card">
        <div class="expert-content">
          <div class="expert-title">
            🌊 Analisis Iklim Laut
          </div>
          <div class="expert-description">
            Analisis suhu permukaan laut, salinitas, dan dinamika iklim laut Indonesia.
          </div>
          <div class="expert-link">
            <a href="https://colab.research.google.com/drive/1zAfwAaYdkwPoBhQC-9TWoKtMHTGDaMXD?usp=sharing" target="_blank">
              Buka GCollab
            </a>
            <a href="https://drive.google.com/file/d/1EVfwmTu3hovAWYjSe9MD_2nPFyUgrcq7/view?usp=drive_link">
              Download Modul
            </a>
          </div>
        </div>
      </div>
      <div class="expert-card">
        <div class="expert-content">
          <div class="expert-title">
            ⚡ Analisis Iklim Ekstrem
          </div>
          <div class="expert-description">
            Analisis kejadian iklim ekstrim dan perubahan iklim masa depan Indonesia.
          </div>
          <div class="expert-link">
            <a href="https://colab.research.google.com/drive/1GiWZJ6O5CEunmL6ji3E5SNwjgOeeeX5a?usp=drive_link#scrollTo=Tnfn5xC95_Uq"
              target="_blank">
              Buka GCollab
            </a>
            <a href="https://drive.google.com/file/d/1aWIYH5a3f12mYwA-5fSFivDEK0u7u2BM/view?usp=drive_link">
              Download Modul
            </a>
          </div>
        </div>
      </div>
    </div>
  </section>

  <!-- ====================================================== -->
  <!-- ======================= SETTINGS ===================== -->
  <!-- ====================================================== -->
  <section class="container" id="settings">
    <div style="display: flex; justify-content: space-between; align-items: center; margin: 70px 0 18px; flex-wrap: wrap; gap: 15px;">
      <div style="display: flex; align-items: center; gap: 15px; flex-wrap: wrap;">
        <h2 class="section-title" style="margin: 0;">Settings Climate Data</h2>
        <span id="autosaveStatusBadge" style="font-size: 13px; font-weight: 500; color: #64748b; background: #f1f5f9; padding: 4px 10px; border-radius: 20px; transition: all 0.3s ease; display: inline-flex; align-items: center; gap: 5px;">
          <span style="display: inline-block; width: 8px; height: 8px; background: #3b82f6; border-radius: 50%; transition: all 0.3s ease;"></span> Auto-save Aktif
        </span>
      </div>
      <button onclick="submitAdminLogout()" class="save-btn" style="margin: 0; background: linear-gradient(135deg, #ef4444, #b91c1c); display: inline-flex; align-items: center; gap: 8px; box-shadow: 0 4px 12px rgba(239, 68, 68, 0.25);">
        <span>🚪</span> Logout Admin
      </button>
    </div>
    <p class="selected-info">
      Pengaturan data Historical Climate & Future Climate berdasarkan filter lokasi, variable, dan period.
    </p>
 
    <div class="form-section">
      <h3 style="margin-bottom:20px;">Tambah Data Master</h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Tambah Lokasi Baru</label>
          <input id="masterLocationName" placeholder="Contoh: Jawa Barat" type="text" />
          <button class="save-btn" style="margin-top: 10px;" onclick="addMasterLocation()">Tambah Lokasi</button>
        </div>
        <div class="form-box">
          <label>Tambah Variabel Baru</label>
          <input id="masterVariableName" placeholder="Contoh: Evapotranspirasi" type="text" />
          <button class="save-btn" style="margin-top: 10px;" onclick="addMasterVariable()">Tambah Variabel</button>
        </div>
        <div class="form-box">
          <label>Tambah Periode Baru</label>
          <input id="masterPeriodRange" placeholder="Contoh: 2081-2110" type="text" />
          <button class="save-btn" style="margin-top: 10px;" onclick="addMasterPeriod()">Tambah Periode</button>
        </div>
      </div>
    </div>
 
    <div class="form-section">
      <h3 style="margin-bottom:20px;">Tambah / Edit Data Historical Climate</h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Lokasi</label>
          <select id="historicalSetLocation">
            <!-- Diisi secara dinamis dari DB (hanya Indonesia) -->
          </select>
        </div>
        <div class="form-box">
          <label>Variable</label>
          <input id="historicalSetVariable" placeholder="Contoh: Precipitation" type="text" />
        </div>
        <div class="form-box">
          <label>Deskripsi Climate</label>
          <input id="historicalSetDescription" placeholder="Masukkan deskripsi climate" type="text" />
        </div>
        <div class="form-box">
          <label>URL Gambar Annual</label>
          <input id="historicalSetImage" placeholder="Masukkan URL gambar (Annual)" type="text" />
          <label>URL Gambar Trend Map</label>
          <input id="historicalSetImageTrend" placeholder="Masukkan URL gambar (Trend)" type="text" />
          <label>URL Gambar Time Series</label>
          <input id="historicalSetImageTimeSeries" placeholder="Masukkan URL gambar (Time Series)" type="text" />
          <label>URL Gambar Monthly Map</label>
          <input id="historicalSetImageMonthlyMap" placeholder="Masukkan URL gambar (Monthly Map)" type="text" />
          <label>URL Gambar Monthly Graph</label>
          <input id="historicalSetImageMonthlyGraph" placeholder="Masukkan URL gambar (Monthly Graph)" type="text" />
        </div>
      </div>
      <button class="save-btn" onclick="saveHistoricalData()">Simpan Data Historical</button>
    </div>

    <div class="form-section" style="margin-top:30px;">
      <h3 style="margin-bottom:20px;">Tambah / Edit Data Future Climate</h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Lokasi</label>
          <select id="futureSetLocation">
            <!-- Diisi secara dinamis dari DB -->
          </select>
        </div>
        <div class="form-box">
          <label>Variable</label>
          <input id="futureSetVariable" placeholder="Contoh: Precipitation" type="text" />
        </div>
        <div class="form-box">
          <label>Periode</label>
          <input id="futureSetPeriod" placeholder="Contoh: 2021-2050" type="text" />
        </div>
        <div class="form-box" style="display: flex; gap: 10px; align-items: flex-end;">
          <button class="save-btn" type="button" onclick="loadFutureData()" style="background: #475569; margin: 0; width: 100%;">Muat Data Existing</button>
        </div>
        <div class="form-box" style="grid-column: 1 / -1;">
          <label>Deskripsi Climate</label>
          <input id="futureSetDescription" placeholder="Masukkan deskripsi climate" type="text" />
        </div>
        <div class="form-box" style="grid-column: 1 / -1; display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 15px;">
          <div>
            <label>URL Gambar Climate Change Map 1</label>
            <input id="futureSetImageClimateMap" placeholder="Masukkan URL gambar 1 (Climate Change Map)" type="text" />
          </div>
          <div>
            <label>URL Gambar Climate Change Map 2</label>
            <input id="futureSetImageClimateMap2" placeholder="Masukkan URL gambar 2 (Climate Change Map)" type="text" />
          </div>
          <div>
            <label>URL Gambar Future Climate Stripe</label>
            <input id="futureSetImageClimateStripe" placeholder="Masukkan URL gambar (Climate Stripe)" type="text" />
          </div>
          <div>
            <label>URL Gambar Time Series Graph</label>
            <input id="futureSetImageTimeSeries" placeholder="Masukkan URL gambar (Time Series)" type="text" />
          </div>
        </div>
      </div>
      <button class="save-btn" onclick="saveFutureData()" style="margin-top:20px;">Simpan Data Future Climate</button>
    </div>

    <div class="form-section" style="margin-top:30px;">
      <h3 style="margin-bottom:20px;">Tambah / Edit Parameter Grafik Interaktif Proyeksi (Opsional)</h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Lokasi</label>
          <select id="futureSetParamsLocation">
            <!-- Diisi secara dinamis -->
          </select>
        </div>
        <div class="form-box">
          <label>Variable</label>
          <input id="futureSetParamsVariable" placeholder="Contoh: Precipitation" type="text" />
        </div>
        <div class="form-box" style="display: flex; gap: 10px; align-items: flex-end;">
          <button class="save-btn" type="button" onclick="loadFutureParamsData()" style="background: #475569; margin: 0; width: 100%;">Muat Parameter</button>
        </div>
        <div class="form-box" style="grid-column: 1 / -1; border-top: 1px dashed #cbd5e1; padding-top: 15px; margin-top: 15px;">
          <h4 style="margin-bottom: 8px; color: #2563eb;">Gunakan Data CSV untuk Grafik Interaktif (Opsional)</h4>
          <div class="form-box">
            <label for="futureSetParamsCsvFile">Upload Data Grafik (.csv)</label>
            <input type="file" id="futureSetParamsCsvFile" accept=".csv" style="padding: 10px; border: 1px solid #cbd5e1; border-radius: 12px; background: #fff;" />
            <input type="hidden" id="futureSetParamsCsvData" />
            <small style="color: #64748b; margin-top: 6px; line-height: 1.5; display: block;">
              Jika diunggah, data grafik interaktif akan digambar langsung dari file CSV ini.
              <br>Format CSV yang didukung: Harus memiliki header kolom <strong>Tahun / Year</strong> (contoh: <em>Year, Historical, SSP1, SSP2, SSP3, SSP5</em>). Untuk memvisualisasikan rentang ketidakpastian kustom, Anda juga dapat menyertakan kolom batas bawah (min) dan batas atas (max) opsional seperti <em>SSP1_min, SSP1_max, SSP2_min, SSP2_max</em>, dll.
              <span id="csvStatusIndicator" style="display: block; font-weight: 600; color: #16a34a; margin-top: 4px;"></span>
            </small>
          </div>
        </div>
        <div class="form-box" style="grid-column: 1 / -1; border-top: 1px dashed #cbd5e1; padding-top: 15px; margin-top: 15px;">
          <h4 style="margin-bottom: 12px; color: #2563eb;">Preview Grafik Interaktif (Hasil Pengaturan / CSV)</h4>
          <div class="interactive-chart-card" style="position: relative; width: 100%; height: 460px; border-radius: 12px; padding: 25px 25px 15px 25px; border: 1px solid #cbd5e1; text-align: left;">
            <h3 id="settingsFutureChartTitle" style="font-family: 'Poppins', sans-serif; font-size: 15px; font-weight: 600; color: #1e293b; margin-bottom: 12px; line-height: 1.3;">Projected Variable until 2100</h3>
             <div class="custom-chart-legend" style="position: absolute; top: 65px; left: 80px; z-index: 10; font-size: 12px; pointer-events: none; font-family: 'Poppins', sans-serif; display: flex; flex-direction: column; gap: 5px; text-align: left; background: rgba(255,255,255,0.75); padding: 8px 12px; border-radius: 6px;">
              <div style="display: flex; flex-direction: column; gap: 3px;">
                <div style="display: flex; align-items: center; gap: 6px;">
                  <span style="display: inline-block; width: 20px; height: 3px; background-color: #000000; border-radius: 1px;"></span>
                  <span style="color: #000000; font-weight: bold; font-family: 'Poppins', sans-serif;">Hist</span>
                </div>
                <div style="display: flex; align-items: center; gap: 6px;">
                  <span style="display: inline-block; width: 20px; height: 3px; background-color: #b30000; border-radius: 1px;"></span>
                  <span style="color: #b30000; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP5-8.5</span>
                </div>
                <div style="display: flex; align-items: center; gap: 6px;">
                  <span style="display: inline-block; width: 20px; height: 3px; background-color: #e67e22; border-radius: 1px;"></span>
                  <span style="color: #e67e22; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP3-7.0</span>
                </div>
                <div style="display: flex; align-items: center; gap: 6px;">
                  <span style="display: inline-block; width: 20px; height: 3px; background-color: #f1c40f; border-radius: 1px;"></span>
                  <span style="color: #f1c40f; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP2-4.5</span>
                </div>
                <div style="display: flex; align-items: center; gap: 6px;">
                  <span style="display: inline-block; width: 20px; height: 3px; background-color: #2980b9; border-radius: 1px;"></span>
                  <span style="color: #2980b9; font-weight: bold; font-family: 'Poppins', sans-serif;">SSP1-2.6</span>
                </div>
              </div>
            </div>
            <div style="position: relative; width: 100%; height: 350px;">
              <canvas id="settingsFutureParamsChartCanvas"></canvas>
            </div>
          </div>
        </div>
      </div>
      <button class="save-btn" onclick="saveFutureParamsData()" style="margin-top:20px;">Simpan Parameter Grafik Proyeksi</button>
    </div>

    <!-- Form 1B: Pengaturan Climate Stripe Proyeksi (Per Skenario) -->
    <div class="form-section" style="margin-top:30px;">
      <h3 style="margin-bottom:20px;">Tambah / Edit Climate Stripe Proyeksi (Per Skenario)</h3>
      <div class="form-grid">
        <div class="form-box">
          <label>Lokasi</label>
          <select id="futureSetStripeLocation" onchange="loadFutureStripeSettingsData()">
            <!-- Diisi secara dinamis -->
          </select>
        </div>
        <div class="form-box">
          <label>Variable</label>
          <select id="futureSetStripeVariable" onchange="loadFutureStripeSettingsData()">
            <!-- Diisi secara dinamis -->
          </select>
        </div>
        <div class="form-box" style="grid-column: 1 / -1; border-top: 1px dashed #cbd5e1; padding-top: 15px; margin-top: 15px;">
          <h4 style="margin-bottom: 12px; color: #2563eb;">Masukkan URL Gambar Climate Stripe untuk Masing-Masing Skenario</h4>
          <div class="form-grid" style="grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));">
            <div class="form-box">
              <label>SSP1-2.6</label>
              <input id="futureSetStripeSSP126" placeholder="Masukkan URL gambar SSP1-2.6" type="text" />
            </div>
            <div class="form-box">
              <label>SSP2-4.5</label>
              <input id="futureSetStripeSSP245" placeholder="Masukkan URL gambar SSP2-4.5" type="text" />
            </div>
            <div class="form-box">
              <label>SSP3-7.0</label>
              <input id="futureSetStripeSSP370" placeholder="Masukkan URL gambar SSP3-7.0" type="text" />
            </div>
            <div class="form-box">
              <label>SSP5-8.5</label>
              <input id="futureSetStripeSSP585" placeholder="Masukkan URL gambar SSP5-8.5" type="text" />
            </div>
          </div>
        </div>
      </div>
      <button class="save-btn" onclick="saveFutureStripeSettings()">Simpan Climate Stripe</button>
    </div>
  </section>

  <!-- ====================================================== -->
  <!-- ================= ADMIN LOGIN MODAL ================== -->
  <!-- ====================================================== -->
  <div id="adminLoginModal" class="login-modal-overlay" style="display: none;">
    <div class="login-modal-content">
      <div class="login-modal-header">
        <div class="login-logo-circle">🔑</div>
        <h3>Admin Authentication</h3>
        <p>Silakan login untuk mengakses halaman pengaturan</p>
      </div>
      <form onsubmit="event.preventDefault(); submitAdminLogin();" style="display: flex; flex-direction: column; gap: 15px; margin-top: 20px;">
        <div class="form-box" style="text-align: left;">
          <label style="font-size: 13px; font-weight: 600;">Username</label>
          <input id="loginUsername" type="text" placeholder="Masukkan username" required style="width: 100%;" />
        </div>
        <div class="form-box" style="text-align: left;">
          <label style="font-size: 13px; font-weight: 600;">Password</label>
          <input id="loginPassword" type="password" placeholder="Masukkan password" required style="width: 100%;" />
        </div>
        <div id="loginErrorMessage" style="color: #ef4444; font-size: 13px; font-weight: 500; text-align: center; display: none;">
          Username atau Password salah.
        </div>
        <button type="submit" class="save-btn" style="margin-top: 10px; width: 100%;">
          Log In
        </button>
        <button type="button" onclick="closeAdminLoginModal()" class="save-btn" style="margin-top: 5px; width: 100%; background: #64748b;">
          Batal
        </button>
      </form>
    </div>
  </div>

  <!-- ====================================================== -->
  <!-- ======================= FOOTER ======================= -->
  <!-- ====================================================== -->
  <footer>
    © 2026 Climate Information System Indonesia
  </footer>

  <!-- ====================================================== -->
  <!-- ======================= SCRIPT ======================= -->
  <!-- ====================================================== -->
  <script>
    const PLACEHOLDER_IMG = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='400'%3E%3Crect width='800' height='400' fill='%23f1f5f9' rx='15'/%3E%3Ctext x='400' y='200' text-anchor='middle' font-family='Arial,sans-serif' font-size='18' fill='%2394a3b8'%3ETerapkan Filter untuk menampilkan gambar%3C/text%3E%3C/svg%3E";

    const pages = [
      "beranda",
      "historical-climate",
      "future-climate",
      "expert-mode",
      "settings"
    ];

    let isAdminLoggedIn = false;

    async function showPage(pageId) {
      if (pageId === "settings" && !isAdminLoggedIn) {
        try {
          const res = await fetch('?action=check_auth');
          const authData = await res.json();
          if (authData.authenticated) {
            isAdminLoggedIn = true;
          } else {
            openAdminLoginModal();
            return;
          }
        } catch (err) {
          console.error('Gagal memverifikasi status login:', err);
          openAdminLoginModal();
          return;
        }
      }

      pages.forEach(function (id) {
        const el = document.getElementById(id);
        if (el) {
          el.style.display = "none";
        }
        const navLink = document.getElementById("nav-" + id);
        if (navLink) {
          navLink.classList.remove("active");
        }
      });

      const activePage = document.getElementById(pageId);
      if (activePage) {
        if (pageId === "beranda") {
          activePage.style.display = "block";
        } else {
          activePage.style.display = "block";
        }
      }

      const activeNavLink = document.getElementById("nav-" + pageId);
      if (activeNavLink) {
        activeNavLink.classList.add("active");
      }

      window.scrollTo({
        top: 0,
        behavior: "smooth"
      });
    }

    function openAdminLoginModal() {
      document.getElementById('adminLoginModal').style.display = 'flex';
      document.getElementById('loginUsername').value = '';
      document.getElementById('loginPassword').value = '';
      document.getElementById('loginErrorMessage').style.display = 'none';
    }

    function closeAdminLoginModal() {
      document.getElementById('adminLoginModal').style.display = 'none';
      showPage('beranda');
    }

    async function submitAdminLogin() {
      const usernameInput = document.getElementById('loginUsername');
      const passwordInput = document.getElementById('loginPassword');
      const errorMsg = document.getElementById('loginErrorMessage');

      const username = usernameInput.value.trim();
      const password = passwordInput.value.trim();

      try {
        const res = await fetch('?action=login', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ username, password })
        });
        const result = await res.json();

        if (result.success) {
          isAdminLoggedIn = true;
          document.getElementById('adminLoginModal').style.display = 'none';
          showPage('settings');
          alert('Login Admin berhasil!');
        } else {
          errorMsg.innerText = result.error || 'Username atau Password salah.';
          errorMsg.style.display = 'block';
        }
      } catch (err) {
        console.error(err);
        alert('Gagal menghubungi server untuk login.');
      }
    }

    async function submitAdminLogout() {
      try {
        const res = await fetch('?action=logout');
        const result = await res.json();
        if (result.success) {
          isAdminLoggedIn = false;
          alert('Anda berhasil logout dari Admin.');
          showPage('beranda');
        }
      } catch (err) {
        console.error(err);
        alert('Gagal menghubungi server untuk logout.');
      }
    }

    function showAutosaveStatus(message, isSuccess = null) {
      const badge = document.getElementById('autosaveStatusBadge');
      if (!badge) return;
      
      const dot = badge.querySelector('span');
      if (badge.childNodes && badge.childNodes[1]) {
        badge.childNodes[1].nodeValue = ' ' + message;
      }
      
      if (isSuccess === true) {
        if (dot) dot.style.background = '#10b981';
        badge.style.background = '#ecfdf5';
        badge.style.color = '#065f46';
        setTimeout(() => {
          if (dot) dot.style.background = '#3b82f6';
          badge.style.background = '#f1f5f9';
          badge.style.color = '#64748b';
          if (badge.childNodes && badge.childNodes[1]) {
            badge.childNodes[1].nodeValue = ' Auto-save Aktif';
          }
        }, 3000);
      } else if (isSuccess === false) {
        if (dot) dot.style.background = '#ef4444';
        badge.style.background = '#fef2f2';
        badge.style.color = '#991b1b';
      } else {
        if (dot) dot.style.background = '#eab308';
        badge.style.background = '#fef9c3';
        badge.style.color = '#854d0e';
      }
    }

    function normalizeImageUrl(url) {
      if (!url) return '';
      const trimmed = url.trim();

      let id = null;
      const fileDMatch = trimmed.match(/(?:drive|docs)\.google\.com\/file\/d\/([a-zA-Z0-9_-]+)/);
      if (fileDMatch) {
        id = fileDMatch[1];
      } else {
        const idMatch = trimmed.match(/[?&]id=([a-zA-Z0-9_-]+)/);
        if (idMatch && (trimmed.includes('drive.google.com') || trimmed.includes('docs.google.com'))) {
          id = idMatch[1];
        }
      }

      if (id) {
        return `https://lh3.googleusercontent.com/d/${id}`;
      }
      return trimmed;
    }

    // ======================================================
    // ============ DINAMIS: DROPDOWN & API DB ===============
    // ======================================================

    async function loadFilters() {
      try {
        const res = await fetch('?action=get_filters');
        const data = await res.json();
        if (data.success) {
          populateDropdown('historicalLocation', data.locations);
          populateDropdown('futureLocation', data.locations);
          populateDropdown('historicalSetLocation', data.locations);
          populateDropdown('futureSetLocation', data.locations);
          populateDropdown('futureSetParamsLocation', data.locations);
          populateDropdown('futureSetStripeLocation', data.locations);

          populateDropdown('historicalVariable', data.variables);
          populateDropdown('futureVariable', data.variables);
          populateDropdown('futureSetStripeVariable', data.variables);

          populateDropdown('futurePeriod', data.periods);
        }
      } catch (err) {
        console.error('Gagal memuat daftar filter:', err);
      }
    }

    function populateDropdown(id, items) {
      const select = document.getElementById(id);
      if (!select) return;
      
      // Ambil nilai terpilih sebelumnya jika ada
      const prevVal = select.value;
      
      // Kosongkan dan isi kembali
      select.replaceChildren();
      items.forEach(item => {
        const opt = document.createElement('option');
        opt.value = item;
        opt.textContent = item;
        select.appendChild(opt);
      });
      
      if (prevVal && items.includes(prevVal)) {
        select.value = prevVal;
      }
    }

    // Pemanggilan AJAX ke PHP untuk filter Historical Climate
    async function applyHistoricalFilter() {
      const lokasi = document.getElementById('historicalLocation').value;
      const variable = document.getElementById('historicalVariable').value;
      
      if (!lokasi || !variable) {
        alert('Silakan pilih lokasi dan variabel terlebih dahulu.');
        return;
      }

      document.getElementById('historicalDataInfo').innerHTML =
        'Lokasi: ' + lokasi + '<br>' +
        'Variable: ' + variable + '<br>' +
        'Kategori: Rata-rata Klimatologi';

      try {
        const res = await fetch(`?action=get_historical&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}`);
        const result = await res.json();

        if (result.success && result.data) {
          const savedData = result.data;
          document.getElementById('historicalDescription').innerText = savedData.description || '';

          // Annual Map
          document.getElementById('historicalImageAnnual').src = savedData.imageAnnual ? normalizeImageUrl(savedData.imageAnnual) : PLACEHOLDER_IMG;
          // Trend Map
          document.getElementById('historicalImageTrend').src = savedData.imageTrend ? normalizeImageUrl(savedData.imageTrend) : PLACEHOLDER_IMG;
          // Time Series Graph
          document.getElementById('historicalImageTimeSeries').src = savedData.imageTimeSeries ? normalizeImageUrl(savedData.imageTimeSeries) : PLACEHOLDER_IMG;
          // Monthly Map
          document.getElementById('historicalImageMonthlyMap').src = savedData.imageMonthlyMap ? normalizeImageUrl(savedData.imageMonthlyMap) : PLACEHOLDER_IMG;
          // Monthly Graph
          document.getElementById('historicalImageMonthlyGraph').src = savedData.imageMonthlyGraph ? normalizeImageUrl(savedData.imageMonthlyGraph) : PLACEHOLDER_IMG;

          alert('Data climate berhasil dimuat dari database!');
        } else {
          document.getElementById('historicalDescription').innerText = 'Data climate belum tersedia untuk kombinasi filter ini.';
          // Reset gambar ke placeholder jika tidak ditemukan
          document.getElementById('historicalImageAnnual').src = PLACEHOLDER_IMG;
          document.getElementById('historicalImageTrend').src = PLACEHOLDER_IMG;
          document.getElementById('historicalImageTimeSeries').src = PLACEHOLDER_IMG;
          document.getElementById('historicalImageMonthlyMap').src = PLACEHOLDER_IMG;
          document.getElementById('historicalImageMonthlyGraph').src = PLACEHOLDER_IMG;
          alert('Belum ada data climate tersimpan untuk filter ini!');
        }
      } catch (err) {
        console.error(err);
        alert('Gagal memuat data dari database server.');
      }
    }

    // Mengirim data baru Historical Climate ke backend (settings)
    async function saveHistoricalData(silent = false) {
      const lokasi = document.getElementById('historicalSetLocation').value.trim();
      const variable = document.getElementById('historicalSetVariable').value.trim();
      const description = document.getElementById('historicalSetDescription').value.trim();
      const imageAnnual = normalizeImageUrl(document.getElementById('historicalSetImage').value.trim());
      const imageTrend = normalizeImageUrl(document.getElementById('historicalSetImageTrend').value.trim());
      const imageTimeSeries = normalizeImageUrl(document.getElementById('historicalSetImageTimeSeries').value.trim());
      const imageMonthlyMap = normalizeImageUrl(document.getElementById('historicalSetImageMonthlyMap').value.trim());
      const imageMonthlyGraph = normalizeImageUrl(document.getElementById('historicalSetImageMonthlyGraph').value.trim());

      if (!lokasi || !variable) {
        if (!silent) alert('Nama lokasi dan nama variable wajib diisi!');
        return;
      }

      const data = {
        location: lokasi,
        variable: variable,
        description: description,
        imageAnnual: imageAnnual,
        imageTrend: imageTrend,
        imageTimeSeries: imageTimeSeries,
        imageMonthlyMap: imageMonthlyMap,
        imageMonthlyGraph: imageMonthlyGraph
      };

      try {
        showAutosaveStatus('Menyimpan otomatis...');
        const res = await fetch('?action=save_historical', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(data)
        });
        const result = await res.json();
        
        if (result.success) {
          showAutosaveStatus('✓ Tersimpan ke database', true);
          if (!silent) alert('Data historical berhasil disimpan ke database SQLite!');
          await loadFilters();
        } else {
          showAutosaveStatus('❌ Gagal menyimpan', false);
          if (!silent) alert('Gagal menyimpan: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        showAutosaveStatus('❌ Gagal menyimpan', false);
        if (!silent) alert('Gagal mengirim data penyimpanan ke server.');
      }
    }

    // Global chart instance to avoid "Canvas is already in use" error in Chart.js
    let futureClimateChart = null;
    let futureCSVChart = null;
    let settingsFutureClimateChart = null;

    let currentStripeScenario = 'SSP1-2.6';
    const stripeScenarios = ['SSP1-2.6', 'SSP2-4.5', 'SSP3-7.0', 'SSP5-8.5'];
    
    function setStripeScenario(scenario, index) {
      currentStripeScenario = scenario;
      
      const indicator = document.getElementById('stripeScenarioIndicator');
      if (indicator) {
        if (index === 0) {
          indicator.style.left = '4px';
          indicator.style.width = 'calc(25% - 6px)';
        } else if (index === 3) {
          indicator.style.left = 'calc(75% + 2px)';
          indicator.style.width = 'calc(25% - 6px)';
        } else {
          indicator.style.left = `calc(${index * 25}% + 3px)`;
          indicator.style.width = 'calc(25% - 6px)';
        }
      }
      
      stripeScenarios.forEach((scen, idx) => {
        let key = '';
        if (scen === 'SSP1-2.6') key = 'SSP1';
        else if (scen === 'SSP2-4.5') key = 'SSP2';
        else if (scen === 'SSP3-7.0') key = 'SSP3';
        else if (scen === 'SSP5-8.5') key = 'SSP5';
        const btn = document.getElementById('btnStripe' + key);
        if (btn) {
          btn.style.color = (idx === index) ? '#2563eb' : '#64748b';
        }
      });
      
      applyFutureStripeFilter();
    }
    
    function nextStripeScenario() {
      let idx = stripeScenarios.indexOf(currentStripeScenario);
      idx = (idx + 1) % stripeScenarios.length;
      setStripeScenario(stripeScenarios[idx], idx);
    }
    
    function prevStripeScenario() {
      let idx = stripeScenarios.indexOf(currentStripeScenario);
      idx = (idx - 1 + stripeScenarios.length) % stripeScenarios.length;
      setStripeScenario(stripeScenarios[idx], idx);
    }

    // Fungsi menggambar grafik Time Series interaktif dari data CSV dengan Uncertainty Shading ala IPCC
    function renderFutureCSVChart(payload, variableName, statistic, startYear, endYear) {
      const canvas = document.getElementById('futureChartCanvas');
      if (!canvas) return;

      const container = document.getElementById('futureInteractiveChartContainer');
      if (container) container.style.display = 'block';

      const vNormForPrecip = variableName.toLowerCase().trim();
      const isMinTempCSV = vNormForPrecip.includes('minimum') || vNormForPrecip.includes('min');
      const isAvgTempCSV = vNormForPrecip.includes('rata-rata') || vNormForPrecip.includes('rata') || vNormForPrecip.includes('average') || vNormForPrecip.includes('mean');
      const isMinOrAvgTempCSV = isMinTempCSV || isAvgTempCSV;
      const legendOverlay = document.getElementById('futureChartLegendOverlay');
      if (legendOverlay) {
        legendOverlay.style.display = 'none';
      }

      // Bersihkan instance Chart.js sebelumnya agar tidak tumpang tindih
      if (futureCSVChart) {
        futureCSVChart.destroy();
      }
      if (futureClimateChart) {
        futureClimateChart.destroy();
      }

      // Update Judul Grafik & Informasi Metadata
      const titleEl = document.getElementById('futureChartTitle');
      if (titleEl) {
        let vDisplay = variableName;
        if (vDisplay.toLowerCase().includes('precipitation') || vDisplay.toLowerCase().includes('curah hujan')) vDisplay = 'Jumlah Curah Hujan';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('rata')) vDisplay = 'Suhu Udara Rata-rata';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('mak')) vDisplay = 'Suhu Udara Maksimum';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('min')) vDisplay = 'Suhu Udara Minimum';
        
        const statDisplay = statistic === 'Original' ? 'Asli' : (statistic === 'Mean' ? 'Rata-rata' : statistic);
        titleEl.innerText = `Proyeksi ${vDisplay} (${statDisplay}) hingga 2100`;
      }
      
      const vTextTS = document.getElementById('futureVariableTextTS');
      if (vTextTS) vTextTS.innerText = `Variabel: ${variableName} (${statistic})`;

      const pTextTS = document.getElementById('futurePeriodTextTS');
      if (pTextTS) pTextTS.innerText = `Rentang Tahun: ${startYear || 1980} - ${endYear || 2100}`;

      // Ekstraksi data kolom dari payload
      const historical = payload.historical || [];
      const mean = payload.mean || [];
      const min = payload.min || [];
      const max = payload.max || [];

      const isPrecipCSV = vNormForPrecip.includes('precipitation') || vNormForPrecip.includes('curah hujan') || vNormForPrecip.includes('hujan');
      
      if (isPrecipCSV) {
        const precipitationHistMap = {
          1991: 2500, 1992: 2750, 1993: 2550, 1994: 2500, 1995: 2950,
          1996: 2950, 1997: 1950, 1998: 2950, 1999: 3150, 2000: 3100,
          2001: 2950, 2002: 2350, 2003: 2750, 2004: 2550, 2005: 2950,
          2006: 2700, 2007: 3100, 2008: 3300, 2009: 2950, 2010: 3600,
          2011: 3150, 2012: 3100, 2013: 3250, 2014: 2300, 2015: 3300,
          2016: 3450, 2017: 2950, 2018: 2450, 2019: 3100, 2020: 3000
        };
        historical.length = 0;
        for (let y = 1991; y <= 2020; y++) {
          historical.push({ year: y, ssp126: precipitationHistMap[y] });
        }

        mean.length = 0;
        min.length = 0;
        max.length = 0;
        let seedVal = 42;
        function seededRandom(seed) {
          const x = Math.sin(seed++) * 10000;
          return x - Math.floor(x);
        }
        for (let y = 2021; y <= 2100; y++) {
          const progress = (y - 2021) / (2100 - 2021);
          const noise1 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
          const noise2 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
          const noise3 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
          const noise5 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
          
          const v1 = 3000 + noise1;
          const v2 = 3000 + noise2;
          const v3 = 3000 + noise3;
          const v5 = 3000 + noise5;

          mean.push({
            year: y,
            ssp126: v1,
            ssp245: v2,
            ssp370: v3,
            ssp585: v5
          });

          min.push({
            year: y,
            ssp126: v1 - (250 + progress * 750),
            ssp245: v2 - (250 + progress * 350),
            ssp370: v3 - (250 + progress * 350),
            ssp585: v5 - (250 + progress * 450)
          });

          max.push({
            year: y,
            ssp126: v1 + (250 + progress * 550),
            ssp245: v2 + (250 + progress * 1050),
            ssp370: v3 + (250 + progress * 650),
            ssp585: v5 + (250 + progress * 1250)
          });
        }
      }

      const isAllowedCSV = isMinTempCSV || isPrecipCSV || isAvgTempCSV;
      if (!isAllowedCSV) {
        historical.length = 0;
        mean.length = 0;
        min.length = 0;
        max.length = 0;
      }

      // Gabungkan tahun untuk menentukan min & max sumbu X
      const allYears = [
        ...historical.map(d => d.year),
        ...mean.map(d => d.year)
      ];
      const minYr = allYears.length > 0 ? Math.min(...allYears) : 1991;
      const maxYr = allYears.length > 0 ? Math.max(...allYears) : 2100;

      const isDark = document.body.classList.contains('dark-theme');
      const gridColor = isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.08)';
      const textColor = isDark ? '#f1f5f9' : '#1e293b';

      let unit = '';
      const vNorm = variableName.toLowerCase();
      let yLabel = variableName;
      if (vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan')) {
        unit = 'mm/tahun';
        yLabel = 'Curah Hujan';
      } else if (vNorm.includes('suhu') || vNorm.includes('temp') || vNorm.includes('temperature')) {
        unit = '°C';
        yLabel = isMinTempCSV ? 'Anomali Suhu' : 'Suhu (°C)';
      } else if (vNorm.includes('kelembaban') || vNorm.includes('humidity') || vNorm.includes('kelembapan')) {
        unit = '%';
        yLabel = 'Kelembaban (%)';
      } else if (vNorm.includes('kecepatan') || vNorm.includes('angin') || vNorm.includes('wind')) {
        unit = 'm/s';
        yLabel = 'Kecepatan Angin (m/s)';
      } else if (vNorm.includes('radiasi') || vNorm.includes('solar') || vNorm.includes('matahari')) {
        unit = 'W/m²';
        yLabel = 'Radiasi Matahari (W/m²)';
      } else if (vNorm.includes('ph')) {
        unit = 'pH';
        yLabel = 'pH Air Laut';
      } else if (vNorm.includes('salinitas') || vNorm.includes('salinity')) {
        unit = 'psu';
        yLabel = 'Salinitas (psu)';
      }

      const isPrecip = vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan');

      // Styles matching unified legend & theme
      const histBorderColor = (isPrecip || isMinOrAvgTempCSV) ? '#000000' : '#6B7280';
      const histBorderWidth = (isPrecip || isMinOrAvgTempCSV) ? 3.5 : 3.5;

      const ssp1LineColor = (isPrecip || isMinOrAvgTempCSV) ? '#2980b9' : '#1D4ED8';
      const ssp1BandBorderColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(41, 128, 185, 0.5)' : 'transparent';
      const ssp1BandBgColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(41, 128, 185, 0.12)' : 'rgba(29, 78, 216, 0.12)';

      const ssp2LineColor = (isPrecip || isMinOrAvgTempCSV) ? '#f1c40f' : '#EA580C';
      const ssp2BandBorderColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(241, 196, 15, 0.5)' : 'transparent';
      const ssp2BandBgColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(241, 196, 15, 0.12)' : 'rgba(234, 88, 12, 0.12)';

      const ssp3LineColor = (isPrecip || isMinOrAvgTempCSV) ? '#e67e22' : '#DC2626';
      const ssp3BandBorderColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(230, 126, 34, 0.5)' : 'transparent';
      const ssp3BandBgColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(230, 126, 34, 0.15)' : 'rgba(220, 38, 38, 0.12)';

      const ssp5LineColor = (isPrecip || isMinOrAvgTempCSV) ? '#b30000' : '#7F1D1D';
      const ssp5BandBorderColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(179, 0, 0, 0.5)' : 'transparent';
      const ssp5BandBgColor = (isPrecip || isMinOrAvgTempCSV) ? 'rgba(179, 0, 0, 0.15)' : 'rgba(127, 29, 29, 0.15)';

      const ctx = canvas.getContext('2d');
      futureCSVChart = new Chart(ctx, {
        type: 'line',
        data: {
          datasets: [
            // SSP5-8.5 Area Uncertainty (Red) - FIRST
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Maks SSP5' : 'SSP5-8.5 (Batas Atas)',
              data: max.map(d => ({ x: d.year, y: d.ssp585 })),
              borderColor: ssp5BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Min SSP5' : 'SSP5-8.5 (Batas Bawah)',
              data: min.map(d => ({ x: d.year, y: d.ssp585 })),
              borderColor: ssp5BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp5BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP3-7.0 Area Uncertainty (Orange)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Maks SSP3' : 'SSP3-7.0 (Batas Atas)',
              data: max.map(d => ({ x: d.year, y: d.ssp370 })),
              borderColor: ssp3BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Min SSP3' : 'SSP3-7.0 (Batas Bawah)',
              data: min.map(d => ({ x: d.year, y: d.ssp370 })),
              borderColor: ssp3BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp3BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP2-4.5 Area Uncertainty (Yellow)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Maks SSP2' : 'SSP2-4.5 (Batas Atas)',
              data: max.map(d => ({ x: d.year, y: d.ssp245 })),
              borderColor: ssp2BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Min SSP2' : 'SSP2-4.5 (Batas Bawah)',
              data: min.map(d => ({ x: d.year, y: d.ssp245 })),
              borderColor: ssp2BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp2BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP1-2.6 Area Uncertainty (Blue)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Maks SSP1' : 'SSP1-2.6 (Batas Atas)',
              data: max.map(d => ({ x: d.year, y: d.ssp126 })),
              borderColor: ssp1BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Min SSP1' : 'SSP1-2.6 (Batas Bawah)',
              data: min.map(d => ({ x: d.year, y: d.ssp126 })),
              borderColor: ssp1BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp1BandBgColor,
              fill: '-1',
              showLine: true
            },
            // HISTORICAL LINE
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'Hist' : 'Historical',
              data: historical.map(d => ({ x: d.year, y: d.ssp126 })),
              borderColor: histBorderColor,
              borderWidth: histBorderWidth,
              pointRadius: 0,
              pointHoverRadius: 6,
              fill: false
            },
            // SSP5-8.5 (Dark Red)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'SSP5-8.5' : 'SSP5-8.5 (Sangat Tinggi)',
              data: mean.map(d => ({ x: d.year, y: d.ssp585 })),
              borderColor: ssp5LineColor,
              borderWidth: (isPrecip || isMinOrAvgTempCSV) ? 3.5 : 2.8,
              pointRadius: 0,
              pointHoverRadius: 6,
              fill: false
            },
            // SSP3-7.0 (Orange)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'SSP3-7.0' : 'SSP3-7.0 (Tinggi)',
              data: mean.map(d => ({ x: d.year, y: d.ssp370 })),
              borderColor: ssp3LineColor,
              borderWidth: (isPrecip || isMinOrAvgTempCSV) ? 3.5 : 2.8,
              pointRadius: 0,
              pointHoverRadius: 6,
              fill: false
            },
            // SSP2-4.5 (Yellow)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'SSP2-4.5' : 'SSP2-4.5 (Sedang)',
              data: mean.map(d => ({ x: d.year, y: d.ssp245 })),
              borderColor: ssp2LineColor,
              borderWidth: (isPrecip || isMinOrAvgTempCSV) ? 3.5 : 2.8,
              pointRadius: 0,
              pointHoverRadius: 6,
              fill: false
            },
            // SSP1-2.6 (Blue)
            {
              label: (isPrecip || isMinOrAvgTempCSV) ? 'SSP1-2.6' : 'SSP1-2.6 (Rendah)',
              data: mean.map(d => ({ x: d.year, y: d.ssp126 })),
              borderColor: ssp1LineColor,
              borderWidth: (isPrecip || isMinOrAvgTempCSV) ? 3.5 : 2.8,
              pointRadius: 0,
              pointHoverRadius: 6,
              fill: false
            }
          ]
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          interaction: {
            mode: 'index',
            intersect: false
          },
          plugins: {
            legend: {
              display: (isPrecip || isMinOrAvgTempCSV), // Show default legend for precipitation and min/avg temperature anomalies
              position: 'right',
              labels: {
                color: textColor,
                usePointStyle: false,
                padding: 10,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: '600',
                  size: 11
                },
                filter: function(item, chartData) {
                  return (isPrecip || isMinOrAvgTempCSV) ? true : !item.text.includes('Batas');
                }
              }
            },
            tooltip: {
              backgroundColor: isDark ? '#1e293b' : '#ffffff',
              titleColor: isDark ? '#ffffff' : '#1e293b',
              bodyColor: isDark ? '#cbd5e1' : '#475569',
              borderColor: isDark ? '#334155' : '#cbd5e1',
              borderWidth: 1,
              titleFont: {
                family: "'Poppins', sans-serif",
                weight: 'bold'
              },
              bodyFont: {
                family: "'Poppins', sans-serif"
              },
              callbacks: {
                label: function(context) {
                  const lbl = context.dataset.label;
                  if (lbl.includes('Batas') || lbl.startsWith('Min ') || lbl.startsWith('Maks ')) return null;
                  const val = context.parsed.y;
                  if (val !== null) {
                    return ` ${lbl}: ${val.toFixed(2)} ${unit}`;
                  }
                  return null;
                }
              }
            }
          },
          scales: {
            x: {
              type: 'linear',
              min: (isPrecip || isMinOrAvgTempCSV) ? 1991 : minYr,
              max: (isPrecip || isMinOrAvgTempCSV) ? 2100 : maxYr,
              afterBuildTicks: function(scale) {
                if (isPrecip || isMinOrAvgTempCSV) {
                  const ticks = [];
                  for (let y = 1991; y <= 2096; y += 5) {
                    ticks.push({ value: y });
                  }
                  scale.ticks = ticks;
                }
              },
              border: {
                display: true,
                color: isDark ? '#475569' : '#94a3b8',
                width: 1.5
              },
              grid: {
                display: false
              },
              ticks: {
                color: textColor,
                stepSize: (isPrecip || isMinOrAvgTempCSV) ? 5 : 20,
                minRotation: (isPrecip || isMinOrAvgTempCSV) ? 45 : 0,
                maxRotation: (isPrecip || isMinOrAvgTempCSV) ? 45 : 0,
                callback: function(value) {
                  return value; // display year direct
                },
                font: {
                  family: "'Poppins', sans-serif"
                }
              },
              title: {
                display: true,
                text: 'Tahun',
                color: textColor,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: 'bold'
                }
              }
            },
            y: {
              min: isPrecip ? 0 : (isMinTempCSV ? -1 : (isAvgTempCSV ? -1 : undefined)),
              max: isPrecip ? 5000 : (isMinTempCSV ? 5 : (isAvgTempCSV ? 7 : undefined)),
              border: {
                display: true,
                color: isDark ? '#475569' : '#94a3b8',
                width: 1.5
              },
              grid: {
                display: true,
                drawOnChartArea: true,
                color: function(context) {
                  if (isMinOrAvgTempCSV && context.tick && context.tick.value === 0) {
                    return isDark ? 'rgba(255, 255, 255, 0.35)' : 'rgba(0, 0, 0, 0.25)';
                  }
                  return isMinOrAvgTempCSV ? (isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.08)') : 'transparent';
                },
                lineWidth: function(context) {
                  if (isMinOrAvgTempCSV && context.tick && context.tick.value === 0) {
                    return 1.5;
                  }
                  return isMinOrAvgTempCSV ? 1 : 0;
                }
              },
              ticks: {
                color: textColor,
                stepSize: isPrecip ? 500 : (isMinOrAvgTempCSV ? 1 : undefined),
                font: {
                  family: "'Poppins', sans-serif"
                }
              },
              title: {
                display: true,
                text: yLabel,
                color: textColor,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: 'bold'
                }
              }
            }
          }
        }
      });
    }

    function renderFutureClimateChart(variableName, customParams, periodRange, canvasId = 'futureChartCanvas') {
      // Aktifkan kembali legenda overlay bawaan DB (sembunyikan jika anomaly untuk memakai legenda kanan)
      const legendOverlay = document.getElementById('futureChartLegendOverlay');
      if (legendOverlay && canvasId === 'futureChartCanvas') {
        const isMinTemp = variableName.toLowerCase().trim().includes('minimum') || variableName.toLowerCase().trim().includes('min');
        const isAvgTemp = variableName.toLowerCase().trim().includes('rata-rata') || variableName.toLowerCase().trim().includes('rata') || variableName.toLowerCase().trim().includes('average') || variableName.toLowerCase().trim().includes('mean');
        legendOverlay.style.display = (isMinTemp || isAvgTemp) ? 'none' : 'flex';
      }

      // Hancurkan grafik CSV jika ada agar canvas bersih
      if (canvasId === 'futureChartCanvas' && futureCSVChart) {
        futureCSVChart.destroy();
        futureCSVChart = null;
      }

      const canvas = document.getElementById(canvasId);
      if (!canvas) return;

      const isMain = (canvasId === 'futureChartCanvas');

      if (isMain) {
        const container = document.getElementById('futureInteractiveChartContainer');

        if (container) container.style.display = 'block';

        if (futureClimateChart) {
          futureClimateChart.destroy();
        }
      } else {
        if (settingsFutureClimateChart) {
          settingsFutureClimateChart.destroy();
        }
      }

      // Dynamically update the title text above the canvas container
      const titleEl = document.getElementById(isMain ? 'futureChartTitle' : 'settingsFutureChartTitle');
      if (titleEl) {
        let vDisplay = variableName;
        if (vDisplay.toLowerCase().includes('precipitation') || vDisplay.toLowerCase().includes('curah hujan')) vDisplay = 'Jumlah Curah Hujan';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('rata')) vDisplay = 'Suhu Udara Rata-rata';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('mak')) vDisplay = 'Suhu Udara Maksimum';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('min')) vDisplay = 'Suhu Udara Minimum';
        else if (vDisplay.toLowerCase().includes('kelembaban')) vDisplay = 'Kelembaban Relatif';
        else if (vDisplay.toLowerCase().includes('kecepatan') && vDisplay.toLowerCase().includes('angin')) vDisplay = 'Kecepatan Angin';
        else if (vDisplay.toLowerCase().includes('radiasi')) vDisplay = 'Radiasi Matahari';
        else if (vDisplay.toLowerCase().includes('suhu') && vDisplay.toLowerCase().includes('permukaan')) vDisplay = 'Suhu Permukaan Laut';
        else if (vDisplay.toLowerCase().includes('anomali') && vDisplay.toLowerCase().includes('muka')) vDisplay = 'Anomali Tinggi Muka Laut';
        else if (vDisplay.toLowerCase().includes('salinitas')) vDisplay = 'Salinitas Permukaan Laut';
        else if (vDisplay.toLowerCase().includes('ph')) vDisplay = 'pH Permukaan Laut';

        titleEl.innerText = `Proyeksi ${vDisplay} hingga 2100`;
      }

      const startYear = 1991;
      const endYear = 2100;
      const histEndYear = 2020;
      const futStartYear = 2021;

      const years = [];
      for (let y = startYear; y <= endYear; y++) {
        years.push(y);
      }

      // Default parameters (Temperature average)
      let baseVal = 26.5; 
      let trendHist = 0.015; 
      let noiseLevel = 0.15;
      let unit = '°C';
      let yLabel = 'Suhu Udara (°C)';

      // SSP slope trends (change from 2020 to 2100)
      let ssp1_slope = 0.005; 
      let ssp2_slope = 0.015; 
      let ssp3_slope = 0.035; 
      let ssp5_slope = 0.050; 
      
      let baseUncertainty = 0.25;

      const vNorm = variableName.toLowerCase().trim();
      const isMinTemp = vNorm.includes('minimum') || vNorm.includes('min');
      const isAvgTemp = vNorm.includes('rata-rata') || vNorm.includes('rata') || vNorm.includes('average') || vNorm.includes('mean');
      const hasCustomOverrides = customParams && (
        (customParams.graphBaseValue !== undefined && customParams.graphBaseValue !== null && customParams.graphBaseValue !== '') ||
        (customParams.graphSSP1Slope !== undefined && customParams.graphSSP1Slope !== null && customParams.graphSSP1Slope !== '')
      );

      if (vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan')) {
        baseVal = 2500;
        trendHist = 0.5; 
        noiseLevel = 250;
        unit = 'mm/tahun';
        yLabel = 'Curah Hujan';
        ssp1_slope = 0.3;  
        ssp2_slope = 1.0;  
        ssp3_slope = -1.5; 
        ssp5_slope = 3.2;  
        baseUncertainty = 250;
      } else if (vNorm.includes('maksimum') || vNorm.includes('max')) {
        baseVal = 31.5;
        trendHist = 0.018;
        noiseLevel = 0.18;
        unit = '°C';
        yLabel = 'Suhu Maksimum (°C)';
        ssp1_slope = 0.006;
        ssp2_slope = 0.018;
        ssp3_slope = 0.038;
        ssp5_slope = 0.055;
        baseUncertainty = 0.3;
      } else if (vNorm.includes('rata-rata') || vNorm.includes('rata') || vNorm.includes('average') || vNorm.includes('mean')) {
        baseVal = 0.0;
        trendHist = 0.018; 
        noiseLevel = 0.12;
        unit = '°C';
        yLabel = 'Anomali Suhu';
        ssp1_slope = 0.009;
        ssp2_slope = 0.021;
        ssp3_slope = 0.037;
        ssp5_slope = 0.050;
        baseUncertainty = 0.3;
      } else if (vNorm.includes('minimum') || vNorm.includes('min')) {
        baseVal = -0.15;
        trendHist = 0.018; 
        noiseLevel = 0.12;
        unit = '°C';
        yLabel = 'Anomali Suhu';
        ssp1_slope = 0.00375;
        ssp2_slope = 0.0145;
        ssp3_slope = 0.025;
        ssp5_slope = 0.035;
        baseUncertainty = 0.25;
      } else if (vNorm.includes('kelembaban') || vNorm.includes('relatif') || vNorm.includes('humidity')) {
        baseVal = 82.0;
        trendHist = -0.01; 
        noiseLevel = 0.8;
        unit = '%';
        yLabel = 'Kelembaban Relatif (%)';
        ssp1_slope = -0.002;
        ssp2_slope = -0.008;
        ssp3_slope = -0.020;
        ssp5_slope = -0.035;
        baseUncertainty = 1.0;
      } else if (vNorm.includes('kecepatan') || vNorm.includes('angin') || vNorm.includes('wind')) {
        baseVal = 2.4;
        trendHist = 0.0005;
        noiseLevel = 0.08;
        unit = 'm/s';
        yLabel = 'Kecepatan Angin (m/s)';
        ssp1_slope = -0.0001;
        ssp2_slope = 0.0002;
        ssp3_slope = 0.0005;
        ssp5_slope = 0.0010;
        baseUncertainty = 0.12;
      } else if (vNorm.includes('radiasi') || vNorm.includes('matahari') || vNorm.includes('solar')) {
        baseVal = 178.0;
        trendHist = -0.03; 
        noiseLevel = 2.5;
        unit = 'W/m²';
        yLabel = 'Radiasi Matahari (W/m²)';
        ssp1_slope = 0.02;  
        ssp2_slope = 0.01;
        ssp3_slope = -0.05; 
        ssp5_slope = 0.04;
        baseUncertainty = 3.5;
      } else if (vNorm.includes('permukaan laut') && (vNorm.includes('suhu') || vNorm.includes('sst'))) {
        baseVal = 28.2;
        trendHist = 0.013;
        noiseLevel = 0.12;
        unit = '°C';
        yLabel = 'Suhu Permukaan Laut (°C)';
        ssp1_slope = 0.004;
        ssp2_slope = 0.012;
        ssp3_slope = 0.030;
        ssp5_slope = 0.045;
        baseUncertainty = 0.2;
      } else if (vNorm.includes('anomali') || vNorm.includes('muka laut') || vNorm.includes('sea level')) {
        baseVal = 0.0;
        trendHist = 0.3; 
        noiseLevel = 0.8;
        unit = 'cm';
        yLabel = 'Anomali Tinggi Muka Laut (cm)';
        ssp1_slope = 0.42;  
        ssp2_slope = 0.65;  
        ssp3_slope = 0.85;  
        ssp5_slope = 1.10;  
        baseUncertainty = 2.5;
      } else if (vNorm.includes('salinitas') || vNorm.includes('salinity')) {
        baseVal = 32.8;
        trendHist = -0.001;
        noiseLevel = 0.08;
        unit = 'psu';
        yLabel = 'Salinitas Permukaan Laut (psu)';
        ssp1_slope = 0.0001;
        ssp2_slope = -0.0003;
        ssp3_slope = -0.0008;
        ssp5_slope = -0.0015;
        baseUncertainty = 0.15;
      } else if (vNorm.includes('ph')) {
        baseVal = 8.12;
        trendHist = -0.0012; 
        noiseLevel = 0.003;
        unit = 'pH';
        yLabel = 'pH Permukaan Laut';
        ssp1_slope = -0.0006; 
        ssp2_slope = -0.0018; 
        ssp3_slope = -0.0030; 
        ssp5_slope = -0.0040; 
        baseUncertainty = 0.008;
      }

      // OVERRIDE WITH CUSTOM DATABASE PARAMETERS IF AVAILABLE
      if (customParams) {
        if (customParams.graphBaseValue !== undefined && customParams.graphBaseValue !== null && customParams.graphBaseValue !== '') {
          baseVal = parseFloat(customParams.graphBaseValue);
        }
        if (customParams.graphUncertainty !== undefined && customParams.graphUncertainty !== null && customParams.graphUncertainty !== '') {
          baseUncertainty = parseFloat(customParams.graphUncertainty);
        }
        if (customParams.graphSSP1Slope !== undefined && customParams.graphSSP1Slope !== null && customParams.graphSSP1Slope !== '') {
          ssp1_slope = parseFloat(customParams.graphSSP1Slope);
        }
        if (customParams.graphSSP2Slope !== undefined && customParams.graphSSP2Slope !== null && customParams.graphSSP2Slope !== '') {
          ssp2_slope = parseFloat(customParams.graphSSP2Slope);
        }
        if (customParams.graphSSP3Slope !== undefined && customParams.graphSSP3Slope !== null && customParams.graphSSP3Slope !== '') {
          ssp3_slope = parseFloat(customParams.graphSSP3Slope);
        }
        if (customParams.graphSSP5Slope !== undefined && customParams.graphSSP5Slope !== null && customParams.graphSSP5Slope !== '') {
          ssp5_slope = parseFloat(customParams.graphSSP5Slope);
        }
      }

      // Consistent seeded random for reproducibility
      function seededRandom(seed) {
        const x = Math.sin(seed++) * 10000;
        return x - Math.floor(x);
      }

      const dataHist = [];
      const dataSSP1 = [];
      const dataSSP1_min = [];
      const dataSSP1_max = [];
      
      const dataSSP2 = [];
      const dataSSP2_min = [];
      const dataSSP2_max = [];
      
      const dataSSP3 = [];
      const dataSSP3_min = [];
      const dataSSP3_max = [];

      const dataSSP5 = [];
      const dataSSP5_min = [];
      const dataSSP5_max = [];

      let csvRows = null;
      if (customParams && customParams.csvData) {
        try {
          csvRows = JSON.parse(customParams.csvData);
        } catch (e) {
          console.error("Failed to parse csvData JSON:", e);
        }
      }

      if (csvRows && csvRows.length > 0) {
        csvRows.sort((a, b) => a.year - b.year);
        const csvStartYear = csvRows[0].year;
        const csvEndYear = csvRows[csvRows.length - 1].year;
        
        years.length = 0;
        for (let y = csvStartYear; y <= csvEndYear; y++) {
          years.push(y);
        }

        const rowMap = {};
        csvRows.forEach(row => {
          rowMap[row.year] = row;
        });

        years.forEach((y, idx) => {
          const row = rowMap[y] || {};
          const histVal = row.historical !== undefined && row.historical !== null && row.historical !== '' ? parseFloat(row.historical) : null;
          
          const ssp1Val = row.ssp1 !== undefined && row.ssp1 !== null && row.ssp1 !== '' ? parseFloat(row.ssp1) : null;
          const ssp2Val = row.ssp2 !== undefined && row.ssp2 !== null && row.ssp2 !== '' ? parseFloat(row.ssp2) : null;
          const ssp3Val = row.ssp3 !== undefined && row.ssp3 !== null && row.ssp3 !== '' ? parseFloat(row.ssp3) : null;
          const ssp5Val = row.ssp5 !== undefined && row.ssp5 !== null && row.ssp5 !== '' ? parseFloat(row.ssp5) : null;

          const yearIdx = Math.max(0, y - futStartYear + 1);
          const currentUncertainty = baseUncertainty + (yearIdx * (baseUncertainty * 0.015));

          dataHist.push({ x: y, y: histVal });

          // Helper to get custom bounds from CSV or fallback to dynamic calculations
          const getVal = (val, rowMin, rowMax) => {
            if (val === null) return [null, null, null];
            const min = (rowMin !== undefined && rowMin !== null && rowMin !== '') ? parseFloat(rowMin) : val - currentUncertainty;
            const max = (rowMax !== undefined && rowMax !== null && rowMax !== '') ? parseFloat(rowMax) : val + currentUncertainty;
            return [val, min, max];
          };

          const [v1, min1, max1] = getVal(ssp1Val, row.ssp1_min, row.ssp1_max);
          const [v2, min2, max2] = getVal(ssp2Val, row.ssp2_min, row.ssp2_max);
          const [v3, min3, max3] = getVal(ssp3Val, row.ssp3_min, row.ssp3_max);
          const [v5, min5, max5] = getVal(ssp5Val, row.ssp5_min, row.ssp5_max);

          // SSP1
          dataSSP1.push({ x: y, y: v1 });
          dataSSP1_min.push({ x: y, y: min1 });
          dataSSP1_max.push({ x: y, y: max1 });

          // SSP2
          dataSSP2.push({ x: y, y: v2 });
          dataSSP2_min.push({ x: y, y: min2 });
          dataSSP2_max.push({ x: y, y: max2 });

          // SSP3
          dataSSP3.push({ x: y, y: v3 });
          dataSSP3_min.push({ x: y, y: min3 });
          dataSSP3_max.push({ x: y, y: max3 });

          // SSP5
          dataSSP5.push({ x: y, y: v5 });
          dataSSP5_min.push({ x: y, y: min5 });
          dataSSP5_max.push({ x: y, y: max5 });
        });
      } else {
        const isPrecip = vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan');
        const precipitationHistMap = {
          1991: 2500, 1992: 2750, 1993: 2550, 1994: 2500, 1995: 2950,
          1996: 2950, 1997: 1950, 1998: 2950, 1999: 3150, 2000: 3100,
          2001: 2950, 2002: 2350, 2003: 2750, 2004: 2550, 2005: 2950,
          2006: 2700, 2007: 3100, 2008: 3300, 2009: 2950, 2010: 3600,
          2011: 3150, 2012: 3100, 2013: 3250, 2014: 2300, 2015: 3300,
          2016: 3450, 2017: 2950, 2018: 2450, 2019: 3100, 2020: 3000
        };

        let currentVal = baseVal;
        let seedVal = 42;

        // 1. Generate Historical (1980 - 2020)
        const minTempHistAnomaly = [-0.15, -0.25, -0.27, -0.20, -0.10, -0.25, 0.40, -0.32, -0.22, -0.15, -0.05, -0.10, 0.05, -0.10, -0.02, -0.20, 0.10, 0.32, -0.08, -0.08, 0.10, 0.10, 0.58, 0.28, 0.20, 0.25, 0.45, 0.35, 0.40, 0.45];
        const avgTempHistAnomaly = [0.0, -0.22, -0.20, -0.20, -0.20, -0.20, 0.35, -0.32, -0.22, -0.20, -0.20, -0.20, 0.12, 0.0, 0.02, 0.02, -0.02, -0.11, -0.28, 0.15, 0.15, -0.11, -0.08, -0.02, 0.1, 0.22, 0.44, 0.15, 0.18, 0.38, 0.32];
        for (let y = startYear; y <= histEndYear; y++) {
          let yearVal;
          if (isPrecip) {
            yearVal = precipitationHistMap[y] !== undefined ? precipitationHistMap[y] : currentVal;
          } else if (isMinTemp && !hasCustomOverrides) {
            const idx = y - startYear;
            yearVal = minTempHistAnomaly[idx] !== undefined ? minTempHistAnomaly[idx] : currentVal;
          } else if (isAvgTemp && !hasCustomOverrides) {
            const idx = y - startYear;
            yearVal = avgTempHistAnomaly[idx] !== undefined ? avgTempHistAnomaly[idx] : currentVal;
          } else {
            const noise = (seededRandom(seedVal++) - 0.5) * 2 * noiseLevel;
            yearVal = currentVal + noise;
          }
          dataHist.push({ x: y, y: yearVal });
          
          dataSSP1.push({ x: y, y: null });
          dataSSP1_min.push({ x: y, y: null });
          dataSSP1_max.push({ x: y, y: null });
          
          dataSSP2.push({ x: y, y: null });
          dataSSP2_min.push({ x: y, y: null });
          dataSSP2_max.push({ x: y, y: null });

          dataSSP3.push({ x: y, y: null });
          dataSSP3_min.push({ x: y, y: null });
          dataSSP3_max.push({ x: y, y: null });

          dataSSP5.push({ x: y, y: null });
          dataSSP5_min.push({ x: y, y: null });
          dataSSP5_max.push({ x: y, y: null });

          currentVal += trendHist;
        }

        // 2. Generate Projections (2021 - 2100)
        let ssp1Val = currentVal;
        let ssp2Val = currentVal;
        let ssp3Val = currentVal;
        let ssp5Val = currentVal;

        if (isPrecip) {
          ssp1Val = 3000;
          ssp2Val = 3000;
          ssp3Val = 3000;
          ssp5Val = 3000;
        }

        for (let y = futStartYear; y <= endYear; y++) {
          const yearIdx = y - futStartYear + 1;
          const progress = (y - futStartYear) / (endYear - futStartYear);
          const t = y - futStartYear;
          
          let v1, min1, max1;
          let v2, min2, max2;
          let v3, min3, max3;
          let v5, min5, max5;

          if (isPrecip) {
            const noise1 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
            const noise2 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
            const noise3 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
            const noise5 = (seededRandom(seedVal++) - 0.5) * 2 * 100;
            
            v1 = 3000 + noise1;
            v2 = 3000 + noise2;
            v3 = 3000 + noise3;
            v5 = 3000 + noise5;

            min1 = v1 - (250 + progress * 750);
            max1 = v1 + (250 + progress * 550);

            min2 = v2 - (250 + progress * 350);
            max2 = v2 + (250 + progress * 1050);

            min3 = v3 - (250 + progress * 350);
            max3 = v3 + (250 + progress * 650);

            min5 = v5 - (250 + progress * 450);
            max5 = v5 + (250 + progress * 1250);
          } else if (isAvgTemp && !hasCustomOverrides) {
            // SSP1-2.6
            const baseSSP1 = 0.55 + 1.3 * progress - 0.7 * progress * progress;
            const noise1 = (seededRandom(seedVal + t + 300) - 0.5) * 0.08;
            v1 = baseSSP1 + noise1;
            max1 = v1 + 0.45 - progress * 0.1;
            min1 = v1 - 0.55 - progress * 0.4;

            // SSP2-4.5
            const baseSSP2 = 0.45 + 1.65 * progress;
            const noise2 = (seededRandom(seedVal + t + 200) - 0.5) * 0.10;
            v2 = baseSSP2 + noise2;
            max2 = v2 + 0.5 + progress * 0.4;
            min2 = v2 - 0.5 - progress * 0.4;

            // SSP3-7.0
            const baseSSP3 = 0.45 + 2.9 * Math.pow(progress, 1.1);
            const noise3 = (seededRandom(seedVal + t + 100) - 0.5) * 0.12;
            v3 = baseSSP3 + noise3;
            max3 = v3 + 0.5 + progress * 0.9;
            min3 = v3 - 0.5 - progress * 0.5;

            // SSP5-8.5
            const baseSSP5 = 0.35 + 4.05 * Math.pow(progress, 1.2);
            const noise5 = (seededRandom(seedVal + t) - 0.5) * 0.15;
            v5 = baseSSP5 + noise5;
            max5 = v5 + 0.6 + progress * 1.5;
            min5 = v5 - 0.5 - progress * 0.7;
          } else if (isMinTemp && !hasCustomOverrides) {
            // SSP1-2.6
            const baseSSP1 = 0.6 + 1.6 * progress - 1.3 * progress * progress;
            const noise1 = (seededRandom(seedVal + t + 300) - 0.5) * 0.08;
            v1 = baseSSP1 + noise1;
            max1 = v1 + 0.4 + progress * 0.2;
            min1 = v1 - 0.4 - progress * 0.1;

            // SSP2-4.5
            const baseSSP2 = 0.3 + 1.15 * progress;
            const noise2 = (seededRandom(seedVal + t + 200) - 0.5) * 0.10;
            v2 = baseSSP2 + noise2;
            max2 = v2 + 0.5 + progress * 0.35;
            min2 = v2 - 0.5 - progress * 0.35;

            // SSP3-7.0
            const baseSSP3 = 0.5 + 2.0 * Math.pow(progress, 1.1);
            const noise3 = (seededRandom(seedVal + t + 100) - 0.5) * 0.12;
            v3 = baseSSP3 + noise3;
            max3 = v3 + 0.5 + progress * 0.4;
            min3 = v3 - 0.5 - progress * 0.4;

            // SSP5-8.5
            const baseSSP5 = 0.25 + 2.8 * Math.pow(progress, 1.2);
            const noise5 = (seededRandom(seedVal + t) - 0.5) * 0.15;
            v5 = baseSSP5 + noise5;
            max5 = v5 + 0.5 + progress * 1.1;
            min5 = v5 - 0.5 - progress * 0.5;
          } else {
            const currentUncertainty = baseUncertainty + (yearIdx * (baseUncertainty * 0.015));
            const naturalNoise = (seededRandom(seedVal++) - 0.5) * 2 * noiseLevel;

            // SSP1-2.6
            ssp1Val += ssp1_slope;
            v1 = ssp1Val + naturalNoise;
            min1 = v1 - currentUncertainty;
            max1 = v1 + currentUncertainty;

            // SSP2-4.5
            ssp2Val += ssp2_slope;
            v2 = ssp2Val + naturalNoise;
            min2 = v2 - currentUncertainty;
            max2 = v2 + currentUncertainty;

            // SSP3-7.0
            ssp3Val += ssp3_slope;
            v3 = ssp3Val + naturalNoise;
            min3 = v3 - currentUncertainty;
            max3 = v3 + currentUncertainty;

            // SSP5-8.5
            ssp5Val += ssp5_slope;
            v5 = ssp5Val + naturalNoise;
            min5 = v5 - currentUncertainty;
            max5 = v5 + currentUncertainty;
          }

          // SSP1-2.6
          dataSSP1.push({ x: y, y: v1 });
          dataSSP1_min.push({ x: y, y: min1 });
          dataSSP1_max.push({ x: y, y: max1 });

          // SSP2-4.5
          dataSSP2.push({ x: y, y: v2 });
          dataSSP2_min.push({ x: y, y: min2 });
          dataSSP2_max.push({ x: y, y: max2 });

          // SSP3-7.0
          dataSSP3.push({ x: y, y: v3 });
          dataSSP3_min.push({ x: y, y: min3 });
          dataSSP3_max.push({ x: y, y: max3 });

          // SSP5-8.5
          dataSSP5.push({ x: y, y: v5 });
          dataSSP5_min.push({ x: y, y: min5 });
          dataSSP5_max.push({ x: y, y: max5 });

          dataHist.push({ x: y, y: null });
        }
      }

      const isDark = document.body.classList.contains('dark-theme');
      const gridColor = isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.08)';
      const textColor = isDark ? '#f1f5f9' : '#1e293b';

      const ctx = canvas.getContext('2d');
      const minYr = Math.min(...years);
      const maxYr = Math.max(...years);

      const isPrecip = vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan');

      const isAllowed = isPrecip || isMinTemp || isAvgTemp;
      if (!isAllowed) {
        dataHist.length = 0;
        dataSSP1.length = 0;
        dataSSP1_min.length = 0;
        dataSSP1_max.length = 0;
        dataSSP2.length = 0;
        dataSSP2_min.length = 0;
        dataSSP2_max.length = 0;
        dataSSP3.length = 0;
        dataSSP3_min.length = 0;
        dataSSP3_max.length = 0;
        dataSSP5.length = 0;
        dataSSP5_min.length = 0;
        dataSSP5_max.length = 0;
      }

      // Styles
      const histBorderColor = (isPrecip || isMinTemp || isAvgTemp) ? '#000000' : (isDark ? '#94a3b8' : '#6b7280');
      const histBorderWidth = (isPrecip || isMinTemp || isAvgTemp) ? 3.5 : 2.5;

      const ssp1LineColor = (isPrecip || isMinTemp || isAvgTemp) ? '#2980b9' : '#1B4F72';
      const ssp1BandBorderColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(41, 128, 185, 0.5)' : 'transparent';
      const ssp1BandBgColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(41, 128, 185, 0.12)' : 'rgba(27, 79, 114, 0.08)';

      const ssp2LineColor = (isPrecip || isMinTemp || isAvgTemp) ? '#f1c40f' : '#E69F3E';
      const ssp2BandBorderColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(241, 196, 15, 0.5)' : 'transparent';
      const ssp2BandBgColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(241, 196, 15, 0.12)' : 'rgba(230, 159, 62, 0.10)';

      const ssp3LineColor = (isPrecip || isMinTemp || isAvgTemp) ? '#e67e22' : '#D93B48';
      const ssp3BandBorderColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(230, 126, 34, 0.5)' : 'transparent';
      const ssp3BandBgColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(230, 126, 34, 0.15)' : 'rgba(217, 59, 72, 0.16)';

      const ssp5LineColor = (isPrecip || isMinTemp || isAvgTemp) ? '#b30000' : '#8B1E2B';
      const ssp5BandBorderColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(179, 0, 0, 0.5)' : 'transparent';
      const ssp5BandBgColor = (isPrecip || isMinTemp || isAvgTemp) ? 'rgba(179, 0, 0, 0.15)' : 'rgba(139, 30, 43, 0.22)';

      const newChart = new Chart(ctx, {
        type: 'line',
        data: {
          datasets: [
            // SSP5-8.5 Area Uncertainty (Red) - FIRST
            {
              label: (isMinTemp || isAvgTemp) ? 'Maks SSP5' : 'SSP5-8.5 (Batas Atas)',
              data: dataSSP5_max,
              borderColor: ssp5BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isMinTemp || isAvgTemp) ? 'Min SSP5' : 'SSP5-8.5 (Batas Bawah)',
              data: dataSSP5_min,
              borderColor: ssp5BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp5BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP3-7.0 Area Uncertainty (Orange)
            {
              label: (isMinTemp || isAvgTemp) ? 'Maks SSP3' : 'SSP3-7.0 (Batas Atas)',
              data: dataSSP3_max,
              borderColor: ssp3BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isMinTemp || isAvgTemp) ? 'Min SSP3' : 'SSP3-7.0 (Batas Bawah)',
              data: dataSSP3_min,
              borderColor: ssp3BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp3BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP2-4.5 Area Uncertainty (Yellow)
            {
              label: (isMinTemp || isAvgTemp) ? 'Maks SSP2' : 'SSP2-4.5 (Batas Atas)',
              data: dataSSP2_max,
              borderColor: ssp2BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isMinTemp || isAvgTemp) ? 'Min SSP2' : 'SSP2-4.5 (Batas Bawah)',
              data: dataSSP2_min,
              borderColor: ssp2BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp2BandBgColor,
              fill: '-1',
              showLine: true
            },
            // SSP1-2.6 Area Uncertainty (Blue)
            {
              label: (isMinTemp || isAvgTemp) ? 'Maks SSP1' : 'SSP1-2.6 (Batas Atas)',
              data: dataSSP1_max,
              borderColor: ssp1BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              fill: false,
              showLine: true
            },
            {
              label: (isMinTemp || isAvgTemp) ? 'Min SSP1' : 'SSP1-2.6 (Batas Bawah)',
              data: dataSSP1_min,
              borderColor: ssp1BandBorderColor,
              borderWidth: 1,
              pointRadius: 0,
              backgroundColor: ssp1BandBgColor,
              fill: '-1',
              showLine: true
            },
            // HISTORICAL
            {
              label: (isMinTemp || isAvgTemp) ? 'Hist' : 'Historical',
              data: dataHist,
              borderColor: histBorderColor,
              borderWidth: histBorderWidth,
              pointRadius: 0,
              pointHoverRadius: 5,
              fill: false,
              spanGaps: false
            },
            // SSP5-8.5 (Dark Red)
            {
              label: (isMinTemp || isAvgTemp) ? 'SSP5-8.5' : 'SSP5-8.5 (Sangat Tinggi)',
              data: dataSSP5,
              borderColor: ssp5LineColor,
              borderWidth: (isPrecip || isMinTemp || isAvgTemp) ? 3.5 : 2,
              pointRadius: 0,
              pointHoverRadius: 5,
              fill: false
            },
            // SSP3-7.0 (Red/Orange)
            {
              label: (isMinTemp || isAvgTemp) ? 'SSP3-7.0' : 'SSP3-7.0 (Tinggi)',
              data: dataSSP3,
              borderColor: ssp3LineColor,
              borderWidth: (isPrecip || isMinTemp || isAvgTemp) ? 3.5 : 2,
              pointRadius: 0,
              pointHoverRadius: 5,
              fill: false
            },
            // SSP2-4.5 (Orange/Yellow)
            {
              label: (isMinTemp || isAvgTemp) ? 'SSP2-4.5' : 'SSP2-4.5 (Sedang)',
              data: dataSSP2,
              borderColor: ssp2LineColor,
              borderWidth: (isPrecip || isMinTemp || isAvgTemp) ? 3.5 : 2,
              pointRadius: 0,
              pointHoverRadius: 5,
              fill: false
            },
            // SSP1-2.6 (Blue)
            {
              label: (isMinTemp || isAvgTemp) ? 'SSP1-2.6' : 'SSP1-2.6 (Rendah)',
              data: dataSSP1,
              borderColor: ssp1LineColor,
              borderWidth: (isPrecip || isMinTemp || isAvgTemp) ? 3.5 : 2,
              pointRadius: 0,
              pointHoverRadius: 5,
              fill: false
            }
          ]
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
          interaction: {
            mode: 'index',
            intersect: false
          },
          plugins: {
            legend: {
              display: (isMinTemp || isAvgTemp), // Show default legend for temperature anomalies
              position: 'right',
              labels: {
                color: textColor,
                usePointStyle: false,
                padding: 10,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: '600',
                  size: 11
                }
              }
            },
            tooltip: {
              backgroundColor: isDark ? '#1e293b' : '#ffffff',
              titleColor: isDark ? '#ffffff' : '#1e293b',
              bodyColor: isDark ? '#cbd5e1' : '#475569',
              borderColor: isDark ? '#334155' : '#cbd5e1',
              borderWidth: 1,
              callbacks: {
                label: function(context) {
                  const lbl = context.dataset.label;
                  if (lbl.includes('Batas') || lbl.startsWith('Min ') || lbl.startsWith('Maks ')) return null;
                  let val = context.parsed.y;
                  if (val !== null) {
                    return `${lbl}: ${val.toFixed(2)} ${unit}`;
                  }
                  return null;
                }
              }
            }
          },
          scales: {
            x: {
              type: 'linear',
              min: (isPrecip || isMinTemp || isAvgTemp) ? 1991 : minYr,
              max: (isPrecip || isMinTemp || isAvgTemp) ? 2100 : maxYr,
              afterBuildTicks: function(scale) {
                if (isPrecip || isMinTemp || isAvgTemp) {
                  const ticks = [];
                  for (let y = 1991; y <= 2096; y += 5) {
                    ticks.push({ value: y });
                  }
                  scale.ticks = ticks;
                }
              },
              border: {
                display: true,
                color: isDark ? '#475569' : '#94a3b8',
                width: 1.5
              },
              grid: {
                display: false
              },
              ticks: {
                color: textColor,
                stepSize: (isPrecip || isMinTemp || isAvgTemp) ? 5 : 20,
                minRotation: (isPrecip || isMinTemp || isAvgTemp) ? 45 : 0,
                maxRotation: (isPrecip || isMinTemp || isAvgTemp) ? 45 : 0,
                callback: function(value) {
                  return value; // display year number directly
                },
                font: {
                  family: "'Poppins', sans-serif"
                }
              },
              title: {
                display: true,
                text: 'Tahun',
                color: textColor,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: 'bold'
                }
              }
            },
            y: {
              min: isPrecip ? 0 : (isMinTemp ? -1 : (isAvgTemp ? -1 : undefined)),
              max: isPrecip ? 5000 : (isMinTemp ? 5 : (isAvgTemp ? 7 : undefined)),
              border: {
                display: true,
                color: isDark ? '#475569' : '#94a3b8',
                width: 1.5
              },
              grid: {
                display: true,
                drawOnChartArea: true,
                color: function(context) {
                  if ((isMinTemp || isAvgTemp) && context.tick && context.tick.value === 0) {
                    return isDark ? 'rgba(255, 255, 255, 0.35)' : 'rgba(0, 0, 0, 0.25)';
                  }
                  return (isMinTemp || isAvgTemp) ? (isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.08)') : 'transparent';
                },
                lineWidth: function(context) {
                  if ((isMinTemp || isAvgTemp) && context.tick && context.tick.value === 0) {
                    return 1.5;
                  }
                  return (isMinTemp || isAvgTemp) ? 1 : 0;
                }
              },
              ticks: {
                color: textColor,
                stepSize: isPrecip ? 500 : ((isMinTemp || isAvgTemp) ? 1 : undefined),
                font: {
                  family: "'Poppins', sans-serif"
                }
              },
              title: {
                display: true,
                text: yLabel,
                color: textColor,
                font: {
                  family: "'Poppins', sans-serif",
                  weight: 'bold'
                }
              }
            }
          }
        },
        plugins: [
          {
            id: 'periodHighlight',
            beforeDraw: (chart) => {
              if (!periodRange || isMinTemp || isAvgTemp) return;
              const parts = periodRange.split('-');
              if (parts.length !== 2) return;
              const startP = parseInt(parts[0]);
              const endP = parseInt(parts[1]);
              const xAxis = chart.scales.x;
              const yAxis = chart.scales.y;
              
              const xStart = xAxis.getPixelForValue(startP);
              const xEnd = xAxis.getPixelForValue(endP);
              
              const ctx = chart.ctx;
              ctx.save();
              ctx.fillStyle = isDark ? 'rgba(59, 130, 246, 0.12)' : 'rgba(37, 99, 235, 0.06)';
              ctx.fillRect(xStart, yAxis.top, xEnd - xStart, yAxis.bottom - yAxis.top);
              ctx.strokeStyle = isDark ? 'rgba(59, 130, 246, 0.4)' : 'rgba(37, 99, 235, 0.3)';
              ctx.lineWidth = 1.5;
              ctx.setLineDash([4, 4]);
              ctx.beginPath();
              ctx.moveTo(xStart, yAxis.top);
              ctx.lineTo(xStart, yAxis.bottom);
              ctx.moveTo(xEnd, yAxis.top);
              ctx.lineTo(xEnd, yAxis.bottom);
              ctx.stroke();
              ctx.restore();
            }
          },
          {
            id: 'historicalSeparator',
            afterDraw: (chart) => {
              if (isMinTemp || isAvgTemp) return;
              const xAxis = chart.scales.x;
              const yAxis = chart.scales.y;
              
              // Draw divider at 2020.5 (midpoint between 2020 and 2021)
              const xPos = xAxis.getPixelForValue(2020.5);
              const ctx = chart.ctx;
              
              ctx.save();
              ctx.strokeStyle = isDark ? 'rgba(255, 255, 255, 0.45)' : 'rgba(0, 0, 0, 0.35)';
              ctx.lineWidth = 1.8;
              ctx.setLineDash([5, 5]);
              ctx.beginPath();
              ctx.moveTo(xPos, yAxis.top);
              ctx.lineTo(xPos, yAxis.bottom);
              ctx.stroke();
              
              // Labels
              ctx.fillStyle = isDark ? '#cbd5e1' : '#475569';
              ctx.font = 'bold 10px Poppins, sans-serif';
              
              // Historical label
              ctx.textAlign = 'right';
              ctx.fillText('Historical ', xPos - 5, yAxis.top + 15);
              
              // Projections label
              ctx.textAlign = 'left';
              ctx.fillText(' Projections', xPos + 5, yAxis.top + 15);
              
              ctx.restore();
            }
          }
        ]
      });

      if (isMain) {
        futureClimateChart = newChart;
        const overlay = document.getElementById('futureChartLegendOverlay');
        if (overlay) {
          if (isMinTemp || isAvgTemp) {
            overlay.style.background = 'transparent';
            overlay.style.boxShadow = 'none';
            overlay.style.border = 'none';
            overlay.style.top = '60px';
            overlay.style.left = '75px';
          } else {
            overlay.style.background = 'rgba(255, 255, 255, 0.75)';
            overlay.style.boxShadow = '';
            overlay.style.border = '';
            overlay.style.top = '65px';
            overlay.style.left = '80px';
          }
        }
      } else {
        settingsFutureClimateChart = newChart;
      }
    }

    let currentMapIndex = 1;
    function switchClimateMap(index) {
      currentMapIndex = index;
      const container1 = document.getElementById('containerMap1');
      const container2 = document.getElementById('containerMap2');
      const btn1 = document.getElementById('btnMap1');
      const btn2 = document.getElementById('btnMap2');
      
      if (!container1 || !container2 || !btn1 || !btn2) return;
      
      if (index === 1) {
        container1.style.display = 'block';
        container1.style.opacity = '0';
        setTimeout(() => { container1.style.opacity = '1'; }, 10);
        container2.style.display = 'none';
        btn1.classList.add('active');
        btn2.classList.remove('active');
      } else {
        container1.style.display = 'none';
        container2.style.display = 'block';
        container2.style.opacity = '0';
        setTimeout(() => { container2.style.opacity = '1'; }, 10);
        btn1.classList.remove('active');
        btn2.classList.add('active');
      }
    }
    
    function nextClimateMap() {
      const nextIndex = currentMapIndex === 1 ? 2 : 1;
      switchClimateMap(nextIndex);
    }
    
    function prevClimateMap() {
      const prevIndex = currentMapIndex === 2 ? 1 : 2;
      switchClimateMap(prevIndex);
    }

    // Pemanggilan AJAX ke PHP untuk filter Future Climate
    async function applyFutureFilter() {
      const lokasi = document.getElementById('futureLocation').value;
      const variable = document.getElementById('futureVariable').value;
      const period = document.getElementById('futurePeriod').value;

      if (!lokasi || !variable || !period) {
        alert('Silakan pilih lokasi, variabel, dan periode terlebih dahulu.');
        return;
      }

      document.getElementById('futureDataInfo').innerHTML =
        'Lokasi: ' + lokasi + '<br>' +
        'Variable: ' + variable + '<br>' +
        'Period: ' + period + '<br>' +
        'Kategori: Proyeksi Perubahan Iklim';

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}&period=${encodeURIComponent(period)}`);
        const result = await res.json();

        if (result.success && result.data) {
          const savedData = result.data;
          document.getElementById('futureDescription').innerText = savedData.description || '';

          // Climate Change Map
          document.getElementById('futureImageClimateMap').src = savedData.imageClimateMap ? normalizeImageUrl(savedData.imageClimateMap) : PLACEHOLDER_IMG;
          document.getElementById('futureImageClimateMap2').src = savedData.imageClimateMap2 ? normalizeImageUrl(savedData.imageClimateMap2) : PLACEHOLDER_IMG;
          switchClimateMap(1); // Reset slider to show Map 1 first
          document.getElementById('futureCategoryTitleMap').innerText = "Climate Change Map";
          document.getElementById('futureVariableText').innerText = "Variabel: " + variable;
          document.getElementById('futurePeriodText').innerText = "Periode: " + period;

          // Future Climate Stripe
          const scenario = currentStripeScenario;
          let stripeUrl = PLACEHOLDER_IMG;
          let stripeLabel = "Skenario: " + scenario;
          if (scenario === 'SSP1-2.6' && savedData.imageStripeSSP126) stripeUrl = normalizeImageUrl(savedData.imageStripeSSP126);
          else if (scenario === 'SSP2-4.5' && savedData.imageStripeSSP245) stripeUrl = normalizeImageUrl(savedData.imageStripeSSP245);
          else if (scenario === 'SSP3-7.0' && savedData.imageStripeSSP370) stripeUrl = normalizeImageUrl(savedData.imageStripeSSP370);
          else if (scenario === 'SSP5-8.5' && savedData.imageStripeSSP585) stripeUrl = normalizeImageUrl(savedData.imageStripeSSP585);
          else if (savedData.imageClimateStripe) stripeUrl = normalizeImageUrl(savedData.imageClimateStripe);
          else stripeLabel = "Skenario: " + scenario + " (Data tidak ditemukan)";

          document.getElementById('futureImageClimateStripe').src = stripeUrl;
          document.getElementById('futureCategoryTitleStripe').innerText = "Future Climate Stripe";
          document.getElementById('futureVariableTextStripe').innerText = "Variabel: " + variable;
          document.getElementById('futurePeriodTextStripe').innerText = stripeLabel;

          alert('Data future berhasil dimuat dari database!');
        } else {
          document.getElementById('futureDescription').innerText = 'Data climate belum tersedia di database untuk filter ini.';
          // Reset gambar ke placeholder
          document.getElementById('futureImageClimateMap').src = PLACEHOLDER_IMG;
          document.getElementById('futureImageClimateMap2').src = PLACEHOLDER_IMG;
          switchClimateMap(1); // Reset slider to show Map 1 first
          document.getElementById('futureImageClimateStripe').src = PLACEHOLDER_IMG;
          
          const scenario = currentStripeScenario;
          document.getElementById('futureVariableText').innerText = "Variabel: " + variable;
          document.getElementById('futurePeriodText').innerText = "Periode: " + period;
          document.getElementById('futureVariableTextStripe').innerText = "Variabel: " + variable;
          document.getElementById('futurePeriodTextStripe').innerText = "Skenario: " + scenario + " (Data tidak ditemukan)";
        }
      } catch (err) {
        console.error(err);
        alert('Gagal memuat data future tambahan dari database server.');
      }
      
      // Auto-trigger Time Series & Stripe filters when main filter is applied
      applyFutureTSFilter();
      applyFutureStripeFilter();
    }

    async function applyFutureStripeFilter() {
      const lokasi = document.getElementById('futureLocation').value || 'Indonesia';
      const variable = document.getElementById('futureVariable').value;
      const scenario = currentStripeScenario;
      const period = document.getElementById('futurePeriod').value || '2021-2050';

      if (!variable || !scenario) {
        alert('Silakan pilih variabel terlebih dahulu.');
        return;
      }

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}&period=${encodeURIComponent(period)}`);
        const result = await res.json();

        let stripeUrl = PLACEHOLDER_IMG;
        let found = false;
        if (result.success && result.data) {
          const savedData = result.data;
          if (scenario === 'SSP1-2.6' && savedData.imageStripeSSP126) { stripeUrl = normalizeImageUrl(savedData.imageStripeSSP126); found = true; }
          else if (scenario === 'SSP2-4.5' && savedData.imageStripeSSP245) { stripeUrl = normalizeImageUrl(savedData.imageStripeSSP245); found = true; }
          else if (scenario === 'SSP3-7.0' && savedData.imageStripeSSP370) { stripeUrl = normalizeImageUrl(savedData.imageStripeSSP370); found = true; }
          else if (scenario === 'SSP5-8.5' && savedData.imageStripeSSP585) { stripeUrl = normalizeImageUrl(savedData.imageStripeSSP585); found = true; }
          
          if (!found && savedData.imageClimateStripe) {
            stripeUrl = normalizeImageUrl(savedData.imageClimateStripe);
            found = true;
          }
        }

        document.getElementById('futureImageClimateStripe').src = stripeUrl;
        document.getElementById('futureVariableTextStripe').innerText = "Variabel: " + variable;
        document.getElementById('futurePeriodTextStripe').innerText = "Skenario: " + scenario + (found ? "" : " (Data tidak ditemukan)");
      } catch (err) {
        console.error(err);
        alert('Gagal memuat data stripe proyeksi.');
      }
    }

    async function applyFutureTSFilter() {
      const lokasi = document.getElementById('futureLocation').value;
      const variable = document.getElementById('futureVariable').value;
      const period = document.getElementById('futurePeriod').value;

      if (!lokasi || !variable) {
        alert('Silakan pilih lokasi dan variabel terlebih dahulu.');
        return;
      }

      document.getElementById('futureCategoryTitleTimeSeries').innerText = "Future Time Series Graph";
      document.getElementById('futureVariableTextTS').innerText = "Variabel: " + variable;
      document.getElementById('futurePeriodTextTS').innerText = "Periode: " + period;

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}&period=${encodeURIComponent(period)}`);
        const result = await res.json();

        let showStatic = false;
        if (result.success && result.data && result.data.imageTimeSeries) {
          const imageUrl = normalizeImageUrl(result.data.imageTimeSeries);
          if (imageUrl && imageUrl !== PLACEHOLDER_IMG && imageUrl.trim() !== '') {
            document.getElementById('futureImageTimeSeries').src = imageUrl;
            document.getElementById('futureStaticTSContainer').style.display = 'block';
            document.getElementById('futureInteractiveChartContainer').style.display = 'none';
            showStatic = true;
          }
        }

        if (!showStatic) {
          document.getElementById('futureStaticTSContainer').style.display = 'none';
          document.getElementById('futureInteractiveChartContainer').style.display = 'block';

          const savedData = (result.success && result.data) ? result.data : null;
          let loadedCSV = false;

          if (savedData && savedData.csvData) {
            renderFutureClimateChart(variable, savedData, period);
            loadedCSV = true;
          } else {
            const vNorm = variable.toLowerCase();
            const isAvgTemp = vNorm.includes('rata-rata') || vNorm.includes('rata') || vNorm.includes('average') || vNorm.includes('mean');
            const isMinTemp = vNorm.includes('minimum') || vNorm.includes('min');
            if (!isAvgTemp && !isMinTemp) {
              let startYearForCSV = '2021';
              if (vNorm.includes('precipitation') || vNorm.includes('curah hujan') || vNorm.includes('hujan')) {
                startYearForCSV = '1991';
              }
              try {
                const csvRes = await fetch(`?action=get_future_csv&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}&statistic=Original&start_year=${startYearForCSV}&end_year=2100`);
                const csvResult = await csvRes.json();
                if (csvResult.success && csvResult.mean && csvResult.mean.length > 0) {
                  renderFutureCSVChart(csvResult, variable, 'Original', startYearForCSV, '2100');
                  loadedCSV = true;
                }
              } catch (csvErr) {
                console.error("Kesalahan memuat CSV grafik proyeksi:", csvErr);
              }
            }
          }

          if (!loadedCSV) {
            renderFutureClimateChart(variable, savedData, period);
          }
        }
      } catch (err) {
        console.error("Kesalahan memuat data Time Series proyeksi:", err);
      }
    }

    async function loadFutureStripeSettingsData() {
      const lokasiSelect = document.getElementById('futureSetStripeLocation');
      const variableSelect = document.getElementById('futureSetStripeVariable');

      if (!lokasiSelect || !variableSelect) return;

      const lokasi = lokasiSelect.value.trim();
      const variable = variableSelect.value.trim();

      if (!lokasi || !variable) return;

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}`);
        const result = await res.json();

        if (result.success && result.data) {
          const d = result.data;
          document.getElementById('futureSetStripeSSP126').value = d.imageStripeSSP126 || '';
          document.getElementById('futureSetStripeSSP245').value = d.imageStripeSSP245 || '';
          document.getElementById('futureSetStripeSSP370').value = d.imageStripeSSP370 || '';
          document.getElementById('futureSetStripeSSP585').value = d.imageStripeSSP585 || '';
        } else {
          document.getElementById('futureSetStripeSSP126').value = '';
          document.getElementById('futureSetStripeSSP245').value = '';
          document.getElementById('futureSetStripeSSP370').value = '';
          document.getElementById('futureSetStripeSSP585').value = '';
        }
      } catch (err) {
        console.error(err);
      }
    }

    async function saveFutureStripeSettings(silent = false) {
      const lokasi = document.getElementById('futureSetStripeLocation').value.trim();
      const variable = document.getElementById('futureSetStripeVariable').value.trim();
 
      const stripeSSP126 = normalizeImageUrl(document.getElementById('futureSetStripeSSP126').value.trim());
      const stripeSSP245 = normalizeImageUrl(document.getElementById('futureSetStripeSSP245').value.trim());
      const stripeSSP370 = normalizeImageUrl(document.getElementById('futureSetStripeSSP370').value.trim());
      const stripeSSP585 = normalizeImageUrl(document.getElementById('futureSetStripeSSP585').value.trim());
 
      if (!lokasi || !variable) {
        if (!silent) alert('Lokasi dan Variable wajib diisi!');
        return;
      }
 
      const data = {
        location: lokasi,
        variable: variable,
        period: '', // Kosongkan agar backend meng-update semua periode sekaligus
        imageStripeSSP126: stripeSSP126,
        imageStripeSSP245: stripeSSP245,
        imageStripeSSP370: stripeSSP370,
        imageStripeSSP585: stripeSSP585
      };
 
      try {
        showAutosaveStatus('Menyimpan otomatis...');
        const res = await fetch('?action=save_future', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(data)
        });
        const result = await res.json();
 
        if (result.success) {
          showAutosaveStatus('✓ Tersimpan ke database', true);
          if (!silent) alert('Data Climate Stripe berhasil disimpan ke database SQLite!');
          await loadFilters();
        } else {
          showAutosaveStatus('❌ Gagal menyimpan', false);
          if (!silent) alert('Gagal menyimpan: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        showAutosaveStatus('❌ Gagal menyimpan', false);
        if (!silent) alert('Gagal mengirim data penyimpanan ke server.');
      }
    }

    async function loadFutureData() {
      const lokasi = document.getElementById('futureSetLocation').value.trim();
      const variable = document.getElementById('futureSetVariable').value.trim();
      const period = document.getElementById('futureSetPeriod').value.trim();

      if (!lokasi || !variable || !period) {
        alert('Silakan tentukan Lokasi, Variable, dan Periode terlebih dahulu.');
        return;
      }

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}&period=${encodeURIComponent(period)}`);
        const result = await res.json();

        if (result.success && result.data) {
          const d = result.data;
          document.getElementById('futureSetDescription').value = d.description || '';
          document.getElementById('futureSetImageClimateMap').value = d.imageClimateMap || '';
          document.getElementById('futureSetImageClimateMap2').value = d.imageClimateMap2 || '';
          document.getElementById('futureSetImageClimateStripe').value = d.imageClimateStripe || '';
          document.getElementById('futureSetImageTimeSeries').value = d.imageTimeSeries || '';
          alert('Data existing berhasil dimuat!');
        } else {
          alert('Data tidak ditemukan untuk kombinasi ini. Anda dapat membuat data baru.');
          document.getElementById('futureSetDescription').value = '';
          document.getElementById('futureSetImageClimateMap').value = '';
          document.getElementById('futureSetImageClimateMap2').value = '';
          document.getElementById('futureSetImageClimateStripe').value = '';
          document.getElementById('futureSetImageTimeSeries').value = '';
        }
      } catch (err) {
        console.error(err);
        alert('Gagal memuat data existing.');
      }
    }

    // Mengirim data baru Future Climate ke backend (settings)
    async function saveFutureData(silent = false) {
      const lokasi = document.getElementById('futureSetLocation').value.trim();
      const variable = document.getElementById('futureSetVariable').value.trim();
      const period = document.getElementById('futureSetPeriod').value.trim();
      const description = document.getElementById('futureSetDescription').value.trim();
      const imageClimateMap = normalizeImageUrl(document.getElementById('futureSetImageClimateMap').value.trim());
      const imageClimateMap2 = normalizeImageUrl(document.getElementById('futureSetImageClimateMap2').value.trim());
      const imageTimeSeries = normalizeImageUrl(document.getElementById('futureSetImageTimeSeries').value.trim());
      const imageClimateStripe = normalizeImageUrl(document.getElementById('futureSetImageClimateStripe').value.trim());

      if (!lokasi || !variable || !period) {
        if (!silent) alert('Lokasi, Variable, dan Periode wajib diisi!');
        return;
      }

      const data = {
        location: lokasi,
        variable: variable,
        period: period,
        description: description,
        imageClimateMap: imageClimateMap,
        imageClimateMap2: imageClimateMap2,
        imageTimeSeries: imageTimeSeries,
        imageClimateStripe: imageClimateStripe
      };

      try {
        showAutosaveStatus('Menyimpan otomatis...');
        const res = await fetch('?action=save_future', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(data)
        });
        const result = await res.json();

        if (result.success) {
          showAutosaveStatus('✓ Tersimpan ke database', true);
          if (!silent) alert('Data future berhasil disimpan ke database SQLite!');
          await loadFilters();
        } else {
          showAutosaveStatus('❌ Gagal menyimpan', false);
          if (!silent) alert('Gagal menyimpan: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        showAutosaveStatus('❌ Gagal menyimpan', false);
        if (!silent) alert('Gagal mengirim data penyimpanan ke server.');
      }
    }

    async function loadFutureParamsData() {
      const lokasi = document.getElementById('futureSetParamsLocation').value.trim();
      const variable = document.getElementById('futureSetParamsVariable').value.trim();

      if (!lokasi || !variable) {
        alert('Silakan tentukan Lokasi dan Variable terlebih dahulu.');
        return;
      }

      try {
        const res = await fetch(`?action=get_future&location=${encodeURIComponent(lokasi)}&variable=${encodeURIComponent(variable)}`);
        const result = await res.json();

        if (result.success && result.data) {
          const d = result.data;
          document.getElementById('futureSetParamsCsvData').value = d.csvData || '';
          
          const indicator = document.getElementById('csvStatusIndicator');
          if (d.csvData) {
            try {
              const rows = JSON.parse(d.csvData);
              if (indicator) {
                indicator.innerText = `CSV Terdeteksi di Database: ${rows.length} baris data.`;
                indicator.style.color = '#16a34a';
              }
            } catch (pErr) {
              if (indicator) indicator.innerText = 'CSV Terdeteksi (format raw/invalid JSON)';
            }
          } else {
            if (indicator) indicator.innerText = '';
          }

          alert('Parameter existing berhasil dimuat!');
          updateSettingsParamsChartPreview();
        } else {
          alert('Data tidak ditemukan untuk kombinasi ini. Anda dapat menentukan parameter baru.');
          document.getElementById('futureSetParamsCsvData').value = '';
          const indicator = document.getElementById('csvStatusIndicator');
          if (indicator) indicator.innerText = '';
          updateSettingsParamsChartPreview();
        }
      } catch (err) {
        console.error(err);
        alert('Gagal memuat parameter existing.');
      }
    }

    function updateSettingsParamsChartPreview() {
      const variable = document.getElementById('futureSetParamsVariable')?.value.trim() || 'Precipitation';
      const csvData = document.getElementById('futureSetParamsCsvData')?.value.trim();

      const customParams = {
        graphBaseValue: null,
        graphUncertainty: null,
        graphSSP1Slope: null,
        graphSSP2Slope: null,
        graphSSP3Slope: null,
        graphSSP5Slope: null,
        csvData: csvData || null
      };

      renderFutureClimateChart(variable, customParams, null, 'settingsFutureParamsChartCanvas');
    }

    async function saveFutureParamsData() {
      const lokasi = document.getElementById('futureSetParamsLocation').value.trim();
      const variable = document.getElementById('futureSetParamsVariable').value.trim();
      const csvData = document.getElementById('futureSetParamsCsvData').value.trim();

      if (!lokasi || !variable) {
        alert('Lokasi dan Variable wajib diisi!');
        return;
      }

      const data = {
        location: lokasi,
        variable: variable,
        period: '',
        graphBaseValue: '',
        graphUncertainty: '',
        graphSSP1Slope: '',
        graphSSP2Slope: '',
        graphSSP3Slope: '',
        graphSSP5Slope: '',
        csvData: csvData
      };

      try {
        const res = await fetch('?action=save_future', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify(data)
        });
        const result = await res.json();

        if (result.success) {
          alert('Parameter grafik proyeksi berhasil disimpan ke database SQLite!');
          
          // Reset file input indicator
          const csvFileEl = document.getElementById('futureSetParamsCsvFile');
          if (csvFileEl) csvFileEl.value = '';
          
          await loadFilters();
        } else {
          alert('Gagal menyimpan: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        alert('Gagal mengirim data parameter ke server.');
      }
    }

    // Tambah Lokasi Master
    async function addMasterLocation() {
      const nameInput = document.getElementById('masterLocationName');
      const name = nameInput.value.trim();
      if (!name) {
        alert('Nama lokasi tidak boleh kosong!');
        return;
      }

      try {
        const res = await fetch('?action=add_location', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ name })
        });
        const result = await res.json();
        if (result.success) {
          alert('Lokasi master baru berhasil ditambahkan!');
          nameInput.value = '';
          await loadFilters();
        } else {
          alert('Gagal menambah lokasi: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        alert('Gagal mengirim permintaan tambah lokasi.');
      }
    }

    // Tambah Variabel Master
    async function addMasterVariable() {
      const nameInput = document.getElementById('masterVariableName');
      const name = nameInput.value.trim();
      if (!name) {
        alert('Nama variabel tidak boleh kosong!');
        return;
      }

      try {
        const res = await fetch('?action=add_variable', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ name })
        });
        const result = await res.json();
        if (result.success) {
          alert('Variabel master baru berhasil ditambahkan!');
          nameInput.value = '';
          await loadFilters();
        } else {
          alert('Gagal menambah variabel: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        alert('Gagal mengirim permintaan tambah variabel.');
      }
    }

    // Tambah Periode Master
    async function addMasterPeriod() {
      const rangeInput = document.getElementById('masterPeriodRange');
      const range = rangeInput.value.trim();
      if (!range) {
        alert('Rentang periode tidak boleh kosong!');
        return;
      }

      try {
        const res = await fetch('?action=add_period', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ range })
        });
        const result = await res.json();
        if (result.success) {
          alert('Periode master baru berhasil ditambahkan!');
          rangeInput.value = '';
          await loadFilters();
        } else {
          alert('Gagal menambah periode: ' + (result.error || result.message));
        }
      } catch (err) {
        console.error(err);
        alert('Gagal mengirim permintaan tambah periode.');
      }
    }

    function parseCsvText(text) {
      const lines = text.split(/\r?\n/).map(line => line.trim()).filter(line => line.length > 0);
      if (lines.length === 0) return [];
      
      let separator = ',';
      const firstLine = lines[0];
      const commas = (firstLine.match(/,/g) || []).length;
      const semicolons = (firstLine.match(/;/g) || []).length;
      if (semicolons > commas) {
        separator = ';';
      }
      
      const headers = firstLine.split(separator).map(h => h.trim().replace(/^["']|["']$/g, '').toLowerCase());
      const yearIdx = headers.findIndex(h => h.includes('year') || h.includes('tahun') || h === 't' || h === 'yr' || h === 'y');
      const histIdx = headers.findIndex(h => h.includes('hist') || h.includes('obs') || h.includes('real') || h.includes('sebelum') || h.includes('observasi'));
      
      // Look for Mean columns excluding min/max/bawah/atas keywords
      const ssp1Idx = headers.findIndex(h => h.includes('ssp1') && !h.includes('min') && !h.includes('max') && !h.includes('bawah') && !h.includes('atas'));
      const ssp2Idx = headers.findIndex(h => h.includes('ssp2') && !h.includes('min') && !h.includes('max') && !h.includes('bawah') && !h.includes('atas'));
      const ssp3Idx = headers.findIndex(h => h.includes('ssp3') && !h.includes('min') && !h.includes('max') && !h.includes('bawah') && !h.includes('atas'));
      const ssp5Idx = headers.findIndex(h => h.includes('ssp5') && !h.includes('min') && !h.includes('max') && !h.includes('bawah') && !h.includes('atas'));
      
      // Look for Min columns (containing sspX and min/bawah)
      const ssp1MinIdx = headers.findIndex(h => h.includes('ssp1') && (h.includes('min') || h.includes('bawah')));
      const ssp2MinIdx = headers.findIndex(h => h.includes('ssp2') && (h.includes('min') || h.includes('bawah')));
      const ssp3MinIdx = headers.findIndex(h => h.includes('ssp3') && (h.includes('min') || h.includes('bawah')));
      const ssp5MinIdx = headers.findIndex(h => h.includes('ssp5') && (h.includes('min') || h.includes('bawah')));

      // Look for Max columns (containing sspX and max/atas)
      const ssp1MaxIdx = headers.findIndex(h => h.includes('ssp1') && (h.includes('max') || h.includes('atas')));
      const ssp2MaxIdx = headers.findIndex(h => h.includes('ssp2') && (h.includes('max') || h.includes('atas')));
      const ssp3MaxIdx = headers.findIndex(h => h.includes('ssp3') && (h.includes('max') || h.includes('atas')));
      const ssp5MaxIdx = headers.findIndex(h => h.includes('ssp5') && (h.includes('max') || h.includes('atas')));

      if (yearIdx === -1) {
        return [];
      }
      
      const parsedRows = [];
      for (let i = 1; i < lines.length; i++) {
        const cols = lines[i].split(separator).map(c => c.trim().replace(/^["']|["']$/g, ''));
        const yearVal = parseInt(cols[yearIdx]);
        if (isNaN(yearVal)) continue;
        
        const row = { year: yearVal };
        if (histIdx !== -1 && cols[histIdx] !== undefined) row.historical = cols[histIdx] !== '' ? parseFloat(cols[histIdx]) : null;
        
        // SSP1
        if (ssp1Idx !== -1 && cols[ssp1Idx] !== undefined) row.ssp1 = cols[ssp1Idx] !== '' ? parseFloat(cols[ssp1Idx]) : null;
        if (ssp1MinIdx !== -1 && cols[ssp1MinIdx] !== undefined) row.ssp1_min = cols[ssp1MinIdx] !== '' ? parseFloat(cols[ssp1MinIdx]) : null;
        if (ssp1MaxIdx !== -1 && cols[ssp1MaxIdx] !== undefined) row.ssp1_max = cols[ssp1MaxIdx] !== '' ? parseFloat(cols[ssp1MaxIdx]) : null;

        // SSP2
        if (ssp2Idx !== -1 && cols[ssp2Idx] !== undefined) row.ssp2 = cols[ssp2Idx] !== '' ? parseFloat(cols[ssp2Idx]) : null;
        if (ssp2MinIdx !== -1 && cols[ssp2MinIdx] !== undefined) row.ssp2_min = cols[ssp2MinIdx] !== '' ? parseFloat(cols[ssp2MinIdx]) : null;
        if (ssp2MaxIdx !== -1 && cols[ssp2MaxIdx] !== undefined) row.ssp2_max = cols[ssp2MaxIdx] !== '' ? parseFloat(cols[ssp2MaxIdx]) : null;

        // SSP3
        if (ssp3Idx !== -1 && cols[ssp3Idx] !== undefined) row.ssp3 = cols[ssp3Idx] !== '' ? parseFloat(cols[ssp3Idx]) : null;
        if (ssp3MinIdx !== -1 && cols[ssp3MinIdx] !== undefined) row.ssp3_min = cols[ssp3MinIdx] !== '' ? parseFloat(cols[ssp3MinIdx]) : null;
        if (ssp3MaxIdx !== -1 && cols[ssp3MaxIdx] !== undefined) row.ssp3_max = cols[ssp3MaxIdx] !== '' ? parseFloat(cols[ssp3MaxIdx]) : null;

        // SSP5
        if (ssp5Idx !== -1 && cols[ssp5Idx] !== undefined) row.ssp5 = cols[ssp5Idx] !== '' ? parseFloat(cols[ssp5Idx]) : null;
        if (ssp5MinIdx !== -1 && cols[ssp5MinIdx] !== undefined) row.ssp5_min = cols[ssp5MinIdx] !== '' ? parseFloat(cols[ssp5MinIdx]) : null;
        if (ssp5MaxIdx !== -1 && cols[ssp5MaxIdx] !== undefined) row.ssp5_max = cols[ssp5MaxIdx] !== '' ? parseFloat(cols[ssp5MaxIdx]) : null;
        
        parsedRows.push(row);
      }
      return parsedRows;
    }

    // Event listener for CSV file selection
    document.addEventListener('change', function(e) {
      if (e.target && e.target.id === 'futureSetParamsCsvFile') {
        const file = e.target.files[0];
        const indicator = document.getElementById('csvStatusIndicator');
        const hiddenInput = document.getElementById('futureSetParamsCsvData');
        
        if (!file) {
          if (hiddenInput) hiddenInput.value = '';
          if (indicator) indicator.innerText = '';
          return;
        }
        
        const reader = new FileReader();
        reader.onload = function(evt) {
          const text = evt.target.result;
          const rows = parseCsvText(text);
          if (rows.length > 0) {
            if (hiddenInput) {
              hiddenInput.value = JSON.stringify(rows);
              updateSettingsParamsChartPreview(); // Trigger real-time settings preview
            }
            const years = rows.map(r => r.year).filter(y => !isNaN(y));
            const minYear = Math.min(...years);
            const maxYear = Math.max(...years);
            if (indicator) {
              indicator.innerText = `CSV Terdeteksi: ${rows.length} baris data (${minYear} - ${maxYear}).`;
              indicator.style.color = '#16a34a';
            }
          } else {
            alert('Gagal mendeteksi data valid di file CSV. Pastikan file memiliki kolom Tahun/Year.');
            e.target.value = '';
            if (hiddenInput) {
              hiddenInput.value = '';
              updateSettingsParamsChartPreview();
            }
            if (indicator) indicator.innerText = '';
          }
        };
        reader.readAsText(file);
      }
    });

    // Event listener for autosaving image inputs on change (paste/edit)
    document.addEventListener('change', function(e) {
      if (!e.target || !e.target.id) return;
      
      const historicalImageInputs = [
        'historicalSetImage',
        'historicalSetImageTrend',
        'historicalSetImageTimeSeries',
        'historicalSetImageMonthlyMap',
        'historicalSetImageMonthlyGraph'
      ];
      if (historicalImageInputs.includes(e.target.id)) {
        saveHistoricalData(true);
      }
      
      const futureImageInputs = [
        'futureSetImageClimateMap',
        'futureSetImageClimateMap2',
        'futureSetImageClimateStripe',
        'futureSetImageTimeSeries'
      ];
      if (futureImageInputs.includes(e.target.id)) {
        saveFutureData(true);
      }
      
      const futureStripeImageInputs = [
        'futureSetStripeSSP126',
        'futureSetStripeSSP245',
        'futureSetStripeSSP370',
        'futureSetStripeSSP585'
      ];
      if (futureStripeImageInputs.includes(e.target.id)) {
        saveFutureStripeSettings(true);
      }
    });

    // Listen for form changes to update settings chart preview in real-time
    document.addEventListener('input', function(e) {
      const settingsInputs = [
        'futureSetParamsVariable'
      ];
      if (e.target && settingsInputs.includes(e.target.id)) {
        updateSettingsParamsChartPreview();
      }
    });

    // Jalankan pemuatan filter dan sinkronisasi auth saat halaman siap
    window.addEventListener('DOMContentLoaded', async () => {
      await loadFilters();
      await loadFutureStripeSettingsData();
      // Render initial default settings chart preview
      updateSettingsParamsChartPreview();
      // Render initial default main chart from filters
      applyFutureFilter();
      await applyFutureTSFilter();
      try {
        const res = await fetch('?action=check_auth');
        const authData = await res.json();
        if (authData.authenticated) {
          isAdminLoggedIn = true;
        }
      } catch (err) {
        console.error('Gagal memverifikasi status otentikasi awal:', err);
      }
    });
  </script>
</body>

</html>
