<?php
session_start();
if (!isset($_SESSION['user_id'])) {
    header("Location: ../auth/login_page.php");
    exit;
}
?>
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>XIEQA IoT Simulator - Professional IoT & Hardware Sandbox</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
    <style>
        .bg-grid {
            background-color: #111827;
            background-size: 20px 20px;
            background-image: 
                linear-gradient(to right, rgba(255, 255, 255, 0.05) 1px, transparent 1px),
                linear-gradient(to bottom, rgba(255, 255, 255, 0.05) 1px, transparent 1px);
        }
        .led-off { background-color: #450a0a; border-color: #7f1d1d; box-shadow: inset 0 2px 4px rgba(0,0,0,0.5); }
        .led-on { 
            background-color: #ef4444; 
            border-color: #f87171;
            box-shadow: 0 0 20px 5px rgba(239, 68, 68, 0.8), inset 0 0 10px rgba(255, 255, 255, 0.8); 
        }
        textarea:focus { outline: none; }
        ::-webkit-scrollbar { width: 8px; }
        ::-webkit-scrollbar-track { background: #1f2937; }
        ::-webkit-scrollbar-thumb { background: #4b5563; border-radius: 4px; }
        
        .wire-active {
            stroke-dasharray: 10;
            animation: flow 0.6s linear infinite;
        }
        @keyframes flow { from { stroke-dashoffset: 20; } to { stroke-dashoffset: 0; } }
        
        /* Animasi Kipas */
        @keyframes fanRotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
        .fan-blade-active { animation: fanRotate 0.2s linear infinite; }
        
        .servo-horn { transition: transform 0.3s ease-in-out; transform-origin: center; }
        
        /* Slider Styling */
        input[type=range] { -webkit-appearance: none; width: 100%; background: #374151; height: 4px; border-radius: 2px; }
        input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; height: 12px; width: 12px; border-radius: 50%; background: #3b82f6; cursor: pointer; }
        /* Robot Arena */
        #robotArena {
            display: none;
            width: 500px;
            height: 500px;
            z-index: 25;
            background: #0f172a;
            overflow: hidden;
            border-radius: 12px;
            box-shadow: 0 0 50px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.1);
            position: relative;
        }
        .arena-wall { fill: #1e293b; stroke: #334155; stroke-width: 2; }
        .robot-car { transition: transform 0.05s linear; }
        .arena-border { fill: none; stroke: #3b82f6; stroke-width: 4; stroke-dasharray: 10,5; filter: drop-shadow(0 0 5px rgba(59, 130, 246, 0.5)); }
        
        /* Wheel Rotation Animation */
        @keyframes wheelSpin { from { stroke-dashoffset: 0; } to { stroke-dashoffset: 20; } }
        .wheel-spinning { animation: wheelSpin 0.1s linear infinite; }

        /* Dashboard & Terminal Styles */
        .glow-sensor { filter: drop-shadow(0 0 5px rgba(239, 68, 68, 0.6)); }
        .terminal-glow { text-shadow: 0 0 5px rgba(34, 197, 94, 0.5); }
        
        #serialOutput::-webkit-scrollbar { width: 4px; }
        #serialOutput::-webkit-scrollbar-thumb { background: #22c55e; border-radius: 10px; }

        .btn-pulse { animation: pulseGlow 2s infinite; }
        @keyframes pulseGlow {
            0% { box-shadow: 0 0 5px rgba(34, 197, 94, 0.4); }
            50% { box-shadow: 0 0 20px rgba(34, 197, 94, 0.8); }
            100% { box-shadow: 0 0 5px rgba(34, 197, 94, 0.4); }
        }

        /* Dashboard Gauges */
        .gauge-container { position: relative; width: 50px; height: 50px; transition: all 0.3s ease; }
        .gauge-svg { transform: rotate(-90deg); }
        .gauge-bg { fill: none; stroke: rgba(255,255,255,0.05); stroke-width: 4; }
        .gauge-bar { fill: none; stroke-width: 4; stroke-linecap: round; transition: stroke-dasharray 0.3s ease; }
        .gauge-text { position: absolute; inset: 0; display: flex; flex-direction: column; items: center; justify-content: center; font-family: monospace; font-weight: bold; }
        .gauge-text span:first-child { font-size: 7px; }
        .gauge-text span:last-child { font-size: 10px; }
        
        @media (max-width: 768px) {
            .gauge-container { width: 38px; height: 38px; }
            .gauge-text span:first-child { font-size: 5px !important; }
            .gauge-text span:last-child { font-size: 7px !important; }
        }
        
        .dashboard-panel {
            background: rgba(15, 23, 42, 0.4);
            backdrop-filter: blur(8px);
            border: 1px solid rgba(255, 255, 255, 0.05);
            border-radius: 20px;
            padding: 8px;
            display: flex;
            gap: 20px;
            box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
            transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
            position: absolute;
            top: 20px;
            right: 20px;
        }
        .dashboard-minimized {
            transform: translateY(-150%) scale(0.8);
            opacity: 0;
            pointer-events: none;
        }

        .hud-toggle-btn {
            position: absolute;
            top: 100px;
            right: 20px;
            background: rgba(59, 130, 246, 0.2);
            border: 1px solid rgba(59, 130, 246, 0.3);
            padding: 4px 12px;
            border-radius: 999px;
            color: #93c5fd;
            font-size: 10px;
            font-weight: bold;
            letter-spacing: 0.1em;
            z-index: 40;
            transition: all 0.3s ease;
            backdrop-filter: blur(4px);
        }
        .hud-toggle-btn:hover { background: rgba(59, 130, 246, 0.4); }

        /* Mobile Adjustments */
        @media (max-width: 768px) {
            #robotArena {
                width: 100% !important;
                height: auto !important;
                aspect-ratio: 1/1;
                margin-bottom: 0;
            }
            .dashboard-panel {
                padding: 4px;
                gap: 4px;
                top: 10px;
                right: 50%;
                bottom: auto;
                transform: translateX(50%);
                display: flex;
                justify-content: space-around;
                width: 98%;
                max-width: 320px;
                scale: 1;
            }
            .dashboard-minimized {
                transform: translateX(50%) translateY(-200%);
            }
            .hud-toggle-btn {
                top: 70px !important;
                bottom: auto !important;
                scale: 0.8;
            }
            #boardCanvas {
                scale: 0.85 !important;
            }
            #componentsCanvas {
                flex-wrap: wrap;
                justify-content: center;
                gap: 4px;
            }
            .mobile-hidden { display: none !important; }
            .mobile-visible { display: flex !important; }
        }

        .tab-active { color: #3b82f6; border-top: 2px solid #3b82f6; background: rgba(59, 130, 246, 0.1); }
        
        /* Smooth Map Marker */
        .car-smooth-marker {
            transition: transform 1.2s linear, margin 1.2s linear;
            z-index: 1000 !important;
        }
        .leaflet-zoom-anim .car-smooth-marker {
            transition: none !important;
        }
        .car-smooth-marker div {
            transition: transform 0.3s ease-in-out; /* Smooth rotation */
            transform-origin: center center;
        }
        
        /* Animasi Elegant Popup */
        .popup-enter { transform: translate(-50%, 0) !important; opacity: 1 !important; }
        .popup-leave { transform: translate(-50%, -2rem) !important; opacity: 0 !important; }

        /* Animasi Arrival Modal */
        @keyframes bounceIn {
            0%   { transform: scale(0.6); opacity: 0; }
            60%  { transform: scale(1.05); opacity: 1; }
            80%  { transform: scale(0.97); }
            100% { transform: scale(1); }
        }
        .animate-bounce-in { animation: bounceIn 0.45s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
    </style>
</head>
<body class="bg-gray-900 text-gray-200 h-screen flex flex-col font-sans overflow-hidden select-none">
    <!-- ELEGANT POPUP TOAST -->
    <div id="elegantPopup" class="fixed top-8 left-1/2 -translate-x-1/2 z-[9999] bg-white text-gray-800 px-6 py-3 rounded-2xl shadow-[0_10px_40px_rgba(37,99,235,0.4)] backdrop-blur-md opacity-0 popup-leave transition-all duration-500 font-semibold text-sm flex items-center gap-3 border-2 border-blue-100 pointer-events-none">
        <span class="text-2xl drop-shadow-sm">👩🏻‍💼</span>
        <span id="elegantPopupText" class="leading-relaxed tracking-wide">Pesan...</span>
    </div>

    <!-- ARRIVAL MODAL (Mobil Sudah Tiba) - fullscreen backdrop removed, now inside App Ojek -->


    <!-- Header -->
    <header class="bg-gray-800 border-b border-gray-700 px-4 md:px-6 py-2 md:py-3 flex justify-between items-center shadow-md z-30 relative">
        <div class="flex items-center gap-3">
            <div class="w-8 h-8 bg-blue-600 rounded flex items-center justify-center font-bold text-white tracking-widest shadow-lg shrink-0">XQ</div>
            <div class="hidden sm:block">
                <h1 class="text-lg md:text-xl font-semibold tracking-wide leading-tight uppercase tracking-[0.2em]">XIEQA <span class="text-blue-500">IoT Simulator</span></h1>
                <p class="text-[10px] text-gray-400">Simulasi Hardware & Integrasi Cloud (Direct Wiring)</p>
            </div>
            <div class="sm:hidden">
                <h1 class="text-sm font-bold text-blue-500 uppercase tracking-widest">XIEQA IOT SIM</h1>
            </div>
            <button onclick="toggleSidebar()" id="btnToggleSidebar" class="hidden md:flex ml-2 px-3 py-1.5 bg-gray-700 hover:bg-gray-600 text-gray-300 border border-gray-600 rounded text-[10px] font-bold transition-all items-center gap-1 shadow-sm">
                <span>◀</span> PANEL
            </button>
        </div>
        <div class="flex gap-2 md:gap-4">
            <a href="../index.php" class="px-3 py-1.5 md:py-2 bg-gray-700 hover:bg-gray-600 text-gray-300 border border-gray-600 rounded-md text-[10px] md:text-sm font-bold transition-all flex items-center gap-2 text-decoration-none">
                <span class="text-lg">🏠</span> <span class="hidden sm:inline">KEMBALI</span>
            </a>
            <button onclick="toggleMapArena()" id="btnToggleMap" class="px-3 py-1.5 md:py-2 bg-green-600/20 hover:bg-green-600/40 text-green-400 border border-green-500/50 rounded-md text-[10px] md:text-sm font-bold transition-all flex items-center gap-2">
                <span class="text-lg">🗺️</span> <span class="hidden sm:inline">PETA IOT</span>
            </button>
            <button onclick="toggleIoTModal()" class="px-3 py-1.5 md:py-2 bg-blue-600/20 hover:bg-blue-600/40 text-blue-400 border border-blue-500/50 rounded-md text-[10px] md:text-sm font-bold transition-all flex items-center gap-2">
                <span class="text-lg">☁️</span> <span class="hidden sm:inline">IOT CONFIG</span>
            </button>
            <button id="btnStop" class="hidden px-3 md:px-6 py-1.5 md:py-2 bg-red-600 hover:bg-red-500 text-white rounded-md text-[10px] md:text-sm font-bold transition-all shadow-[0_0_10px_rgba(220,38,38,0.3)]">
                HENTIKAN
            </button>
            <button id="btnRun" class="px-3 md:px-6 py-1.5 md:py-2 bg-green-600 hover:bg-green-500 text-white rounded-md text-[10px] md:text-sm font-bold transition-all flex items-center gap-2 shadow-[0_0_15px_rgba(22,163,74,0.4)]">
                <span class="hidden md:inline">▶</span> MULAI <span class="hidden sm:inline">SIMULASI</span>
            </button>
        </div>
    </header>

    <main class="flex-1 flex w-full relative">
        
        <!-- Toolbox (Kiri) -->
        <aside id="toolbox" class="w-72 bg-gray-800 border-r border-gray-700 flex flex-col z-20 shadow-xl relative mobile-hidden md:flex">
            <div class="p-4 border-b border-gray-700 flex justify-between items-center">
                <h2 class="text-sm font-bold text-gray-300 uppercase tracking-wider">Perangkat Keras</h2>
                <button onclick="resetBoard()" class="text-xs text-blue-400 hover:text-blue-300">Reset</button>
            </div>
            <div class="p-4 flex flex-col gap-4 overflow-y-auto">
                <div>
                    <label class="text-xs text-gray-500 font-bold mb-2 block uppercase">Mikrokontroler</label>
                    <div class="flex gap-2">
                        <button onclick="selectBoard('arduino')" id="btnArd" class="flex-1 bg-gray-700 py-2 rounded text-sm hover:bg-gray-600 border border-blue-500 transition-colors">Arduino</button>
                        <button onclick="selectBoard('esp32')" id="btnEsp" class="flex-1 bg-gray-700 py-2 rounded text-sm hover:bg-gray-600 border border-transparent transition-colors">ESP32</button>
                    </div>
                </div>
                <hr class="border-gray-700">
                <div>
                    <label class="text-xs text-gray-500 font-bold mb-2 block uppercase">Modul Eksternal</label>
                    <div class="grid grid-cols-2 gap-2">
                        <button onclick="tambahkanLED()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-red-500">●</span> LED
                        </button>
                        <button onclick="tambahkanServo()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-blue-500">⊚</span> Servo
                        </button>
                        <button onclick="tambahkanUltrasonik()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-green-500">◎◎</span> Ultra
                        </button>
                        <button onclick="tambahkanDHT11()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-cyan-500">▦</span> DHT11
                        </button>
                        <button onclick="tambahkanKipas()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-yellow-500">❃</span> Kipas
                        </button>
                        <button onclick="tambahkanPotensio()" class="bg-gray-700 hover:bg-gray-600 border border-gray-600 border-dashed py-2 rounded-lg text-[11px] font-medium transition-all flex flex-col items-center gap-1 text-gray-300">
                            <span class="text-orange-400">⊘</span> Potensio
                        </button>
                    </div>
                </div>
                <hr class="border-gray-700">
                <div>
                    <label class="text-xs text-gray-500 font-bold mb-2 block uppercase">Projek Robotik</label>
                    <div class="grid grid-cols-1 gap-2">
                        <button onclick="aktifkanMobil()" class="bg-blue-900/30 hover:bg-blue-800/40 border border-blue-500/50 py-3 rounded-lg text-[12px] md:text-[12px] font-bold transition-all flex items-center justify-center gap-3 text-blue-200">
                            <span class="text-xl">🏎️</span> MOBIL 4 RODA (ULTRA)
                        </button>
                        <button onclick="openRideHailingApp()" class="bg-green-900/30 hover:bg-green-800/40 border border-green-500/50 py-3 rounded-lg text-[12px] md:text-[12px] font-bold transition-all flex items-center justify-center gap-3 text-green-200 mt-1">
                            <span class="text-xl">📱</span> APP OJEK ONLINE
                        </button>
                        <button id="btnAddObstacle" onclick="tambahkanRintangan()" class="hidden bg-gray-700 hover:bg-gray-600 border border-gray-600 py-2 rounded-lg text-[11px] font-medium transition-all flex items-center justify-center gap-2 text-gray-300">
                            <span>➕</span> Tambah Rintangan
                        </button>
                    </div>
                </div>
            </div>
        </aside>

        <!-- AREA SIMULASI (Tengah) -->
        <section id="simulationArea" class="flex-1 relative bg-grid overflow-hidden z-0 flex flex-col items-center justify-between py-6 md:py-12">
            
            <!-- LAYER KABEL (SVG) -->
            <svg id="wiringLayer" class="absolute inset-0 w-full h-full pointer-events-none z-10"></svg>

            <!-- BOARD MIKROKONTROLER (Akan disembunyikan jika Robot Aktif) -->
            <div id="boardCanvas" class="relative z-20 transition-all duration-500 shadow-2xl scale-110 mt-4">
                <!-- Board di-render melalui JS -->
            </div>

            <!-- MAP ARENA -->
            <div id="mapArena" class="absolute inset-0 w-full h-full z-40" style="display: none;">
                
                <!-- Map Mode Toggle -->
                <div class="absolute top-4 left-4 z-[2000] flex bg-gray-900/90 rounded-lg p-1 shadow-2xl border border-gray-700">
                    <button onclick="setMapMode('real')" id="btnMapReal" class="px-4 py-1.5 bg-blue-600 text-white rounded text-xs font-bold transition-all">Map Real (GPS)</button>
                    <button onclick="setMapMode('canvas')" id="btnMapCanvas" class="px-4 py-1.5 bg-gray-700 text-gray-400 hover:text-white rounded text-xs font-bold transition-all">Map Ruangan (10 M)</button>
                </div>

                <div id="leafletMap" class="w-full h-full bg-[#0f172a]"></div>
                
                <!-- Canvas Indoor Map -->
                <canvas id="indoorCanvas" class="w-full h-full hidden cursor-crosshair bg-[#0f172a]" style="background-image: radial-gradient(#334155 2px, transparent 2px); background-size: 40px 40px;"></canvas>
                <div id="canvasCoordDisplay" class="absolute bg-gray-900/90 text-green-400 font-mono text-[10px] px-2 py-1 rounded border border-green-500/30 shadow-lg hidden pointer-events-none z-[500] transition-opacity duration-150">X: 0, Y: 0</div>
                
                <!-- Search & Location Controls -->
                <div id="mapSearchControls" class="absolute top-4 right-4 z-[400] flex flex-col gap-2 bg-gray-900/90 p-3 rounded-xl backdrop-blur-lg border border-white/10 shadow-2xl">
                    <div class="flex items-center bg-gray-800 p-1.5 rounded-lg border border-gray-700">
                        <span class="text-[10px] text-blue-400 font-bold w-16 px-1">LOKASI :</span>
                        <input type="text" id="mapOriginInput" placeholder="Lokasi awal (cth: Monas)" class="bg-transparent text-gray-200 text-xs px-2 outline-none w-32 md:w-48" onkeypress="if(event.key === 'Enter') searchOriginOnly()">
                        <button onclick="searchOriginOnly()" class="text-gray-400 hover:text-white px-2 transition-colors border-l border-gray-600 pl-2" title="Cari Lokasi">🔍</button>
                        <button onclick="autoDetectLocation()" class="text-gray-400 hover:text-white px-2 transition-colors border-l border-gray-600 pl-2" title="Deteksi GPS">📍</button>
                    </div>
                    <div class="flex items-center bg-gray-800 p-1.5 rounded-lg border border-gray-700">
                        <span class="text-[10px] text-red-400 font-bold w-16 px-1">TUJUAN :</span>
                        <input type="text" id="mapDestInput" placeholder="Tujuan (cth: Desa Sukamaju)" class="bg-transparent text-gray-200 text-xs px-2 outline-none w-32 md:w-48" onkeypress="if(event.key === 'Enter') calculateTextRoute()">
                    </div>
                    <div class="flex justify-between mt-1">
                        <button id="btnThemeToggle" onclick="toggleMapTheme()" class="bg-gray-700 hover:bg-gray-600 text-white p-1.5 px-3 rounded-lg text-xs transition-all shadow-lg flex items-center justify-center" title="Ganti Mode Terang/Gelap">🌞 TEMA</button>
                        <button onclick="calculateTextRoute()" class="bg-blue-600 hover:bg-blue-500 text-white px-4 py-1.5 rounded-lg text-xs font-bold transition-all shadow-[0_0_10px_rgba(37,99,235,0.4)]">🔍 CARI RUTE</button>
                    </div>
                </div>
                
                <!-- Floating Controls -->
                <div class="absolute bottom-8 left-1/2 -translate-x-1/2 z-[400] flex gap-3 bg-gray-900/80 p-3 rounded-2xl backdrop-blur-lg border border-white/10 shadow-2xl">
                    <button onclick="clearMapRoute()" class="bg-gray-700 hover:bg-red-500 text-white px-4 py-2 rounded-xl text-xs font-bold transition-all flex items-center gap-2">
                        <span class="text-base">🗑️</span> Hapus Rute
                    </button>
                    <button onclick="sendMapRoute()" class="bg-blue-600 hover:bg-blue-500 text-white px-6 py-2 rounded-xl text-xs font-bold transition-all flex items-center gap-2 shadow-[0_0_15px_rgba(37,99,235,0.4)]">
                        <span class="text-base">🚀</span> Kirim Rute ke ESP32
                    </button>
                </div>
            </div>

            <!-- ARENA ROBOT (Centered Box) -->
            <div id="robotArena" class="mb-8 select-none" style="display: none;">
                <svg id="arenaCanvas" class="w-full h-full" onmousedown="startDragging(event)" onmousemove="drag(event)" onmouseup="stopDragging()">
                    <!-- Arena Walls (Tembok Keliling) -->
                    <!-- Top -->
                    <rect x="0" y="0" width="100%" height="20" fill="url(#wallGradient)" stroke="#0f172a" stroke-width="1" />
                    <!-- Bottom -->
                    <rect x="0" y="calc(100% - 20)" width="100%" height="20" fill="url(#wallGradient)" stroke="#0f172a" stroke-width="1" />
                    <!-- Left -->
                    <rect x="0" y="0" width="20" height="100%" fill="url(#wallGradientSide)" stroke="#0f172a" stroke-width="1" />
                    <!-- Right -->
                    <rect x="calc(100% - 20)" y="0" width="20" height="100%" fill="url(#wallGradientSide)" stroke="#0f172a" stroke-width="1" />
                    
                    <!-- Neon Border -->
                    <rect x="20" y="20" width="calc(100% - 40)" height="calc(100% - 40)" class="arena-border" />
                    
                    <defs>
                        <linearGradient id="wallGradient" x1="0%" y1="0%" x2="0%" y2="100%">
                            <stop offset="0%" style="stop-color:#334155" />
                            <stop offset="50%" style="stop-color:#1e293b" />
                            <stop offset="100%" style="stop-color:#334155" />
                        </linearGradient>
                        <linearGradient id="wallGradientSide" x1="0%" y1="0%" x2="100%" y2="0%">
                            <stop offset="0%" style="stop-color:#334155" />
                            <stop offset="50%" style="stop-color:#1e293b" />
                            <stop offset="100%" style="stop-color:#334155" />
                        </linearGradient>
                    </defs>
                    
                    <!-- The Robot Car -->
                    <g id="robotCarContainer" class="robot-car">
                        <!-- Wheels (Yellow) -->
                        <g class="wheel-group" id="wheel-fl">
                            <rect x="-25" y="-35" width="10" height="20" fill="#facc15" rx="2" />
                            <line x1="-20" y1="-35" x2="-20" y2="-15" stroke="#854d0e" stroke-width="2" stroke-dasharray="4,2" class="wheel-line" />
                        </g>
                        <g class="wheel-group" id="wheel-fr">
                            <rect x="15" y="-35" width="10" height="20" fill="#facc15" rx="2" />
                            <line x1="20" y1="-35" x2="20" y2="-15" stroke="#854d0e" stroke-width="2" stroke-dasharray="4,2" class="wheel-line" />
                        </g>
                        <g class="wheel-group" id="wheel-bl">
                            <rect x="-25" y="15" width="10" height="20" fill="#facc15" rx="2" />
                            <line x1="-20" y1="15" x2="-20" y2="35" stroke="#854d0e" stroke-width="2" stroke-dasharray="4,2" class="wheel-line" />
                        </g>
                        <g class="wheel-group" id="wheel-br">
                            <rect x="15" y="15" width="10" height="20" fill="#facc15" rx="2" />
                            <line x1="20" y1="15" x2="20" y2="35" stroke="#854d0e" stroke-width="2" stroke-dasharray="4,2" class="wheel-line" />
                        </g>
                        <!-- Chassis -->
                        <rect x="-20" y="-30" width="40" height="60" fill="#3b82f6" rx="5" />
                        <rect x="-15" y="-25" width="30" height="50" fill="#1d4ed8" rx="3" />
                        <!-- Sensor Eyes -->
                        <circle cx="-10" cy="-30" r="6" fill="#111" stroke="#4b5563" stroke-width="2" />
                        <circle cx="10" cy="-30" r="6" fill="#111" stroke="#4b5563" stroke-width="2" />
                        <!-- Ray Lines -->
                        <line id="rayFront" x1="0" y1="-30" x2="0" y2="-150" stroke="rgba(239, 68, 68, 0.8)" stroke-width="2" stroke-dasharray="5,5" class="glow-sensor" />
                        <line id="rayLeft" x1="-15" y1="0" x2="-100" y2="0" stroke="rgba(59, 130, 246, 0.8)" stroke-width="2" stroke-dasharray="5,5" />
                        <line id="rayRight" x1="15" y1="0" x2="100" y2="0" stroke="rgba(59, 130, 246, 0.8)" stroke-width="2" stroke-dasharray="5,5" />
                    </g>
                </svg>
                <!-- Sensor Dashboard -->
                <button onclick="toggleHUD()" class="hud-toggle-btn mobile-visible hidden">
                    <span id="hudToggleText">HIDE HUD</span>
                </button>
                
                <div id="dashboardPanel" class="dashboard-panel z-30">
                    <!-- Front Gauge -->
                    <div class="flex flex-col items-center gap-1">
                        <div class="gauge-container">
                            <svg class="gauge-svg w-full h-full" viewBox="0 0 36 36">
                                <circle class="gauge-bg" cx="18" cy="18" r="14" />
                                <circle id="gauge-f-bar" class="gauge-bar stroke-red-500" cx="18" cy="18" r="14" stroke-dasharray="0, 100" />
                            </svg>
                            <div class="gauge-text">
                                <span class="text-[8px] text-gray-500 uppercase">Front</span>
                                <span id="stat-f" class="text-[10px] text-red-400">0</span>
                            </div>
                        </div>
                    </div>
                    <!-- Speed Gauge -->
                    <div class="flex flex-col items-center gap-1">
                        <div class="gauge-container">
                            <svg class="gauge-svg w-full h-full" viewBox="0 0 36 36">
                                <circle class="gauge-bg" cx="18" cy="18" r="14" />
                                <circle id="gauge-s-bar" class="gauge-bar stroke-yellow-400" cx="18" cy="18" r="14" stroke-dasharray="0, 100" />
                            </svg>
                            <div class="gauge-text">
                                <span class="text-[8px] text-gray-500 uppercase">Km/h</span>
                                <span id="stat-s" class="text-[10px] text-yellow-400">0</span>
                            </div>
                        </div>
                    </div>
                    <!-- Left Gauge -->
                    <div class="flex flex-col items-center gap-1">
                        <div class="gauge-container">
                            <svg class="gauge-svg w-full h-full" viewBox="0 0 36 36">
                                <circle class="gauge-bg" cx="18" cy="18" r="14" />
                                <circle id="gauge-l-bar" class="gauge-bar stroke-blue-500" cx="18" cy="18" r="14" stroke-dasharray="0, 100" />
                            </svg>
                            <div class="gauge-text">
                                <span class="text-[8px] text-gray-500 uppercase">Left</span>
                                <span id="stat-l" class="text-[10px] text-blue-400">0</span>
                            </div>
                        </div>
                    </div>
                    <!-- Right Gauge -->
                    <div class="flex flex-col items-center gap-1">
                        <div class="gauge-container">
                            <svg class="gauge-svg w-full h-full" viewBox="0 0 36 36">
                                <circle class="gauge-bg" cx="18" cy="18" r="14" />
                                <circle id="gauge-r-bar" class="gauge-bar stroke-blue-500" cx="18" cy="18" r="14" stroke-dasharray="0, 100" />
                            </svg>
                            <div class="gauge-text">
                                <span class="text-[8px] text-gray-500 uppercase">Right</span>
                                <span id="stat-r" class="text-[10px] text-blue-400">0</span>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="absolute top-4 left-4 bg-black/60 backdrop-blur-md border border-white/10 px-3 py-1 rounded-full text-[10px] text-blue-300 font-mono flex items-center gap-2">
                    <span class="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></span>
                    ROBOT VIEWPORT: AKTIF
                </div>
            </div>

            <!-- BOARD MIKROKONTROLER -->
            <div id="boardCanvas" class="relative z-20 transition-all duration-500 shadow-2xl scale-110 mt-4">
                <!-- Board di-render melalui JS -->
            </div>

            <!-- KOMPONEN LED -->
            <div id="componentsCanvas" class="relative z-20 flex gap-8 mb-10">
                <!-- LED akan muncul ke samping dari tengah -->
            </div>

            <div class="absolute bottom-6 left-6 bg-gray-800/80 backdrop-blur px-4 py-2 rounded-lg border border-gray-700 text-sm flex items-center gap-3 z-30">
                <span id="engineStatusIndicator" class="w-2.5 h-2.5 rounded-full bg-gray-500"></span>
                <span id="engineStatusText" class="font-mono text-gray-400">Siap Dieksekusi</span>
            </div>
        </section>

        <!-- Editor (Kanan) -->
        <aside id="editorContainer" class="w-full md:w-[450px] bg-[#1e1e1e] flex flex-col border-l border-gray-700 z-20 shadow-2xl relative mobile-hidden md:flex">
            <div class="flex bg-gray-800 border-b border-gray-700 justify-between items-center pr-4">
                <div class="flex">
                    <div class="px-5 py-2 border-t-2 border-blue-500 bg-[#1e1e1e] text-sm text-gray-200 font-mono">main.ino</div>
                </div>
                <div class="relative group">
                    <button class="text-xs bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded text-gray-300 flex items-center gap-2 border border-gray-600 transition-all">
                        <span>Contoh Kode</span>
                        <span class="text-[10px]">▼</span>
                    </button>
                    <div class="absolute right-0 top-full mt-1 w-48 bg-gray-800 border border-gray-700 rounded-md shadow-2xl opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all z-50 overflow-hidden">
                        <button onclick="loadExample('blink')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">LED - Blink (Kedip)</button>
                        <button onclick="loadExample('servo')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">Servo - Sweep (Berputar)</button>
                        <button onclick="loadExample('ultra')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">Ultra - Radar Jarak</button>
                        <button onclick="loadExample('dht')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">DHT11 - Monitor Suhu</button>
                        <button onclick="loadExample('pot_read')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">Potensio - Baca Nilai</button>
                        <button onclick="loadExample('pot_led')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors">Potensio - Kontrol LED</button>
                        <button onclick="loadExample('fan_auto')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors border-t border-gray-700">Auto Fan (Radar + Kipas)</button>
                        <button onclick="loadExample('robot_basic')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors border-t border-blue-900 bg-blue-900/20 font-bold italic">🏎️ Robot: Dasar Gerak</button>
                        <button onclick="loadExample('robot_avoid')" class="w-full text-left px-4 py-2 text-xs text-gray-300 hover:bg-blue-600 hover:text-white transition-colors bg-blue-900/20 font-bold italic">🏎️ Robot: Hindari Dinding</button>
                        <button onclick="loadExample('iot_basic')" class="w-full text-left px-4 py-2 text-xs text-blue-400 hover:bg-blue-600 hover:text-white transition-colors border-t border-blue-900 bg-blue-900/20 font-bold">☁️ IoT: Kirim Data Cloud</button>
                        <button onclick="loadExample('iot_control')" class="w-full text-left px-4 py-2 text-xs text-green-400 hover:bg-blue-600 hover:text-white transition-colors border-t border-green-900/30 bg-green-900/10 font-bold">☁️ IoT: Kontrol LED Cloud</button>
                    </div>
                </div>
            </div>
            <textarea id="codeEditor" spellcheck="false" class="flex-1 w-full bg-[#1e1e1e] text-blue-300 p-4 md:p-5 font-mono text-sm md:text-sm leading-relaxed resize-none transition-all focus:ring-0">
int led1 = 13;

void setup() {
  Serial.begin(9600);
  pinMode(led1, OUTPUT);
  Serial.println("Sistem Berhasil Dimulai!");
}

void loop() {
  digitalWrite(led1, HIGH);
  Serial.println("Status: LED NYALA");
  delay(1000);
  digitalWrite(led1, LOW);
  Serial.println("Status: LED MATI");
  delay(1000);
}
            </textarea>
            <div class="h-48 border-t border-gray-700 bg-[#181818] flex flex-col">
                <div class="px-4 py-2 bg-gray-800 text-xs font-bold text-gray-400 uppercase tracking-widest flex justify-between items-center">
                    <span>Serial Monitor</span>
                    <button onclick="clearSerial()" class="text-gray-500 hover:text-white transition-colors">Bersihkan</button>
                </div>
                <div id="serialOutput" class="p-4 font-mono text-[11px] text-green-500 terminal-glow overflow-y-auto flex-1 whitespace-pre-wrap select-text bg-black/40"></div>
            </div>
        </aside>

        <!-- Bottom Navigation (Mobile Only) -->
        <nav class="md:hidden fixed bottom-0 left-0 right-0 bg-gray-800 border-t border-gray-700 flex justify-around items-center h-16 z-50">
            <button onclick="switchView('hardware')" id="tab-hardware" class="flex-1 h-full flex flex-col items-center justify-center gap-1 text-gray-400">
                <span class="text-xl">🛠️</span>
                <span class="text-[10px] font-bold uppercase">Hardware</span>
            </button>
            <button onclick="switchView('simulation')" id="tab-simulation" class="flex-1 h-full flex flex-col items-center justify-center gap-1 tab-active">
                <span class="text-xl">🤖</span>
                <span class="text-[10px] font-bold uppercase">Simulasi</span>
            </button>
            <button onclick="switchView('code')" id="tab-code" class="flex-1 h-full flex flex-col items-center justify-center gap-1 text-gray-400">
                <span class="text-xl">💻</span>
                <span class="text-[10px] font-bold uppercase">Kode</span>
            </button>
        </nav>
    </main>

    <script>
        let isRunning = false;
        let activeView = 'simulation'; // hardware, simulation, code

        function switchView(view) {
            if (window.innerWidth > 768) return;
            activeView = view;
            
            const toolbox = document.getElementById('toolbox');
            const simArea = document.getElementById('simulationArea');
            const editor = document.getElementById('editorContainer');
            
            const tabHardware = document.getElementById('tab-hardware');
            const tabSim = document.getElementById('tab-simulation');
            const tabCode = document.getElementById('tab-code');

            // Reset views
            toolbox.classList.add('mobile-hidden');
            simArea.classList.add('mobile-hidden');
            editor.classList.add('mobile-hidden');
            
            // Reset tabs
            [tabHardware, tabSim, tabCode].forEach(t => t.classList.remove('tab-active'));
            [tabHardware, tabSim, tabCode].forEach(t => t.classList.add('text-gray-400'));

            if (view === 'hardware') {
                toolbox.classList.remove('mobile-hidden');
                tabHardware.classList.add('tab-active');
                tabHardware.classList.remove('text-gray-400');
            } else if (view === 'simulation') {
                simArea.classList.remove('mobile-hidden');
                tabSim.classList.add('tab-active');
                tabSim.classList.remove('text-gray-400');
                setTimeout(drawWires, 50); // Redraw wires as dimensions might have changed
            } else if (view === 'code') {
                editor.classList.remove('mobile-hidden');
                tabCode.classList.add('tab-active');
                tabCode.classList.remove('text-gray-400');
            }
        }

        function toggleHUD() {
            const panel = document.getElementById('dashboardPanel');
            const text = document.getElementById('hudToggleText');
            const isMinimized = panel.classList.toggle('dashboard-minimized');
            text.innerText = isMinimized ? 'SHOW HUD' : 'HIDE HUD';
        }

        // Handle window resize to reset views if going back to desktop
        window.addEventListener('resize', () => {
            if (window.innerWidth > 768) {
                document.getElementById('toolbox').classList.remove('mobile-hidden');
                document.getElementById('simulationArea').classList.remove('mobile-hidden');
                document.getElementById('editorContainer').classList.remove('mobile-hidden');
            } else {
                switchView(activeView);
            }
            drawWires();
        });
        let virtualPins = {}; 
        let registeredComponents = {}; 
        let componentCounter = 0;
        const wireColors = ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4'];
        
        // Constants for Simulation
        const OUTPUT = 'OUTPUT', INPUT = 'INPUT', INPUT_PULLUP = 'INPUT_PULLUP', HIGH = 1, LOW = 0;
        const A0 = 'A0', A1 = 'A1', A2 = 'A2', A3 = 'A3', A4 = 'A4', A5 = 'A5';
        
        const BOARD_CONFIGS = {
            arduino: {
                name: "Arduino Uno",
                pins: {
                    digital: [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
                    analog: ['A0', 'A1', 'A2', 'A3', 'A4', 'A5']
                },
                html: `
                    <div class="w-80 h-56 bg-[#006468] rounded-xl border-4 border-[#004b4e] flex flex-col justify-between p-4 relative shadow-2xl">
                        <!-- Digital Pins (Top) -->
                        <div id="anchor-digital" class="absolute -top-1 right-8 w-44 h-3 bg-black flex justify-between px-2 rounded-b-sm border-x-2 border-gray-600">
                            ${Array(12).fill('<div class="w-1.5 h-full bg-gray-400"></div>').join('')}
                        </div>
                        <!-- Analog Pins (Bottom) -->
                        <div id="anchor-analog" class="absolute -bottom-1 right-12 w-24 h-3 bg-black flex justify-between px-2 rounded-t-sm border-x-2 border-gray-600">
                            ${Array(6).fill('<div class="w-1.5 h-full bg-gray-400"></div>').join('')}
                        </div>
                        <!-- GND (Top Left) -->
                        <div id="anchor-gnd" class="absolute -top-1 left-16 w-6 h-3 bg-black flex justify-center rounded-b-sm border-x-2 border-gray-600">
                            <div class="w-1.5 h-full bg-gray-400"></div>
                        </div>
                        
                        <div class="flex justify-between items-start">
                            <div class="text-[10px] text-white/40 font-mono">POWER</div>
                            <div class="text-[10px] text-white/40 font-mono">DIGITAL (PWM ~)</div>
                        </div>
                        <div class="flex-1 flex items-center justify-center">
                            <div class="w-40 h-12 bg-gray-900 rounded border border-gray-700 flex items-center justify-center text-[10px] text-gray-500 font-mono shadow-inner">ATMEGA328P-PU</div>
                        </div>
                        <div class="flex justify-between items-end">
                            <div class="text-xs text-white/70 font-bold font-mono tracking-widest">UNO R3</div>
                            <div class="text-[10px] text-white/40 font-mono">ANALOG IN</div>
                        </div>
                    </div>`
            },
            esp32: {
                name: "ESP32 DevKit",
                pins: {
                    digital: [2, 4, 5, 12, 14, 27, 26, 25, 33, 32],
                    analog: [34, 35, 36, 39]
                },
                html: `
                    <div class="w-44 h-64 bg-[#1a1a1a] rounded-xl border-2 border-gray-700 flex flex-col justify-between p-2 relative shadow-2xl">
                        <div id="anchor-digital" class="absolute top-12 right-0 w-2 h-32 bg-black flex flex-col justify-between py-1">
                            ${Array(8).fill('<div class="w-2 h-1 bg-gray-400"></div>').join('')}
                        </div>
                        <div id="anchor-analog" class="absolute bottom-4 right-0 w-2 h-10 bg-black flex flex-col justify-between py-1">
                            ${Array(4).fill('<div class="w-2 h-1 bg-gray-400"></div>').join('')}
                        </div>
                        <div id="anchor-gnd" class="absolute bottom-12 left-0 w-2 h-4 bg-black flex items-center">
                            <div class="w-2 h-1 bg-gray-400"></div>
                        </div>
                        <div class="w-28 h-32 bg-gray-300 mx-auto mt-6 rounded-md border-2 border-gray-400 flex items-center justify-center font-bold text-gray-800 text-[10px] text-center px-2">ESP-WROOM-32</div>
                        <div class="text-center text-xs text-gray-400 font-bold mb-2 uppercase tracking-widest">ESP32 DevKit</div>
                    </div>`
            }
        };

        let currentBoard = 'arduino';
        let currentPinIndex = 0;

        const UI = {
            boardCanvas: document.getElementById('boardCanvas'),
            componentsCanvas: document.getElementById('componentsCanvas'),
            wiringLayer: document.getElementById('wiringLayer'),
            simulationArea: document.getElementById('simulationArea'),
            serial: document.getElementById('serialOutput'),
            btnRun: document.getElementById('btnRun'),
            btnStop: document.getElementById('btnStop'),
            btnArd: document.getElementById('btnArd'),
            btnEsp: document.getElementById('btnEsp'),
            indicatorBox: document.getElementById('engineStatusIndicator'),
            indicatorText: document.getElementById('engineStatusText')
        };

        function selectBoard(boardType) {
            showHardwareView();
            exitRobotMode();
            currentBoard = boardType;
            UI.boardCanvas.innerHTML = BOARD_CONFIGS[boardType].html;
            if(boardType === 'arduino') {
                UI.btnArd.classList.add('border-blue-500'); UI.btnArd.classList.remove('border-transparent');
                UI.btnEsp.classList.remove('border-blue-500'); UI.btnEsp.classList.add('border-transparent');
            } else {
                UI.btnEsp.classList.add('border-blue-500'); UI.btnEsp.classList.remove('border-transparent');
                UI.btnArd.classList.remove('border-blue-500'); UI.btnArd.classList.add('border-transparent');
            }
            resetBoard(); 
        }

        function resetBoard() {
            showHardwareView();
            exitRobotMode();
            UI.componentsCanvas.innerHTML = '';
            UI.wiringLayer.innerHTML = ''; 
            registeredComponents = {};
            componentCounter = 0;
            currentPinIndex = 0;
            virtualPins = {};
            
            if (robotCar.isActive) {
                const arena = document.getElementById('robotArena');
                const rect = arena.getBoundingClientRect();
                robotCar.reset(rect.width, rect.height);
            }
            
            if(isRunning) stopSimulation();
        }

        function generatePinOptions(currentPin) {
            const config = BOARD_CONFIGS[currentBoard].pins;
            let html = '';
            
            if (config.digital) {
                html += `<optgroup label="Digital (PWM)">`;
                config.digital.forEach(p => {
                    html += `<option value="${p}" ${p == currentPin ? 'selected' : ''}>Pin ${p}</option>`;
                });
                html += `</optgroup>`;
            }
            
            if (config.analog) {
                html += `<optgroup label="Analog IN">`;
                config.analog.forEach(p => {
                    html += `<option value="${p}" ${p == currentPin ? 'selected' : ''}>Pin ${p}</option>`;
                });
                html += `</optgroup>`;
            }
            
            return html;
        }
        function updateComponentPin(id, newPin) {
            if (registeredComponents[id]) {
                registeredComponents[id].pin = parseInt(newPin);
                drawWires();
            }
        }

        function tambahkanPotensio() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTarget = config.analog ? config.analog[0] : config.digital[0];
            const id = ++componentCounter;

            const node = document.createElement('div');
            node.id = `comp-container-${id}`;
            node.className = 'flex flex-col items-center bg-gray-800 p-3 rounded-lg border border-gray-700 relative w-32 group';
            node.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 text-white rounded-full text-[10px] z-30">×</button>
                <div id="cathode-${id}" class="absolute -top-1 left-4 w-1 h-1"></div>
                <div id="anode-${id}" class="absolute -top-1 right-4 w-1 h-1"></div>
                <div class="w-12 h-12 bg-gray-700 rounded-full border-4 border-gray-600 flex items-center justify-center mb-2 relative overflow-hidden">
                    <div id="knob-${id}" class="w-1 h-6 bg-orange-400 rounded-full transition-transform duration-75" style="transform: rotate(0deg) translateY(-8px)"></div>
                </div>
                <input type="range" id="val-${id}" min="0" max="1023" value="512" class="w-full mb-1">
                <select onchange="updateComponentPin(${id}, this.value)" class="bg-gray-700 text-[10px] text-white rounded border border-gray-600 px-1 py-0.5 w-full outline-none">
                    ${generatePinOptions(pinTarget)}
                </select>
            `;
            UI.componentsCanvas.appendChild(node);
            const slider = document.getElementById(`val-${id}`);
            const knob = document.getElementById(`knob-${id}`);
            slider.oninput = () => knob.style.transform = `rotate(${(slider.value/1023)*270 - 135}deg) translateY(-8px)`;
            
            registeredComponents[id] = { id, type: 'pot', pin: pinTarget, slider: slider, anodeId: `anode-${id}`, cathodeId: `cathode-${id}`, color: '#f97316' };
            drawWires();
        }

        function tambahkanLED() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTarget = config.digital[currentPinIndex % config.digital.length];
            const wireColor = wireColors[currentPinIndex % wireColors.length];
            const id = ++componentCounter;
            currentPinIndex++;

            const ledNode = document.createElement('div');
            ledNode.id = `comp-container-${id}`;
            ledNode.className = 'flex flex-col items-center bg-gray-800/95 p-3 rounded-lg border border-gray-600 shadow-lg relative w-24 group transition-all hover:border-blue-500/50';
            ledNode.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 hover:bg-red-500 text-white rounded-full text-[10px] flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity z-30 shadow-lg">×</button>
                <div id="anode-${id}" class="absolute -top-1 right-3 w-1 h-1"></div>
                <div id="cathode-${id}" class="absolute -top-1 left-3 w-1 h-1"></div>
                <div class="mb-2 mt-2">
                    <div id="dom-${id}" class="w-8 h-8 rounded-full border-2 led-off transition-all duration-75 relative z-10"></div>
                </div>
                <select onchange="updateComponentPin(${id}, this.value)" class="bg-gray-700 text-[10px] text-white rounded border border-gray-600 px-1 py-0.5 w-full outline-none">
                    ${generatePinOptions(pinTarget)}
                </select>
            `;
            UI.componentsCanvas.appendChild(ledNode);
            registeredComponents[id] = { id, type: 'led', pin: pinTarget, dom: document.getElementById(`dom-${id}`), anodeId: `anode-${id}`, cathodeId: `cathode-${id}`, color: wireColor };
            drawWires();
        }

        function tambahkanServo() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTarget = config.digital[currentPinIndex % config.digital.length];
            const wireColor = wireColors[currentPinIndex % wireColors.length];
            const id = ++componentCounter;
            currentPinIndex++;

            const node = document.createElement('div');
            node.id = `comp-container-${id}`;
            node.className = 'flex flex-col items-center bg-gray-800 p-3 rounded-lg border border-gray-700 relative w-24 group';
            node.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 text-white rounded-full text-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-30">×</button>
                <div id="cathode-${id}" class="absolute -top-1 left-4 w-1 h-1"></div>
                <div id="anode-${id}" class="absolute -top-1 right-4 w-1 h-1"></div>
                <div class="w-16 h-10 bg-blue-600 rounded-sm relative flex items-center justify-center mb-2">
                    <div id="horn-${id}" class="w-12 h-2 bg-gray-200 rounded-full servo-horn shadow-md"></div>
                </div>
                <select onchange="updateComponentPin(${id}, this.value)" class="bg-gray-700 text-[10px] text-white rounded border border-gray-600 px-1 py-0.5 w-full outline-none">
                    ${generatePinOptions(pinTarget)}
                </select>
            `;
            UI.componentsCanvas.appendChild(node);
            registeredComponents[id] = { id, type: 'servo', pin: pinTarget, horn: document.getElementById(`horn-${id}`), anodeId: `anode-${id}`, cathodeId: `cathode-${id}`, color: wireColor };
            drawWires();
        }

        function tambahkanKipas() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTarget = config.digital[currentPinIndex % config.digital.length];
            const wireColor = wireColors[currentPinIndex % wireColors.length];
            const id = ++componentCounter;
            currentPinIndex++;

            const node = document.createElement('div');
            node.id = `comp-container-${id}`;
            node.className = 'flex flex-col items-center bg-gray-800 p-3 rounded-lg border border-gray-700 relative w-24 group';
            node.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 text-white rounded-full text-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-30">×</button>
                <div id="cathode-${id}" class="absolute -top-1 left-4 w-1 h-1"></div>
                <div id="anode-${id}" class="absolute -top-1 right-4 w-1 h-1"></div>
                <div class="w-14 h-14 bg-gray-700 rounded-full relative flex items-center justify-center mb-2 border-2 border-gray-600 overflow-hidden">
                    <div id="blade-${id}" class="w-12 h-12 flex items-center justify-center relative">
                        <div class="absolute w-12 h-2 bg-gray-400 rounded-full"></div>
                        <div class="absolute w-2 h-12 bg-gray-400 rounded-full"></div>
                    </div>
                </div>
                <select onchange="updateComponentPin(${id}, this.value)" class="bg-gray-700 text-[10px] text-white rounded border border-gray-600 px-1 py-0.5 w-full outline-none">
                    ${generatePinOptions(pinTarget)}
                </select>
            `;
            UI.componentsCanvas.appendChild(node);
            registeredComponents[id] = { id, type: 'fan', pin: pinTarget, blade: document.getElementById(`blade-${id}`), anodeId: `anode-${id}`, cathodeId: `cathode-${id}`, color: wireColor };
            drawWires();
        }

        function tambahkanUltrasonik() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTrig = config.digital[currentPinIndex % config.digital.length];
            const pinEcho = config.digital[(currentPinIndex + 1) % config.digital.length];
            const wireColor1 = wireColors[currentPinIndex % wireColors.length];
            const wireColor2 = wireColors[(currentPinIndex + 1) % wireColors.length];
            const id = ++componentCounter;
            currentPinIndex += 2;

            const node = document.createElement('div');
            node.id = `comp-container-${id}`;
            node.className = 'flex flex-col items-center bg-gray-800 p-3 rounded-lg border border-gray-700 relative w-40 group';
            node.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 text-white rounded-full text-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-30">×</button>
                <div id="cathode-${id}" class="absolute -top-1 left-4 w-1 h-1"></div>
                <div id="anode-trig-${id}" class="absolute -top-1 left-1/3 w-1 h-1"></div>
                <div id="anode-echo-${id}" class="absolute -top-1 right-1/3 w-1 h-1"></div>
                
                <div class="flex gap-2 mb-2">
                    <div class="w-10 h-10 rounded-full border-4 border-gray-600 bg-gray-900 flex items-center justify-center shadow-inner"><div class="w-4 h-4 bg-gray-800 rounded-full border border-gray-600"></div></div>
                    <div class="w-10 h-10 rounded-full border-4 border-gray-600 bg-gray-900 flex items-center justify-center shadow-inner"><div class="w-4 h-4 bg-gray-800 rounded-full border border-gray-600"></div></div>
                </div>
                
                <div class="w-full px-1 mb-2">
                    <input type="range" id="val-${id}" min="2" max="400" value="100" class="w-full">
                    <div class="flex justify-between text-[8px] text-gray-500 mt-1"><span>2cm</span><span id="txt-${id}">100cm</span><span>400cm</span></div>
                </div>

                <div class="grid grid-cols-2 gap-1 w-full">
                    <div class="flex flex-col">
                        <label class="text-[7px] text-gray-500 uppercase font-bold ml-1">Trig</label>
                        <select onchange="updateComponentMultiPin(${id}, 'trig', this.value)" class="bg-gray-700 text-[9px] text-white rounded border border-gray-600 px-1 py-0.5 outline-none">
                            ${generatePinOptions(pinTrig)}
                        </select>
                    </div>
                    <div class="flex flex-col">
                        <label class="text-[7px] text-gray-500 uppercase font-bold ml-1">Echo</label>
                        <select onchange="updateComponentMultiPin(${id}, 'echo', this.value)" class="bg-gray-700 text-[9px] text-white rounded border border-gray-600 px-1 py-0.5 outline-none">
                            ${generatePinOptions(pinEcho)}
                        </select>
                    </div>
                </div>
            `;
            UI.componentsCanvas.appendChild(node);
            const slider = document.getElementById(`val-${id}`);
            const text = document.getElementById(`txt-${id}`);
            slider.oninput = () => text.innerText = slider.value + 'cm';
            
            registeredComponents[id] = { 
                id, type: 'ultra', 
                trigPin: pinTrig, echoPin: pinEcho,
                slider: slider, 
                cathodeId: `cathode-${id}`,
                multiAnodes: [
                    { id: `anode-trig-${id}`, pinKey: 'trigPin', color: wireColor1 },
                    { id: `anode-echo-${id}`, pinKey: 'echoPin', color: wireColor2 }
                ]
            };
            drawWires();
        }

        function updateComponentMultiPin(id, key, newPin) {
            if (registeredComponents[id]) {
                if(key === 'trig') registeredComponents[id].trigPin = parseInt(newPin);
                if(key === 'echo') registeredComponents[id].echoPin = parseInt(newPin);
                drawWires();
            }
        }

        function tambahkanDHT11() {
            showHardwareView();
            exitRobotMode();
            const config = BOARD_CONFIGS[currentBoard].pins;
            const pinTarget = config.digital[currentPinIndex % config.digital.length];
            const wireColor = wireColors[currentPinIndex % wireColors.length];
            const id = ++componentCounter;
            currentPinIndex++;

            const node = document.createElement('div');
            node.id = `comp-container-${id}`;
            node.className = 'flex flex-col items-center bg-gray-800 p-3 rounded-lg border border-gray-700 relative w-32 group';
            node.innerHTML = `
                <button onclick="hapusComponent(${id})" class="absolute -top-2 -right-2 w-5 h-5 bg-red-600 text-white rounded-full text-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-30">×</button>
                <div id="cathode-${id}" class="absolute -top-1 left-4 w-1 h-1"></div>
                <div id="anode-${id}" class="absolute -top-1 right-4 w-1 h-1"></div>
                <div class="w-10 h-12 bg-cyan-600 rounded flex flex-wrap p-1 gap-0.5 mb-2">
                    ${Array(12).fill('<div class="w-2 h-2 bg-cyan-800/50 rounded-sm"></div>').join('')}
                </div>
                <div class="w-full space-y-1 mb-2">
                    <div class="flex justify-between text-[8px] text-cyan-400"><span>Temp</span><span id="t-txt-${id}">25°C</span></div>
                    <input type="range" id="t-${id}" min="0" max="50" value="25" class="w-full">
                </div>
                <select onchange="updateComponentPin(${id}, this.value)" class="bg-gray-700 text-[10px] text-white rounded border border-gray-600 px-1 py-0.5 w-full outline-none">
                    ${generatePinOptions(pinTarget)}
                </select>
            `;
            UI.componentsCanvas.appendChild(node);
            const sliderT = document.getElementById(`t-${id}`);
            const textT = document.getElementById(`t-txt-${id}`);
            sliderT.oninput = () => textT.innerText = sliderT.value + '°C';
            
            registeredComponents[id] = { id, type: 'dht', pin: pinTarget, sliderT: sliderT, anodeId: `anode-${id}`, cathodeId: `cathode-${id}`, color: wireColor };
            drawWires();
        }

        function hapusComponent(id) {
            const node = document.getElementById(`comp-container-${id}`);
            if (node) { node.remove(); delete registeredComponents[id]; drawWires(); }
        }

        // --- SISTEM KABEL LURUS (POINT TO POINT) ---
        function drawWires() {
            UI.wiringLayer.innerHTML = ''; 
            const areaRect = UI.simulationArea.getBoundingClientRect();
            const boardAnchorDigital = document.getElementById('anchor-digital');
            const boardAnchorAnalog = document.getElementById('anchor-analog');
            const boardAnchorGnd = document.getElementById('anchor-gnd');
            
            if (!boardAnchorDigital || !boardAnchorGnd) return;

            const gRect = boardAnchorGnd.getBoundingClientRect();
            const startX_Gnd = gRect.left - areaRect.left + (gRect.width / 2);
            const startY_Gnd = gRect.top - areaRect.top + (gRect.height / 2);

            Object.values(registeredComponents).forEach((comp) => {
                const cathodeEl = document.getElementById(comp.cathodeId);
                if (!cathodeEl) return;

                const cRect = cathodeEl.getBoundingClientRect();
                const endX_Gnd = cRect.left - areaRect.left + 2;
                const endY_Gnd = cRect.top - areaRect.top;
                const cpY_Offset = 80;

                const drawPath = (d, color, id, isGnd) => {
                    const filterId = `shadow-${id || Math.random().toString(36).substr(2, 9)}`;
                    const filter = document.createElementNS("http://www.w3.org/2000/svg", "filter");
                    filter.setAttribute("id", filterId);
                    filter.innerHTML = `<feDropShadow dx="0" dy="2" stdDeviation="2" flood-opacity="0.3"/>`;
                    UI.wiringLayer.appendChild(filter);

                    const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
                    path.setAttribute("d", d);
                    path.setAttribute("fill", "transparent");
                    path.setAttribute("stroke", color);
                    path.setAttribute("stroke-width", isGnd ? "2.5" : "3.5");
                    path.setAttribute("stroke-linecap", "round");
                    path.setAttribute("filter", `url(#${filterId})`);
                    if(isGnd) path.setAttribute("stroke-dasharray", "8, 4");
                    if(id) path.setAttribute("id", id);
                    UI.wiringLayer.appendChild(path);
                };

                const drawDot = (cx, cy, color) => {
                    const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
                    dot.setAttribute("cx", cx); dot.setAttribute("cy", cy);
                    dot.setAttribute("r", "3.5"); dot.setAttribute("fill", color);
                    UI.wiringLayer.appendChild(dot);
                };

                // Draw GND
                const routeGnd = `M ${startX_Gnd} ${startY_Gnd} Q ${(startX_Gnd + endX_Gnd) / 2} ${Math.min(startY_Gnd, endY_Gnd) - (cpY_Offset - 20)} ${endX_Gnd} ${endY_Gnd}`;
                drawPath(routeGnd, "#4b5563", null, true); 
                drawDot(endX_Gnd, endY_Gnd, "#4b5563");

                // Draw Data Pins
                const drawDataWire = (targetId, color, wireId, pin) => {
                    const el = document.getElementById(targetId);
                    if (!el) return;

                    // Determine which board anchor to use
                    const isAnalog = typeof pin === 'string' && pin.startsWith('A');
                    const anchor = isAnalog && boardAnchorAnalog ? boardAnchorAnalog : boardAnchorDigital;
                    const aRect = anchor.getBoundingClientRect();
                    const sx = aRect.left - areaRect.left + (aRect.width / 2);
                    const sy = aRect.top - areaRect.top + (aRect.height / 2);

                    const r = el.getBoundingClientRect();
                    const ex = r.left - areaRect.left + 2;
                    const ey = r.top - areaRect.top;
                    const route = `M ${sx} ${sy} Q ${(sx + ex) / 2} ${Math.min(sy, ey) - cpY_Offset} ${ex} ${ey}`;
                    drawPath(route, color, wireId, false);
                    drawDot(ex, ey, color);
                };

                if (comp.multiAnodes) {
                    comp.multiAnodes.forEach((anode, idx) => {
                        const p = comp[anode.pinKey];
                        drawDataWire(anode.id, anode.color, `wire-data-${comp.id}-${idx}`, p);
                    });
                } else if (comp.anodeId) {
                    drawDataWire(comp.anodeId, comp.color, `wire-data-${comp.id}`, comp.pin);
                }
            });
        }

        window.addEventListener('resize', drawWires);

        const Serial = {
            begin: (baud) => printSerial(`<span class="text-blue-400">[Serial] Komunikasi dimulai pada ${baud} baud.</span>`),
            print: (val) => printSerial(val.toString()),
            println: (val) => printSerial(val.toString())
        };

        function pinMode(pin, mode) { virtualPins[pin] = { mode: mode, state: LOW }; }
        function digitalWrite(pin, state) {
            // Auto-initialize pin if pinMode wasn't called
            if (!virtualPins[pin]) {
                virtualPins[pin] = { mode: OUTPUT, state: LOW };
            }
            virtualPins[pin].state = state;
            updateVisuals(pin, state);
        }
        const delay = ms => new Promise(res => setTimeout(res, ms));

        function printSerial(text) {
            const time = new Date().toLocaleTimeString('id-ID', { hour12: false });
            UI.serial.innerHTML += `<div class="mb-1"><span class="text-gray-600">[${time}]</span> ${text}</div>`;
            UI.serial.scrollTop = UI.serial.scrollHeight;
        }
        function clearSerial() { UI.serial.innerHTML = ''; }

        function updateVisuals(pin, state) {
            const components = Object.values(registeredComponents).filter(c => c.pin == pin || (c.trigPin == pin) || (c.echoPin == pin));
            components.forEach(obj => {
                const wire = document.getElementById(`wire-data-${obj.id}`);
                // Support PWM (analogWrite)
                const brightness = (state > 1) ? state / 255 : (state == 1 ? 1 : 0);
                
                if (state > 0) {
                    if(wire) {
                        wire.classList.add('wire-active'); 
                        wire.style.strokeWidth = state > 1 ? (2 + (state/255)*3) : "3.5";
                    }
                    if (obj.dom) {
                        obj.dom.classList.replace('led-off', 'led-on');
                        obj.dom.style.opacity = state > 1 ? brightness : 1;
                        obj.dom.style.boxShadow = state > 1 ? `0 0 ${20 * brightness}px ${5 * brightness}px rgba(239, 68, 68, 0.8)` : '';
                    }
                    if (obj.blade) {
                        obj.blade.classList.add('fan-blade-active');
                        const speed = state > 1 ? (0.5 - (state/255)*0.4) : 0.2;
                        obj.blade.style.animationDuration = `${speed}s`;
                    }
                } else {
                    if(wire) wire.classList.remove('wire-active');
                    if (obj.dom) {
                        obj.dom.classList.replace('led-on', 'led-off');
                        obj.dom.style.opacity = 1;
                        obj.dom.style.boxShadow = '';
                    }
                    if (obj.blade) obj.blade.classList.remove('fan-blade-active');
                }
            });
        }

        class Servo {
            constructor() {}
            attach(pin) { this.pin = pin; }
            write(angle) {
                const components = Object.values(registeredComponents).filter(c => c.pin == this.pin && c.type === 'servo');
                components.forEach(obj => {
                    obj.horn.style.transform = `rotate(${angle}deg)`;
                });
            }
        }

        function pulseIn(pin, state) {
            const obj = Object.values(registeredComponents).find(c => c.echoPin == pin && c.type === 'ultra');
            if (obj) { return (obj.slider.value / 0.034) * 2; }
            
            // Car Sensor Support (Front)
            if (robotCar.isActive && (pin == robotCar.echoPin)) {
                return (robotCar.sensors.front / 0.034) * 2;
            }
            return 0;
        }

        // New Helper Functions for Car
        function bacaJarakKiri() { return robotCar.sensors.left; }
        function bacaJarakKanan() { return robotCar.sensors.right; }
        function bacaJarakDepan() { return robotCar.sensors.front; }

        // --- ROBOT CAR ENGINE ---
        class RobotCar {
            constructor() {
                this.isActive = false;
                this.x = 0; this.y = 0; this.angle = 0;
                this.speedL = 0; this.speedR = 0;
                this.currentMove = 0; this.currentRotate = 0;
                this.sensors = { front: 400, left: 400, right: 400 };
                this.trigPin = 13; this.echoPin = 12;
                this.motorPins = [5, 6, 9, 10]; // L+, L-, R+, R-
                
                // Realistic Physics Params
                this.mass = 1.0;
                this.friction = 0.85; 
                this.acceleration = 0.6;
                this.turnMomentum = 0.8;
            }

            reset(containerWidth, containerHeight) {
                this.x = containerWidth / 2;
                this.y = containerHeight - 100; // Start at the bottom
                this.angle = 0;
                this.speedL = 0; this.speedR = 0;
                this.currentMove = 0; this.currentRotate = 0;
                this.updateVisual();
            }

            updatePhysics() {
                if (!this.isActive || !isRunning) return;

                // Motor Logic based on pins (Normalizing 0-1 and 0-255)
                const getVal = (p) => {
                    const s = virtualPins[p]?.state || 0;
                    return s > 1 ? s / 255 : (s == 1 ? 1 : 0);
                };

                let lPlus = getVal(5);
                let lMinus = getVal(6);
                let rPlus = getVal(9);
                let rMinus = getVal(10);

                this.speedL = lPlus - lMinus;
                this.speedR = rPlus - rMinus;

                const maxSpeed = 8.0;
                const maxTurn = 4.0;

                // Physics with Acceleration & Friction
                let targetMove = (this.speedL + this.speedR) / 2 * maxSpeed;
                let targetRotate = (this.speedL - this.speedR) * maxTurn;
                
                // Momentum Logic
                if (Math.abs(targetMove) > 0.01) {
                    this.currentMove += (targetMove - this.currentMove) * (this.acceleration / this.mass);
                } else {
                    this.currentMove *= this.friction; // Apply friction when stopped
                }

                if (Math.abs(targetRotate) > 0.01) {
                    this.currentRotate += (targetRotate - this.currentRotate) * (this.turnMomentum / this.mass);
                } else {
                    this.currentRotate *= this.friction;
                }

                this.angle += this.currentRotate;
                this.x += Math.sin(this.angle * Math.PI / 180) * this.currentMove;
                this.y -= Math.cos(this.angle * Math.PI / 180) * this.currentMove;

                // Update wheel animations
                const wheels = document.querySelectorAll('.wheel-line');
                wheels.forEach(w => {
                    if (Math.abs(this.currentMove) > 0.1 || Math.abs(this.currentRotate) > 0.1) {
                        w.classList.add('wheel-spinning');
                        w.style.animationDuration = `${0.5 / (Math.abs(this.currentMove) + 0.1)}s`;
                    } else {
                        w.classList.remove('wheel-spinning');
                    }
                });

                // Simple collision detection (Arena Walls - Tembok 20px)
                const arena = document.getElementById('robotArena').getBoundingClientRect();
                const wallW = 20;
                const carPadding = 25;
                const limit = wallW + carPadding;
                
                if (this.x < limit) { this.x = limit; this.currentMove = 0; }
                if (this.x > arena.width - limit) { this.x = arena.width - limit; this.currentMove = 0; }
                if (this.y < limit) { this.y = limit; this.currentMove = 0; }
                if (this.y > arena.height - limit) { this.y = arena.height - limit; this.currentMove = 0; }

                // Obstacle collision detection
                const obstacles = document.querySelectorAll('.arena-wall');
                obstacles.forEach(obs => {
                    const r = obs.getBBox();
                    // Basic AABB collision with car padding
                    if (this.x > r.x - 20 && this.x < r.x + r.width + 20 &&
                        this.y > r.y - 20 && this.y < r.y + r.height + 20) {
                        // Push back
                        this.x -= Math.sin(this.angle * Math.PI / 180) * this.currentMove * 1.5;
                        this.y += Math.cos(this.angle * Math.PI / 180) * this.currentMove * 1.5;
                        this.currentMove = 0;
                    }
                });

                this.updateSensor(arena);
                this.updateVisual();
            }

            updateSensor(arena) {
                const getDistance = (angleOffset) => {
                    const rad = (this.angle + angleOffset) * Math.PI / 180;
                    const dirX = Math.sin(rad);
                    const dirY = -Math.cos(rad);
                    
                    let tMin = 1000;
                    const walls = [
                        { nx: 0, ny: 1, d: 20 },           // Top Wall
                        { nx: 0, ny: -1, d: arena.height - 20 }, // Bottom Wall
                        { nx: 1, ny: 0, d: 20 },           // Left Wall
                        { nx: -1, ny: 0, d: arena.width - 20 }  // Right Wall
                    ];

                    walls.forEach(w => {
                        const denom = dirX * w.nx + dirY * w.ny;
                        if (Math.abs(denom) > 0.001) {
                            const t = (w.d - (this.x * w.nx + this.y * w.ny)) / denom;
                            if (t > 0 && t < tMin) tMin = t;
                        }
                    });
                    
                    // Also check obstacles if any exist
                    const obstacles = document.querySelectorAll('.arena-wall');
                    obstacles.forEach(obs => {
                        const r = obs.getBBox();
                        const rectWalls = [
                            { x1: r.x, y1: r.y, x2: r.x + r.width, y2: r.y },
                            { x1: r.x, y1: r.y + r.height, x2: r.x + r.width, y2: r.y + r.height },
                            { x1: r.x, y1: r.y, x2: r.x, y2: r.y + r.height },
                            { x1: r.x + r.width, y1: r.y, x2: r.x + r.width, y2: r.y + r.height }
                        ];
                        rectWalls.forEach(wall => {
                            const x1 = wall.x1, y1 = wall.y1, x2 = wall.x2, y2 = wall.y2;
                            const denom = (y2 - y1) * dirX - (x2 - x1) * dirY;
                            if (Math.abs(denom) > 0.001) {
                                const t = ((x2 - x1) * (this.y - y1) - (y2 - y1) * (this.x - x1)) / denom;
                                if (t > 0 && t < tMin) {
                                    const ix = this.x + t * dirX;
                                    const iy = this.y + t * dirY;
                                    if (ix >= Math.min(x1, x2) - 1 && ix <= Math.max(x1, x2) + 1 &&
                                        iy >= Math.min(y1, y2) - 1 && iy <= Math.max(y1, y2) + 1) {
                                        tMin = t;
                                    }
                                }
                            }
                        });
                    });

                    return Math.max(0, tMin - 30);
                };

                this.sensors.front = getDistance(0);
                this.sensors.left = getDistance(-90);
                this.sensors.right = getDistance(90);
                
                // Visual Update
                document.getElementById('stat-f').innerText = Math.round(this.sensors.front);
                document.getElementById('stat-l').innerText = Math.round(this.sensors.left);
                document.getElementById('stat-r').innerText = Math.round(this.sensors.right);
                document.getElementById('stat-s').innerText = Math.abs(Math.round(this.currentMove * 2)); // Artificial Km/h

                // Update SVG Gauges (Circumference ~88)
                const updateGauge = (id, val, max) => {
                    const el = document.getElementById(id);
                    if (!el) return;
                    const percent = Math.min(100, (val / max) * 100);
                    const offset = (percent / 100) * 88;
                    el.setAttribute('stroke-dasharray', `${offset}, 100`);
                };

                updateGauge('gauge-f-bar', this.sensors.front, 400);
                updateGauge('gauge-l-bar', this.sensors.left, 400);
                updateGauge('gauge-r-bar', this.sensors.right, 400);
                updateGauge('gauge-s-bar', Math.abs(this.currentMove), 8);
            }

            updateVisual() {
                const el = document.getElementById('robotCarContainer');
                if (el) el.setAttribute('transform', `translate(${this.x}, ${this.y}) rotate(${this.angle})`);
            }
        }

        const robotCar = new RobotCar();

        function aktifkanMobil() {
            showHardwareView();
            robotCar.isActive = !robotCar.isActive;
            const arena = document.getElementById('robotArena');
            const board = document.getElementById('boardCanvas');
            const components = document.getElementById('componentsCanvas');
            const wiring = document.getElementById('wiringLayer');
            const btnAdd = document.getElementById('btnAddObstacle');
            
            // Adjust arena size before reset
            if (window.innerWidth <= 768) {
                arena.style.width = '100%';
                arena.style.height = arena.clientWidth + 'px';
            } else {
                arena.style.width = '500px';
                arena.style.height = '500px';
            }
            
            if (robotCar.isActive) {
                arena.style.display = 'block';
                board.style.display = 'none';
                components.style.display = 'none';
                wiring.style.opacity = '0';
                btnAdd.classList.remove('hidden');
                const rect = arena.getBoundingClientRect();
                robotCar.reset(rect.width, rect.height);
                printSerial(`<span class="text-blue-400">[System] Mode Robot Aktif. Silakan tambah & geser rintangan sesukamu!</span>`);
            } else {
                arena.style.display = 'none';
                board.style.display = 'block';
                components.style.display = 'flex';
                wiring.style.opacity = '1';
                btnAdd.classList.add('hidden');
            }
        }

        // --- DYNAMIC OBSTACLES ---
        let selectedObstacle = null;
        let offset = { x: 0, y: 0 };

        function tambahkanRintangan() {
            const svg = document.getElementById('arenaCanvas');
            const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
            g.setAttribute("class", "arena-wall-group cursor-move");
            
            const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
            rect.setAttribute("class", "arena-wall");
            rect.setAttribute("x", "100"); rect.setAttribute("y", "100");
            rect.setAttribute("width", "80"); rect.setAttribute("height", "20");
            rect.setAttribute("rx", "5"); rect.setAttribute("fill", "#334155");
            rect.setAttribute("stroke", "#475569"); rect.setAttribute("stroke-width", "2");
            
            const handle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
            handle.setAttribute("class", "resize-handle cursor-nwse-resize");
            handle.setAttribute("cx", "180"); handle.setAttribute("cy", "120");
            handle.setAttribute("r", "6"); handle.setAttribute("fill", "#3b82f6");
            
            g.appendChild(rect);
            g.appendChild(handle);
            svg.appendChild(g);
            
            // Add double click to delete
            g.ondblclick = () => g.remove();
            
            // Add click to rotate (swap W/H)
            let mousedownTime = 0;
            g.onmousedown = (e) => {
                if (e.target === handle) return;
                mousedownTime = Date.now();
            };
            g.onmouseup = (e) => {
                if (e.target === handle) return;
                if (Date.now() - mousedownTime < 200) {
                    const w = rect.getAttribute("width");
                    const h = rect.getAttribute("height");
                    rect.setAttribute("width", h);
                    rect.setAttribute("height", w);
                    updateHandlePos(rect, handle);
                }
            };
        }

        function updateHandlePos(rect, handle) {
            const x = parseFloat(rect.getAttribute("x"));
            const y = parseFloat(rect.getAttribute("y"));
            const w = parseFloat(rect.getAttribute("width"));
            const h = parseFloat(rect.getAttribute("height"));
            handle.setAttribute("cx", x + w);
            handle.setAttribute("cy", y + h);
        }

        let isResizing = false;
        function startDragging(evt) {
            const target = evt.target;
            if (target.classList.contains('arena-wall') || target.parentNode.classList.contains('arena-wall-group')) {
                selectedObstacle = target.tagName === 'rect' ? target : target.parentNode.querySelector('rect');
                const svg = document.getElementById('arenaCanvas');
                const CTM = svg.getScreenCTM();
                
                if (target.classList.contains('resize-handle')) {
                    isResizing = true;
                } else {
                    isResizing = false;
                    offset.x = (evt.clientX - CTM.e) / CTM.a - selectedObstacle.getAttribute("x");
                    offset.y = (evt.clientY - CTM.f) / CTM.d - selectedObstacle.getAttribute("y");
                }
            }
        }

        function drag(evt) {
            if (selectedObstacle) {
                evt.preventDefault();
                const svg = document.getElementById('arenaCanvas');
                const CTM = svg.getScreenCTM();
                const handle = selectedObstacle.parentNode.querySelector('.resize-handle');
                let mouseX = (evt.clientX - CTM.e) / CTM.a;
                let mouseY = (evt.clientY - CTM.f) / CTM.d;

                if (isResizing) {
                    let newW = mouseX - selectedObstacle.getAttribute("x");
                    let newH = mouseY - selectedObstacle.getAttribute("y");
                    if (newW > 20) selectedObstacle.setAttribute("width", newW);
                    if (newH > 20) selectedObstacle.setAttribute("height", newH);
                    updateHandlePos(selectedObstacle, handle);
                } else {
                    let x = mouseX - offset.x;
                    let y = mouseY - offset.y;
                    
                    // Boundary check
                    const arenaRect = svg.getBoundingClientRect();
                    const w = parseFloat(selectedObstacle.getAttribute("width"));
                    const h = parseFloat(selectedObstacle.getAttribute("height"));
                    const limitX = arenaRect.width - 20 - w;
                    const limitY = arenaRect.height - 20 - h;
                    
                    if (x < 20) x = 20; if (x > limitX) x = limitX;
                    if (y < 20) y = 20; if (y > limitY) y = limitY;

                    selectedObstacle.setAttribute("x", x);
                    selectedObstacle.setAttribute("y", y);
                    updateHandlePos(selectedObstacle, handle);
                }
            }
        }

        function stopDragging() {
            selectedObstacle = null;
        }

        const dht = {
            readTemperature: () => {
                const dhtObj = Object.values(registeredComponents).find(o => o.type === 'dht');
                return dhtObj ? parseFloat(dhtObj.sliderT.value) : 0;
            },
            readHumidity: () => 50
        };

        function analogRead(pin) {
            const obj = Object.values(registeredComponents).find(c => c.pin == pin && c.type === 'pot');
            if (obj) return parseInt(obj.slider.value);
            return 0;
        }

        function analogWrite(pin, val) {
            updateVisuals(pin, val);
        }

        function transpileToJS(code) {
            let js = code.replace(/\bint\s+([a-zA-Z0-9_]+)\s*=\s*([^;]+);/g, 'var $1 = $2;');
            js = js.replace(/\bfloat\s+([a-zA-Z0-9_]+)\s*=\s*([^;]+);/g, 'var $1 = $2;');
            js = js.replace(/\blong\s+([a-zA-Z0-9_]+)\s*=\s*([^;]+);/g, 'var $1 = $2;');
            js = js.replace(/\bconst\s+int\s+([a-zA-Z0-9_]+)\s*=\s*([^;]+);/g, 'var $1 = $2;');
            js = js.replace(/void\s+setup\s*\(\s*\)/g, 'async function setup()');
            js = js.replace(/void\s+loop\s*\(\s*\)/g, 'async function loop()');
            js = js.replace(/delay\s*\(/g, 'await delay(');
            js = js.replace(/delayMicroseconds\s*\(/g, 'await delay(0.001 * ');
            
            // IoT support
            js = js.replace(/IoT\.send\s*\(/g, 'await IoT.send(');
            js = js.replace(/IoT\.status\s*\(/g, 'await IoT.status(');
            
            // Analog support
            js = js.replace(/analogRead\s*\(/g, 'analogRead(');
            js = js.replace(/analogWrite\s*\(/g, 'analogWrite(');
            
            // Servo library support
            js = js.replace(/Servo\s+([a-zA-Z0-9_]+);/g, 'var $1 = new Servo();');
            
            // Serial support
            js = js.replace(/Serial\.begin\s*\(/g, 'Serial.begin(');
            js = js.replace(/Serial\.println\s*\(/g, 'Serial.println(');
            js = js.replace(/Serial\.print\s*\(/g, 'Serial.print(');
            
            return js;
        }

        const EXAMPLES = {
            blink: `int led = 13;

void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(9600);
  Serial.println("Blink Started!");
}

void loop() {
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
  delay(1000);
}`,
            servo: `Servo myservo;

void setup() {
  myservo.attach(13);
  Serial.begin(9600);
}

void loop() {
  Serial.println("Servo ke 0 derajat");
  myservo.write(0);
  delay(1000);
  Serial.println("Servo ke 90 derajat");
  myservo.write(90);
  delay(1000);
  Serial.println("Servo ke 180 derajat");
  myservo.write(180);
  delay(1000);
}`,
            ultra: `int trigPin = 13;
int echoPin = 12;

void setup() {
  Serial.begin(9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void loop() {
  // Trigger sensor
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  // Baca pantulan suara
  long durasi = pulseIn(echoPin, HIGH);
  int jarak = durasi * 0.034 / 2;
  
  Serial.print("Jarak: ");
  Serial.print(jarak);
  Serial.println(" cm");
  
  delay(500);
}`,
            dht: `void setup() {
  Serial.begin(9600);
  Serial.println("DHT11 Sensor Read...");
}

void loop() {
  float t = dht.readTemperature();
  Serial.print("Suhu saat ini: ");
  Serial.print(t);
  Serial.println(" *C");
  
  delay(2000);
}`,
            fan_auto: `int fan = 11;
int trigPin = 13;
int echoPin = 12;

void setup() {
  pinMode(fan, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
  Serial.println("Sistem Pendingin Otomatis Aktif");
}

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  long duration = pulseIn(echoPin, HIGH);
  int distance = duration * 0.034 / 2;
  
  if(distance < 30) {
    digitalWrite(fan, HIGH);
    Serial.println("OBJEK DEKAT! Kipas NYALA");
  } else {
    digitalWrite(fan, LOW);
  }
  delay(500);
}`,
            pot_read: `int potPin = A0;

void setup() {
  Serial.begin(9600);
  Serial.println("Pembacaan Potensiometer Dimulai");
}

void loop() {
  int nilai = analogRead(potPin);
  Serial.print("Nilai Analog: ");
  Serial.println(nilai);
  delay(100);
}`,
            pot_led: `int potPin = A0;
int ledPin = 11;

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(potPin);
  // Ubah 0-1023 menjadi 0-255
  int brightness = sensorValue / 4; 
  
  analogWrite(ledPin, brightness);
  
  Serial.print("Kecerahan LED: ");
  Serial.println(brightness);
  delay(30);
}`,
            robot_basic: `// Pin Motor Kiri
int L_plus = 5;
int L_minus = 6;
// Pin Motor Kanan
int R_plus = 9;
int R_minus = 10;

void setup() {
  Serial.begin(9600);
  pinMode(L_plus, OUTPUT);
  pinMode(L_minus, OUTPUT);
  pinMode(R_plus, OUTPUT);
  pinMode(R_minus, OUTPUT);
  Serial.println("Robot Siap Beraksi!");
}

void loop() {
  Serial.println("MAJU...");
  digitalWrite(L_plus, HIGH); digitalWrite(L_minus, LOW);
  digitalWrite(R_plus, HIGH); digitalWrite(R_minus, LOW);
  delay(2000);

  Serial.println("BERHENTI...");
  digitalWrite(L_plus, LOW); digitalWrite(L_minus, LOW);
  digitalWrite(R_plus, LOW); digitalWrite(R_minus, LOW);
  delay(1000);

  Serial.println("BELOK KANAN...");
  digitalWrite(L_plus, HIGH); digitalWrite(L_minus, LOW);
  digitalWrite(R_plus, LOW); digitalWrite(R_minus, HIGH);
  delay(1000);
}`,
            robot_avoid: `int trigPin = 13;
int echoPin = 12;
int L_plus = 5;
int L_minus = 6;
int R_plus = 9;
int R_minus = 10;

void setup() {
  Serial.begin(9600);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  pinMode(L_plus, OUTPUT); pinMode(L_minus, OUTPUT);
  pinMode(R_plus, OUTPUT); pinMode(R_minus, OUTPUT);
}

void loop() {
  // Baca Jarak
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  
  long duration = pulseIn(echoPin, HIGH);
  int distance = duration * 0.034 / 2;
  
  Serial.print("Jarak: "); Serial.println(distance);

  if (distance < 50) {
    Serial.println("ADA HALANGAN! BELOK...");
    // Mundur sebentar
    digitalWrite(L_plus, LOW); digitalWrite(L_minus, HIGH);
    digitalWrite(R_plus, LOW); digitalWrite(R_minus, HIGH);
    delay(500);
    // Belok
    digitalWrite(L_plus, HIGH); digitalWrite(L_minus, LOW);
    digitalWrite(R_plus, LOW); digitalWrite(R_minus, HIGH);
    delay(800);
  } else {
    // Maju
    digitalWrite(L_plus, HIGH); digitalWrite(L_minus, LOW);
    digitalWrite(R_plus, HIGH); digitalWrite(R_minus, LOW);
  }
  delay(50);
}`,
            iot_basic: `// Contoh IoT: Mengirim Data Sensor ke Cloud
// Pastikan sudah memasukkan API Key di tombol ☁️ IOT CONFIG

void setup() {
  Serial.begin(9600);
  Serial.println("Sistem Monitoring IoT Dimulai...");
}

void loop() {
  // 1. Baca data sensor (contoh DHT11)
  float suhu = dht.readTemperature();
  
  // 2. Kirim ke Xieqa IoT (ID Alat: sensor_suhu)
  // Data akan muncul di dashboard /apikey
  IoT.send("sensor_suhu", suhu);
  
  Serial.print("Data terkirim ke Cloud: ");
  Serial.println(suhu);
  
  delay(5000); // Kirim setiap 5 detik
}`,
            iot_control: `// Contoh IoT: Kontrol LED dari Cloud
// Anda bisa menyalakan/mematikan LED simulator dari dashboard /apikey
int led = 13;

void setup() {
  pinMode(led, OUTPUT);
  Serial.begin(9600);
  Serial.println("Sistem Kontrol LED IoT Aktif...");
}

void loop() {
  // Ambil status dari cloud (ID Alat: lampu_led)
  // Pastikan ID 'lampu_led' sudah terdaftar di /apikey
  int status = IoT.status("lampu_led");
  
  digitalWrite(led, status);
  
  if(status == 1) {
    Serial.println("Cloud Command: NYALAKAN LED");
  } else {
    Serial.println("Cloud Command: MATIKAN LED");
  }
  
  delay(2000); // Cek status setiap 2 detik
}`
        };

        function loadExample(type) {
            if (EXAMPLES[type]) {
                document.getElementById('codeEditor').value = EXAMPLES[type];
                printSerial(`<span class="text-blue-400">[System] Contoh kode '${type}' berhasil dimuat.</span>`);
            }
        }

        async function runSimulation() {
            if (isRunning) return;
            const rawCode = document.getElementById('codeEditor').value;
            clearSerial();
            
            UI.btnRun.classList.add('hidden');
            UI.btnStop.classList.remove('hidden');
            UI.btnStop.classList.add('btn-pulse');
            UI.indicatorBox.classList.replace('bg-gray-500', 'bg-green-500');
            UI.indicatorBox.classList.add('animate-pulse');
            UI.indicatorText.innerText = "Sistem Berjalan (Simulasi Real-time)";
            UI.indicatorText.classList.replace('text-gray-400', 'text-green-400');
            isRunning = true;
            virtualPins = {}; 

            // Reset all components to LOW state visually
            Object.values(registeredComponents).forEach(comp => updateVisuals(comp.pin, LOW));

            try {
                const transpiled = transpileToJS(rawCode);
                eval(transpiled); 
                
                // Physics Loop (High Frequency)
                const physicsInterval = setInterval(() => {
                    if (isRunning && robotCar.isActive) robotCar.updatePhysics();
                    if (!isRunning) clearInterval(physicsInterval);
                }, 20);

                if (typeof setup === 'function') await setup();
                if (typeof loop === 'function') {
                    while (isRunning) { 
                        await loop(); 
                        await new Promise(r => setTimeout(r, 10)); 
                    }
                }
            } catch (error) {
                printSerial(`<span class="text-red-500 font-bold">[Runtime Error] ${error.message}</span>`);
                console.error(error);
                stopSimulation();
            }
        }

        function stopSimulation() {
            isRunning = false;
            UI.btnRun.classList.remove('hidden');
            UI.btnStop.classList.add('hidden');
            UI.indicatorBox.classList.replace('bg-green-500', 'bg-red-500');
            UI.indicatorBox.classList.remove('animate-pulse');
            UI.indicatorText.innerText = "Sistem Dihentikan";
            UI.indicatorText.classList.replace('text-green-400', 'text-red-400');
            
            Object.values(registeredComponents).forEach(comp => updateVisuals(comp.pin, LOW));
            
            setTimeout(() => {
                UI.indicatorBox.classList.replace('bg-red-500', 'bg-gray-500');
                UI.indicatorText.innerText = "Siap Dieksekusi";
                UI.indicatorText.classList.replace('text-red-400', 'text-gray-400');
            }, 2000);
        }

        UI.btnRun.addEventListener('click', runSimulation);
        UI.btnStop.addEventListener('click', stopSimulation);

        // --- IoT INTEGRATION ---
        const IoT = {
            apiKey: localStorage.getItem('iot_api_key') || '',
            send: async (deviceId, value) => {
                if (!IoT.apiKey) {
                    printSerial(`<span class="text-yellow-500">[IoT] API Key belum disetel! Klik tombol ☁️ IOT CONFIG.</span>`);
                    return;
                }
                try {
                    const response = await fetch(`../api/iot_gateway.php?k=${IoT.apiKey}&d=${deviceId}&v=${value}&_t=${Date.now()}`);
                    const text = (await response.text()).trim();
                    printSerial(`<span class="text-blue-400">[IoT] Kirim ke ${deviceId}: ${value} | Resp: ${text}</span>`);
                } catch (e) {
                    printSerial(`<span class="text-red-400">[IoT] Gagal mengirim data: ${e.message}</span>`);
                }
            },
            status: async (deviceId) => {
                if (!IoT.apiKey) return 0;
                try {
                    const response = await fetch(`../api/iot_gateway.php?k=${IoT.apiKey}&d=${deviceId}&_t=${Date.now()}`);
                    const text = (await response.text()).trim();
                    const val = parseInt(text);
                    return isNaN(val) ? 0 : val;
                } catch (e) {
                    console.error("IoT Status Error:", e);
                    return 0;
                }
            }
        };

        function toggleIoTModal() {
            const modal = document.getElementById('iotModal');
            modal.classList.toggle('hidden');
            const input = document.getElementById('iotApiKeyInput');
            if (input) input.value = IoT.apiKey;
            if (!modal.classList.contains('hidden')) refreshIoTDevices();
        }

        async function refreshIoTDevices() {
            if (!IoT.apiKey) return;
            const container = document.getElementById('iotDeviceList');
            try {
                const response = await fetch(`../api/iot_gateway.php?k=${IoT.apiKey}&d=list&_t=${Date.now()}`);
                const devices = await response.json();
                
                if (devices.error) {
                    container.innerHTML = `<div class="text-red-400 text-[10px]">${devices.error}</div>`;
                    return;
                }

                container.innerHTML = devices.map(d => `
                    <div class="flex items-center justify-between p-2 bg-gray-900/50 rounded border border-gray-700/50 mb-1">
                        <div class="flex flex-col">
                            <span class="text-[10px] font-bold text-gray-300">${d.label}</span>
                            <code class="text-[9px] text-blue-400">${d.device_id}</code>
                        </div>
                        <div class="flex items-center gap-2">
                            ${d.last_value != '0' && d.last_value != '' ? 
                                `<span class="text-[9px] bg-blue-900/40 text-blue-300 px-2 py-0.5 rounded-full border border-blue-500/30">${d.last_value}</span>` : 
                                `<span class="w-2 h-2 rounded-full ${d.status == 1 ? 'bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]' : 'bg-red-500'}"></span>`
                            }
                        </div>
                    </div>
                `).join('');
            } catch (e) {
                container.innerHTML = `<div class="text-gray-500 text-[10px]">Gagal memuat data...</div>`;
            }
        }

        // Auto refresh inside simulation if IoT is active
        setInterval(() => {
            if (isRunning && IoT.apiKey) {
                const modal = document.getElementById('iotModal');
                if (!modal.classList.contains('hidden')) refreshIoTDevices();
            }
        }, 3000);

        function saveIoTConfig() {
            const key = document.getElementById('iotApiKeyInput').value.trim();
            IoT.apiKey = key;
            localStorage.setItem('iot_api_key', key);
            toggleIoTModal();
            printSerial(`<span class="text-green-400">[System] API Key IoT berhasil disimpan.</span>`);
        }

        // --- INITIALIZATION ---
        selectBoard('arduino');
        tambahkanLED();
    </script>

    <!-- IoT Modal -->
    <div id="iotModal" class="hidden fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm">
        <div class="bg-gray-800 border border-gray-700 w-80 md:w-96 p-6 rounded-2xl shadow-2xl">
            <h3 class="text-xl font-bold text-blue-400 mb-4 flex items-center gap-2">
                <span>☁️</span> IoT Configuration
            </h3>
            <p class="text-xs text-gray-400 mb-4 leading-relaxed">Masukkan API Key Anda dari menu <b>IoT Integrasi (/apikey)</b> untuk mengaktifkan fitur Cloud.</p>
            
            <div class="mb-5">
                <label class="text-[10px] text-gray-500 font-bold uppercase mb-1 block">Xieqa IoT API Key</label>
                <input type="password" id="iotApiKeyInput" class="w-full bg-gray-900 border border-gray-600 rounded-lg px-4 py-2 text-sm text-blue-300 focus:border-blue-500 outline-none" placeholder="sk-iot-xxxxxx...">
            </div>

            <div class="mb-5">
                <div class="flex justify-between items-center mb-2">
                    <label class="text-[10px] text-gray-500 font-bold uppercase block">Status Database (Real-time)</label>
                    <button onclick="refreshIoTDevices()" class="text-[9px] text-blue-400 hover:text-blue-300">REFRESH</button>
                </div>
                <div id="iotDeviceList" class="max-h-40 overflow-y-auto pr-1">
                    <div class="text-gray-600 text-[10px] italic text-center py-4">Belum ada API Key...</div>
                </div>
            </div>

            <div class="flex gap-3">
                <button onclick="toggleIoTModal()" class="flex-1 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg text-sm font-bold transition-all">BATAL</button>
                <button onclick="saveIoTConfig()" class="flex-1 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-lg text-sm font-bold transition-all">SIMPAN</button>
            </div>
        </div>
    </div>
    
    <!-- Map Selection Modal -->
    <div id="mapSelectionModal" class="hidden fixed inset-0 z-[500] flex items-center justify-center bg-black/60 backdrop-blur-sm">
        <div class="bg-gray-800 border border-gray-700 w-80 md:w-96 p-6 rounded-2xl shadow-2xl text-center">
            <h3 class="text-xl font-bold text-blue-400 mb-2">Pilih Mode Peta</h3>
            <p class="text-xs text-gray-400 mb-6">Pilih jenis peta yang ingin Anda gunakan untuk simulasi pengiriman rute (GPS nyata atau ukuran kanvas ruangan).</p>
            
            <div class="flex flex-col gap-3">
                <button onclick="confirmMapSelection('real')" class="w-full py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-xl text-sm font-bold transition-all flex items-center justify-center gap-2 shadow-[0_0_15px_rgba(37,99,235,0.4)]">
                    <span class="text-xl">🌍</span> Peta Real (Sistem GPS)
                </button>
                <button onclick="confirmMapSelection('canvas')" class="w-full py-3 bg-gray-700 hover:bg-gray-600 text-white border border-gray-600 rounded-xl text-sm font-bold transition-all flex items-center justify-center gap-2">
                    <span class="text-xl">📏</span> Peta Ruangan (Maks 10 M)
                </button>
            </div>
            
            <button onclick="closeMapSelectionModal()" class="mt-6 text-xs text-gray-500 hover:text-red-400 transition-colors font-bold tracking-widest">BATAL</button>
        </div>
    </div>

    <!-- Ride-Hailing App Modal -->
    <div id="rideHailingModal" class="hidden fixed top-20 right-4 z-[3000] w-80 bg-gray-50 border border-gray-300 rounded-3xl shadow-2xl overflow-hidden flex flex-col font-sans">
        <!-- Phone Header -->
        <div class="bg-white px-4 py-3 border-b border-gray-200 flex justify-between items-center shadow-sm relative z-10">
            <h3 class="text-sm font-bold text-green-600 flex items-center gap-2">
                <span class="text-xl">🛵</span> App Ojek
            </h3>
            <button onclick="closeRideHailingApp()" class="text-gray-400 hover:text-red-500 font-bold transition-colors">✖</button>
        </div>
        
        <!-- Phone Body -->
        <div class="bg-gray-100 p-4 flex-1 flex flex-col gap-3 relative overflow-hidden">
            <!-- Map placeholder / graphic -->
            <div class="h-32 bg-green-100 rounded-xl overflow-hidden relative shadow-inner border border-green-200/50 flex items-center justify-center">
                <div class="absolute inset-0 opacity-30" style="background-image: radial-gradient(#22c55e 1px, transparent 1px); background-size: 10px 10px;"></div>
                <span class="text-5xl absolute animate-bounce" id="rideAppCarIcon">🚕</span>
            </div>
            
            <div class="bg-white p-3 rounded-xl shadow-sm border border-gray-200">
                <div class="flex items-center gap-2 mb-2 pb-2 border-b border-gray-100">
                    <span class="text-sm shrink-0" title="Titik Awal Driver">🚕</span>
                    <input type="text" id="rideCarLocationInput" class="w-full bg-yellow-50 border border-yellow-200 rounded-lg px-2 py-1.5 text-[10px] text-gray-700 focus:border-yellow-500 outline-none" placeholder="Lokasi Awal Mobil (Opsional / Otomatis)" oninput="delete this.dataset.lat; delete this.dataset.lng;">
                </div>
                <div class="flex flex-col gap-2 relative">
                    <div class="flex items-center gap-2">
                        <span class="w-3 h-3 rounded-full bg-blue-500 border-2 border-white shadow-sm shrink-0"></span>
                        <input type="text" id="rideOriginInput" class="flex-1 bg-gray-50 border border-gray-200 rounded-lg px-2 py-1.5 text-xs text-gray-700 focus:border-green-500 outline-none" placeholder="Lokasi Penjemputan..." oninput="delete this.dataset.lat; delete this.dataset.lng;">
                        <button id="btnPickOrigin" onclick="startMapPick('origin')" title="Tandai di peta" class="shrink-0 w-7 h-7 flex items-center justify-center rounded-lg bg-blue-50 border border-blue-200 hover:bg-blue-100 transition-all text-blue-600 text-xs font-bold">📍</button>
                    </div>
                    <!-- Connector line -->
                    <div class="absolute left-1.5 top-5 bottom-5 w-px bg-gray-300"></div>
                    <div class="flex items-center gap-2">
                        <span class="w-3 h-3 rounded-full bg-red-500 border-2 border-white shadow-sm shrink-0"></span>
                        <input type="text" id="rideDestInput" class="flex-1 bg-gray-50 border border-gray-200 rounded-lg px-2 py-1.5 text-xs text-gray-700 focus:border-green-500 outline-none" placeholder="Lokasi Tujuan (Opsional)..." oninput="delete this.dataset.lat; delete this.dataset.lng;">
                        <button id="btnPickDest" onclick="startMapPick('dest')" title="Tandai di peta" class="shrink-0 w-7 h-7 flex items-center justify-center rounded-lg bg-red-50 border border-red-200 hover:bg-red-100 transition-all text-red-600 text-xs font-bold">📍</button>
                    </div>
                </div>
            </div>

            <!-- MAP PICK INDICATOR (muncul saat mode pilih titik) -->
            <div id="mapPickIndicator" class="hidden items-center gap-2 bg-blue-600 text-white rounded-xl px-3 py-2 text-[10px] font-bold shadow-lg">
                <span class="animate-pulse text-sm">📍</span>
                <span id="mapPickText">Klik di peta GPS untuk menentukan lokasi</span>
                <button onclick="cancelMapPick()" class="ml-auto bg-white/20 hover:bg-white/40 rounded-lg px-2 py-0.5 text-white transition-all">Batal</button>
            </div>

            <!-- Status Box -->
            <div id="rideStatusBox" class="bg-white p-3 rounded-xl shadow-sm border border-gray-200 text-center">
                <p id="rideStatusText" class="text-xs font-bold text-gray-600">Pilih lokasi jemput & tujuan</p>
                <p id="rideStatusSubtext" class="text-[10px] text-gray-400 mt-1 hidden">Jarak: -</p>
            </div>

            <!-- ARRIVAL SCREEN (muncul di dalam App Ojek saat mobil tiba) -->
            <div id="arrivalScreen" class="hidden bg-white rounded-2xl shadow-lg border border-green-200 p-4 text-center">
                <div class="w-14 h-14 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-3">
                    <span class="text-3xl">🚗</span>
                </div>
                <h3 class="text-sm font-extrabold text-gray-800 mb-1">Mobil Sudah Tiba!</h3>
                <p class="text-[11px] text-gray-500 mb-1">Driver di titik penjemputan.</p>
                <div class="inline-flex items-center gap-1 bg-red-50 border border-red-200 rounded-lg px-3 py-1 mb-3">
                    <span class="w-2 h-2 rounded-full bg-red-500 animate-pulse"></span>
                    <span class="text-[10px] text-red-600 font-bold">MOBIL BERHENTI</span>
                </div>
                <button onclick="confirmArrivalAndContinue()" class="w-full py-2.5 bg-gradient-to-r from-green-500 to-emerald-600 hover:from-green-400 hover:to-emerald-500 text-white rounded-xl text-xs font-extrabold transition-all shadow-[0_4px_12px_rgba(22,163,74,0.4)] active:scale-95">
                    ✅ OK, Lanjutkan ke Tujuan!
                </button>
            </div>

            <!-- Action Button -->
            <button id="btnRideAction" onclick="startRideHailing()" class="mt-2 w-full py-3 bg-green-600 hover:bg-green-500 text-white rounded-xl text-sm font-bold transition-all shadow-[0_4px_15px_rgba(22,163,74,0.4)]">
                Pesan Mobil Sekarang
            </button>
        </div>
    </div>

    <!-- Map Simulator Logic -->
    <script>
        let currentMapMode = 'real';
        let canvasWaypoints = [];
        let phase1Waypoints = [];  // start → pickup
        let phase2Waypoints = [];  // pickup → destination
        // Stationary detection
        let stationaryCount = 0;
        let prevPollLat = null;
        let prevPollLng = null;

        function setMapMode(mode) {
            currentMapMode = mode;
            const btnReal = document.getElementById('btnMapReal');
            const btnCanvas = document.getElementById('btnMapCanvas');
            const leafletMapDiv = document.getElementById('leafletMap');
            const indoorCanvas = document.getElementById('indoorCanvas');
            const searchControls = document.getElementById('mapSearchControls');

            if (mode === 'real') {
                btnReal.classList.replace('bg-gray-700', 'bg-blue-600');
                btnReal.classList.replace('text-gray-400', 'text-white');
                btnCanvas.classList.replace('bg-blue-600', 'bg-gray-700');
                btnCanvas.classList.replace('text-white', 'text-gray-400');
                
                leafletMapDiv.style.display = 'block';
                indoorCanvas.classList.add('hidden');
                if (searchControls) searchControls.style.display = 'flex';
                if (leafletMap) setTimeout(() => leafletMap.invalidateSize(), 100);
            } else {
                btnCanvas.classList.replace('bg-gray-700', 'bg-blue-600');
                btnCanvas.classList.replace('text-gray-400', 'text-white');
                btnReal.classList.replace('bg-blue-600', 'bg-gray-700');
                btnReal.classList.replace('text-white', 'text-gray-400');
                
                leafletMapDiv.style.display = 'none';
                indoorCanvas.classList.remove('hidden');
                if (searchControls) searchControls.style.display = 'none';
                resizeIndoorCanvas();
                drawIndoorCanvas();
                initIndoorCanvasEvents();
            }
        }

        let isCanvasEventsInitialized = false;
        function initIndoorCanvasEvents() {
            if (isCanvasEventsInitialized) return;
            const canvas = document.getElementById('indoorCanvas');
            const coordDisplay = document.getElementById('canvasCoordDisplay');

            canvas.addEventListener('click', (e) => {
                const rect = canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                canvasWaypoints.push({x, y});
                drawIndoorCanvas();
            });

            canvas.addEventListener('mousemove', (e) => {
                if (currentMapMode !== 'canvas') return;
                const rect = canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                
                const maxDimension = Math.max(canvas.width, canvas.height);
                const scale = 1000 / maxDimension;
                
                const x_cm = Math.round(x * scale);
                const y_cm = Math.round(y * scale);
                
                if (coordDisplay) {
                    coordDisplay.innerText = `X: ${x_cm}, Y: ${y_cm} (cm)`;
                    coordDisplay.style.left = (x + 15) + 'px';
                    coordDisplay.style.top = (y + 15) + 'px';
                    coordDisplay.classList.remove('hidden');
                }
            });

            canvas.addEventListener('mouseleave', () => {
                if (coordDisplay) coordDisplay.classList.add('hidden');
            });

            window.addEventListener('resize', () => {
                if(currentMapMode === 'canvas') {
                    resizeIndoorCanvas();
                    drawIndoorCanvas();
                }
            });
            isCanvasEventsInitialized = true;
        }

        function resizeIndoorCanvas() {
            const canvas = document.getElementById('indoorCanvas');
            const parent = canvas.parentElement;
            let w = parent.clientWidth;
            let h = parent.clientHeight;
            
            // Fallback jika elemen induk tersembunyi
            if (w === 0) w = window.innerWidth || 800;
            if (h === 0) h = window.innerHeight || 600;
            
            canvas.width = w;
            canvas.height = h;
        }

        function drawIndoorCanvas() {
            const canvas = document.getElementById('indoorCanvas');
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // Maksimal 10 Meter = 1000 cm. Area terpanjang (width atau height) merepresentasikan 1000 cm.
            const maxDimension = Math.max(canvas.width, canvas.height);
            const scale = 1000 / maxDimension; // cm per pixel

            if (canvasWaypoints.length > 0) {
                // Draw path
                ctx.beginPath();
                ctx.strokeStyle = '#ef4444';
                ctx.lineWidth = 4;
                ctx.moveTo(canvasWaypoints[0].x, canvasWaypoints[0].y);
                for (let i = 1; i < canvasWaypoints.length; i++) {
                    ctx.lineTo(canvasWaypoints[i].x, canvasWaypoints[i].y);
                }
                ctx.stroke();

                // Draw points and labels
                for (let i = 0; i < canvasWaypoints.length; i++) {
                    const p = canvasWaypoints[i];
                    
                    // Draw node
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, 6, 0, Math.PI * 2);
                    ctx.fillStyle = '#ef4444';
                    ctx.fill();
                    ctx.strokeStyle = '#b91c1c';
                    ctx.lineWidth = 2;
                    ctx.stroke();

                    // Draw node coordinates (X, Y cm)
                    const pX_cm = Math.round(p.x * scale);
                    const pY_cm = Math.round(p.y * scale);
                    const coordText = `X: ${pX_cm}, Y: ${pY_cm}`;
                    
                    ctx.font = 'bold 10px monospace';
                    const cwText = ctx.measureText(coordText).width;
                    
                    ctx.fillStyle = 'rgba(15, 23, 42, 0.8)';
                    ctx.fillRect(p.x + 10, p.y - 20, cwText + 8, 18);
                    
                    ctx.fillStyle = '#4ade80';
                    ctx.textAlign = 'left';
                    ctx.textBaseline = 'middle';
                    ctx.fillText(coordText, p.x + 14, p.y - 11);

                    // Draw distance text
                    if (i > 0) {
                        const prev = canvasWaypoints[i-1];
                        const dx = p.x - prev.x;
                        const dy = p.y - prev.y;
                        const distPx = Math.sqrt(dx*dx + dy*dy);
                        const distCm = distPx * scale;
                        
                        const midX = (prev.x + p.x) / 2;
                        const midY = (prev.y + p.y) / 2;
                        
                        ctx.font = 'bold 11px sans-serif';
                        ctx.textAlign = 'center';
                        ctx.textBaseline = 'middle';
                        
                        const text = distCm >= 100 ? (distCm/100).toFixed(2) + ' m' : Math.round(distCm) + ' cm';
                        const textWidth = ctx.measureText(text).width;
                        
                        ctx.fillStyle = 'rgba(15, 23, 42, 0.8)';
                        ctx.fillRect(midX - textWidth/2 - 6, midY - 10, textWidth + 12, 20);
                        
                        ctx.fillStyle = '#93c5fd';
                        ctx.fillText(text, midX, midY);
                    }
                }
            }

            // Tentukan posisi gambar mobil. Jika belum ada koordinat valid (masih koordinat GPS awal ESP32 yang biasanya minus), 
            // kita taruh mobil di tengah canvas sebagai "standby".
            let drawX = carCanvasX;
            let drawY = carCanvasY;
            
            // carCanvasX bisa bernilai minus jika ESP32 masih mengirim lintang bujur Jakarta (-6.2)
            if (drawX === null || drawX < 0) {
                drawX = (canvas.width / 2) * scale;
                drawY = (canvas.height / 2) * scale;
            }

            if (drawX !== null && drawY !== null) {
                // Convert back from cm to pixel
                const pxX = drawX / scale;
                const pxY = drawY / scale;
                
                // Debug log
                console.log(`[Render] Drawing car at pxX: ${pxX}, pxY: ${pxY}, angle: ${carCanvasAngle}`);

                ctx.save();
                ctx.translate(pxX, pxY);
                // Draw a small halo/shadow under car
                ctx.beginPath();
                ctx.arc(0, 0, 18, 0, Math.PI * 2);
                ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
                ctx.fill();
                ctx.strokeStyle = '#2563eb';
                ctx.lineWidth = 2;
                ctx.stroke();
                
                // Rotasi emoji
                // Canvas rotate menerima radian. Jika NaN, diabaikan.
                if (!isNaN(carCanvasAngle)) {
                    ctx.rotate((carCanvasAngle - 90) * Math.PI / 180); 
                }
                
                ctx.font = '24px Arial';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                ctx.fillText('🚕', 0, 0);
                ctx.restore();

                // Draw Text Debug Information above the car (un-rotated)
                ctx.font = '14px monospace';
                ctx.fillStyle = '#ffed4a';
                ctx.textAlign = 'center';
                ctx.fillText(`Pos: ${Math.round(drawX)}cm, ${Math.round(drawY)}cm`, pxX, pxY - 30);
            }
        }

        let leafletMap;
        let mapMarker;
        let mapPolyline;
        let mapWaypoints = [];
        let fullRoutePath = [];
        let mapWaypointPins = []; // Array untuk menyimpan titik jarum merah
        let mapInterval;
        let currentTileLayer;
        let isDarkMode = true;

        function exitRobotMode() {
            if (typeof robotCar !== 'undefined' && robotCar.isActive) {
                robotCar.isActive = false;
                document.getElementById('robotArena').style.display = 'none';
                document.getElementById('boardCanvas').style.display = 'block';
                document.getElementById('componentsCanvas').style.display = 'flex';
                document.getElementById('wiringLayer').style.opacity = '1';
                document.getElementById('btnAddObstacle').classList.add('hidden');
                
                const btn = document.querySelector('button[onclick="aktifkanMobil()"]');
                if(btn) {
                    btn.classList.remove('bg-blue-800');
                    btn.classList.add('bg-blue-900/30');
                    btn.innerHTML = '<span class="text-xl">🏎️</span> MOBIL 4 RODA (ULTRA)';
                }
            }
        }

        function showHardwareView() {
            const mapArena = document.getElementById('mapArena');
            if (mapArena && mapArena.style.display !== 'none') {
                toggleMapArena();
            }
        }

        function toggleMapArena() {
            const mapArena = document.getElementById('mapArena');
            const robotArena = document.getElementById('robotArena');
            const btnToggleMap = document.getElementById('btnToggleMap');
            
            if (mapArena.style.display === 'none') {
                // Show modal selection
                document.getElementById('mapSelectionModal').classList.remove('hidden');
            } else {
                mapArena.style.display = 'none';
                robotArena.style.display = typeof robotCar !== 'undefined' && robotCar.isActive ? 'block' : 'none';
                btnToggleMap.classList.remove('bg-green-600/60');
                btnToggleMap.classList.add('bg-green-600/20');
                stopMapPolling();
            }
        }

        function closeMapSelectionModal() {
            document.getElementById('mapSelectionModal').classList.add('hidden');
        }

        function confirmMapSelection(mode) {
            closeMapSelectionModal();
            const mapArena = document.getElementById('mapArena');
            const robotArena = document.getElementById('robotArena');
            const btnToggleMap = document.getElementById('btnToggleMap');
            
            mapArena.style.display = 'block';
            robotArena.style.display = 'none';
            btnToggleMap.classList.add('bg-green-600/60');
            btnToggleMap.classList.remove('bg-green-600/20');
            
            if (!leafletMap) initLeafletMap();
            setTimeout(() => leafletMap.invalidateSize(), 100);
            startMapPolling();
            
            setMapMode(mode);
        }

        async function updateCarLocationOnServer(lat, lng) {
            if (!IoT.apiKey) return;
            try {
                await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_location`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ lat: lat, lng: lng })
                });
                console.log(`[IoT Map] Car location updated on server: Lat ${lat}, Lng ${lng}`);
            } catch (e) {
                console.error("Failed to update car location on server:", e);
            }
        }

        async function sendMapRouteSilent() {
            if (!IoT.apiKey) return;
            let payloadRoute = [];
            if (currentMapMode === 'real') {
                if (mapWaypoints.length === 0) return;
                payloadRoute = fullRoutePath.length > 0 ? fullRoutePath : mapWaypoints;
            } else {
                if (canvasWaypoints.length === 0) return;
                const canvas = document.getElementById('indoorCanvas');
                const maxDimension = Math.max(canvas.width, canvas.height);
                const scale = 1000 / maxDimension;
                payloadRoute = canvasWaypoints.map(wp => ({
                    x_cm: wp.x * scale,
                    y_cm: wp.y * scale
                }));
            }
            try {
                await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_route`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ 
                        route: payloadRoute, 
                        mode: currentMapMode 
                    })
                });
                console.log(`[IoT Map] Route synced silently to server`);
            } catch (e) {
                console.error("Failed to sync route silently:", e);
            }
        }

        function initLeafletMap() {
            leafletMap = L.map('leafletMap', {
                zoomControl: false // Matikan kontrol bawaan (kiri atas)
            }).setView([-6.200000, 106.816666], 13);
            
            // Pindahkan kontrol zoom ke pojok kanan bawah agar tidak menabrak tombol mode
            L.control.zoom({
                position: 'bottomright'
            }).addTo(leafletMap);
            
            // Set tile layer pertama kali (Gelap)
            currentTileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
                maxZoom: 19,
                attribution: '© OpenStreetMap'
            }).addTo(leafletMap);

            const carIcon = L.divIcon({
                html: '<div id="carIconInner" style="font-size: 20px; display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; text-shadow: 0 0 15px rgba(234, 179, 8, 0.9); transform: rotate(90deg);">🚕</div>',
                className: 'car-smooth-marker',
                iconSize: [26, 26],
                iconAnchor: [13, 13]
            });

            mapMarker = L.marker([-6.200000, 106.816666], {icon: carIcon, zIndexOffset: 1000}).addTo(leafletMap);
            mapPolyline = L.polyline([], {color: '#ef4444', weight: 4, dashArray: '10, 10'}).addTo(leafletMap);

            leafletMap.on('click', async function(e) {
                if (typeof mapPickMode !== 'undefined' && mapPickMode !== null) return;

                // Tambahkan data rute
                mapWaypoints.push({lat: e.latlng.lat, lng: e.latlng.lng});
                
                // Jika titik pertama, otomatis teleport mobil ke sini
                if (mapWaypoints.length === 1) {
                    lastCarLat = e.latlng.lat;
                    lastCarLng = e.latlng.lng;
                    if (mapMarker) {
                        mapMarker.setLatLng([lastCarLat, lastCarLng]);
                    }
                    await updateCarLocationOnServer(lastCarLat, lastCarLng);
                    showElegantPopup("🚕 Posisi awal mobil diatur ke koordinat ini.");
                }
                
                // Tambahkan titik jarum (Pin) merah di peta
                let pin = L.circleMarker([e.latlng.lat, e.latlng.lng], {
                    radius: 6,
                    color: '#b91c1c',     // Border merah gelap
                    fillColor: '#ef4444', // Isi merah terang
                    fillOpacity: 1
                }).addTo(leafletMap);
                
                // Tambahkan Popup agar koordinatnya bisa dilihat (Elegan)
                pin.bindPopup(`<div class="text-center p-1"><b class="text-blue-700 text-sm">🚕 Menuju Lokasi Ini!</b><br><span class="text-xs text-gray-500 mt-1 block">Titik Rute:<br>Lat ${e.latlng.lat.toFixed(5)}, Lng ${e.latlng.lng.toFixed(5)}</span></div>`).openPopup();
                
                mapWaypointPins.push(pin);
                
                await redrawMapRoute();
                await sendMapRouteSilent();
            });
        }

        function toggleMapTheme() {
            isDarkMode = !isDarkMode;
            const url = isDarkMode ? 
                'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png' : 
                'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png'; // Tema Terang (Voyager)
            
            if (currentTileLayer) {
                leafletMap.removeLayer(currentTileLayer);
            }
            currentTileLayer = L.tileLayer(url, {
                maxZoom: 19,
                attribution: '© OpenStreetMap'
            }).addTo(leafletMap);
            
            // Pastikan marker mobil tetap di atas peta baru
            mapMarker.bringToFront();
            
            // Ubah ikon tombol
            const btn = document.getElementById('btnThemeToggle');
            btn.innerHTML = isDarkMode ? '🌞' : '🌙';
            
            // Ubah warna polyline agar kontras
            mapPolyline.setStyle({ color: isDarkMode ? '#ef4444' : '#2563eb' });
        }

        async function redrawMapRoute() {
            if (mapWaypoints.length < 2) {
                const latlngs = mapWaypoints.map(wp => [wp.lat, wp.lng]);
                mapPolyline.setLatLngs(latlngs);
                fullRoutePath = mapWaypoints;
                return;
            }
            
            try {
                // Gunakan OSRM API untuk mencari rute jalan nyata antara titik-titik
                const coordsString = mapWaypoints.map(wp => `${wp.lng},${wp.lat}`).join(';');
                const url = `https://router.project-osrm.org/route/v1/driving/${coordsString}?overview=full&geometries=geojson`;
                
                const response = await fetch(url);
                const data = await response.json();
                
                if (data.code === 'Ok' && data.routes && data.routes.length > 0) {
                    const routeCoords = data.routes[0].geometry.coordinates;
                    // OSRM mereturn format [lng, lat], sedangkan Leaflet butuh [lat, lng]
                    const latlngs = routeCoords.map(coord => [coord[1], coord[0]]);
                    mapPolyline.setLatLngs(latlngs);
                    fullRoutePath = latlngs.map(ll => ({lat: ll[0], lng: ll[1]}));
                } else {
                    // Fallback garis lurus jika gagal
                    const latlngs = mapWaypoints.map(wp => [wp.lat, wp.lng]);
                    mapPolyline.setLatLngs(latlngs);
                    fullRoutePath = mapWaypoints;
                }
            } catch (e) {
                console.error("OSRM Routing Error: ", e);
                // Fallback garis lurus jika error (contoh: tidak ada internet atau API limit)
                const latlngs = mapWaypoints.map(wp => [wp.lat, wp.lng]);
                mapPolyline.setLatLngs(latlngs);
                fullRoutePath = mapWaypoints;
            }
        }

        function clearMapRoute() {
            if (currentMapMode === 'real') {
                mapWaypoints = [];
                fullRoutePath = [];
                redrawMapRoute();
                
                // Hapus semua titik jarum dari peta
                mapWaypointPins.forEach(pin => leafletMap.removeLayer(pin));
                mapWaypointPins = [];
            } else {
                canvasWaypoints = [];
                drawIndoorCanvas();
            }
        }

        async function sendMapRoute() {
            if (!IoT.apiKey) {
                alert('API Key IoT belum diatur. Silakan konfigurasi di menu IOT CONFIG.');
                return;
            }

            let payloadRoute = [];
            if (currentMapMode === 'real') {
                if (mapWaypoints.length === 0) {
                    alert('Rute kosong! Klik di peta GPS untuk membuat jalur.');
                    return;
                }
                payloadRoute = fullRoutePath.length > 0 ? fullRoutePath : mapWaypoints;
            } else {
                if (canvasWaypoints.length === 0) {
                    alert('Rute ruangan kosong! Klik di canvas untuk membuat jalur cm/m.');
                    return;
                }
                
                // Convert pixels to cm
                const canvas = document.getElementById('indoorCanvas');
                const maxDimension = Math.max(canvas.width, canvas.height);
                const scale = 1000 / maxDimension; // cm per pixel
                
                payloadRoute = canvasWaypoints.map(wp => ({
                    x_cm: wp.x * scale,
                    y_cm: wp.y * scale
                }));
            }

            try {
                const res = await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_route`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ 
                        route: payloadRoute, 
                        mode: currentMapMode 
                    })
                });
                const data = await res.json();
                if (data.status === 'success') {
                    if (typeof printSerial === 'function') {
                        printSerial(`<span class="text-green-400">[IoT Map] Rute mode ${currentMapMode} berhasil dikirim ke server.</span>`);
                    }
                    alert('Rute berhasil dikirim. ESP32 sekarang dapat membacanya.');
                } else {
                    alert('Gagal: ' + data.error);
                }
            } catch (e) {
                console.error(e);
                alert('Gagal mengirim rute ke server.');
            }
        }

        let lastCarLat = 0;
        let lastCarLng = 0;
        let carCanvasX = null;
        let carCanvasY = null;
        let carCanvasAngle = 0;

        async function pollMapLocation() {
            if (!IoT.apiKey) return;
            try {
                // Tambahkan cache-buster (_t) agar browser tidak meload dari cache
                const res = await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=get_location&_t=${Date.now()}`);
                const data = await res.json();
                if (data.location && data.location.lat !== 0 && data.location.lng !== 0) {
                    const newLat = data.location.lat;
                    const newLng = data.location.lng;
                    
                    let totalRotation = carCanvasAngle; // Default keep old rotation
                    
                    // Kalkulasi Rotasi jika posisi berubah
                    let dLng = 0, dLat = 0, hasMoved = false;
                    if (currentMapMode === 'real') {
                        if (lastCarLat !== 0 && (lastCarLat !== newLat || lastCarLng !== newLng)) {
                            dLng = newLng - lastCarLng;
                            dLat = newLat - lastCarLat;
                            hasMoved = true;
                        }
                    } else {
                        if (carCanvasX !== null && (carCanvasX !== newLat || carCanvasY !== newLng)) {
                            dLng = newLng - carCanvasY;
                            dLat = newLat - carCanvasX;
                            hasMoved = true;
                        }
                    }

                    if (hasMoved) {
                        // Threshold jarak (ignore jitter / noise GPS yang terlalu kecil)
                        const distSq = (dLng * dLng) + (dLat * dLat);
                        if (currentMapMode !== 'real' || distSq > 1e-10) {
                            let angle = Math.atan2(dLng, dLat) * (180 / Math.PI);
                            let targetRotation = angle + 90;
                            let deltaAngle = (targetRotation - carCanvasAngle) % 360;
                            if (deltaAngle > 180) {
                                deltaAngle -= 360;
                            } else if (deltaAngle < -180) {
                                deltaAngle += 360;
                            }
                            totalRotation = carCanvasAngle + deltaAngle;
                            carCanvasAngle = totalRotation;
                        }
                    }

                    if (currentMapMode === 'real') {
                        const innerIcon = document.getElementById('carIconInner');
                        if (innerIcon) {
                            innerIcon.style.transform = `rotate(${totalRotation}deg)`;
                        }
                        if (mapMarker) {
                            // Validasi koordinat GPS agar Leaflet tidak crash jika menerima koordinat Canvas (ratusan/ribuan)
                            if (newLat >= -90 && newLat <= 90) {
                                mapMarker.setLatLng([newLat, newLng]);
                                lastCarLat = newLat;
                                lastCarLng = newLng;
                            } else {
                                console.warn('[Map Real] Koordinat terdeteksi di luar batas GPS (mungkin koordinat Canvas). Menunggu mode Canvas...');
                            }
                        }
                    } else {
                        // Di mode canvas, lat = x (cm) dan lng = y (cm)
                        carCanvasX = parseFloat(newLat);
                        carCanvasY = parseFloat(newLng);
                        
                        // Debug log untuk membantu jika ada kendala
                        console.log(`[Canvas Mode] Menerima koordinat dari ESP32 -> X: ${carCanvasX} cm, Y: ${carCanvasY} cm`);
                        
                        // Render ulang mobil di atas canvas
                        drawIndoorCanvas();
                    }
                    
                    // --- STATIONARY DETECTION FOR ARRIVAL ---
                    let distanceToTarget = Infinity;
                    if (rideState === 'picking_up' && ridePickupLatLng) {
                        if (currentMapMode === 'real') {
                            const dLat = newLat - ridePickupLatLng.lat;
                            const dLng = newLng - ridePickupLatLng.lng;
                            distanceToTarget = Math.sqrt(dLat * dLat + dLng * dLng); // distance in degrees
                        } else {
                            const dX = newLat - ridePickupLatLng.lat;
                            const dY = newLng - ridePickupLatLng.lng;
                            distanceToTarget = Math.sqrt(dX * dX + dY * dY); // distance in cm
                        }
                    } else if (rideState === 'dropping_off' && rideDestLatLng) {
                        if (currentMapMode === 'real') {
                            const dLat = newLat - rideDestLatLng.lat;
                            const dLng = newLng - rideDestLatLng.lng;
                            distanceToTarget = Math.sqrt(dLat * dLat + dLng * dLng);
                        } else {
                            const dX = newLat - rideDestLatLng.lat;
                            const dY = newLng - rideDestLatLng.lng;
                            distanceToTarget = Math.sqrt(dX * dX + dY * dY);
                        }
                    }

                    const threshold = (currentMapMode === 'real') ? 0.0005 : 80;
                    console.log(`[DEBUG] rideState=${rideState} | carPos=Lat:${newLat},Lng:${newLng} | distanceToTarget=${distanceToTarget.toFixed(6)} | threshold=${threshold} | stationaryCount=${stationaryCount}`);

                    if (rideState === 'picking_up') {
                        if (distanceToTarget < threshold) {
                            if (prevPollLat === newLat && prevPollLng === newLng) {
                                stationaryCount++;
                                // Mobil berhenti selama 3 poll berturut-turut = sudah tiba di pickup
                                if (stationaryCount >= 3) {
                                    stationaryCount = 0;
                                    rideState = 'waiting_user';
                                    updateRideAppUI();
                                    await sendStopCommand();
                                    showArrivalModal();
                                }
                            } else {
                                stationaryCount = 0;
                            }
                        } else {
                            stationaryCount = 0; // reset jika mobil masih jauh dari pickup
                        }
                        prevPollLat = newLat;
                        prevPollLng = newLng;
                    } else if (rideState === 'dropping_off') {
                        if (distanceToTarget < threshold) {
                            if (prevPollLat === newLat && prevPollLng === newLng) {
                                stationaryCount++;
                                if (stationaryCount >= 3) {
                                    stationaryCount = 0;
                                    rideState = 'finished';
                                    updateRideAppUI();
                                    showElegantPopup('Anda telah sampai di tujuan. Terima kasih!');
                                }
                            } else {
                                stationaryCount = 0;
                            }
                        } else {
                            stationaryCount = 0; // reset jika mobil masih jauh dari tujuan
                        }
                        prevPollLat = newLat;
                        prevPollLng = newLng;
                    }
                }
            } catch (e) {
                console.error('Polling location error:', e);
            }
        }

        function startMapPolling() {
            if (!mapInterval) {
                mapInterval = setInterval(pollMapLocation, 1000);
            }
        }

        function stopMapPolling() {
            if (mapInterval) {
                clearInterval(mapInterval);
                mapInterval = null;
            }
        }

        // Fitur Pencarian Lokasi (Geocoding)
        let gpsLocation = null;

        async function geocode(query) {
            const res = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&countrycodes=id&limit=1`);
            const data = await res.json();
            if(data && data.length > 0) return { lat: parseFloat(data[0].lat), lng: parseFloat(data[0].lon) };
            return null;
        }

        async function searchOriginOnly() {
            const originVal = document.getElementById('mapOriginInput').value.trim();
            if (!originVal) {
                alert('Silakan masukkan nama lokasi yang ingin dicari.');
                return;
            }
            if (originVal === "Lokasi GPS Saat Ini" && gpsLocation) {
                lastCarLat = gpsLocation.lat;
                lastCarLng = gpsLocation.lng;
                leafletMap.flyTo([lastCarLat, lastCarLng], 15, { animate: true, duration: 1.5 });
                mapMarker.setLatLng([lastCarLat, lastCarLng]);
                await updateCarLocationOnServer(lastCarLat, lastCarLng);
                showElegantPopup("🚕 Memusatkan kembali ke lokasi GPS.");
                return;
            }
            
            const btn = document.querySelector('button[title="Cari Lokasi"]');
            let oldContent = '';
            if (btn) {
                oldContent = btn.innerHTML;
                btn.innerHTML = '⏳';
            }

            const loc = await geocode(originVal);
            
            if (btn) btn.innerHTML = oldContent;

            if (!loc) {
                alert(`Lokasi "${originVal}" tidak ditemukan.`);
                return;
            }
            
            // Pindahkan Taksi (titik awal)
            lastCarLat = loc.lat;
            lastCarLng = loc.lng;
            leafletMap.flyTo([lastCarLat, lastCarLng], 15, { animate: true, duration: 1.5 });
            mapMarker.setLatLng([lastCarLat, lastCarLng]);
            await updateCarLocationOnServer(lastCarLat, lastCarLng);
            clearMapRoute();
            
            showElegantPopup(`🚕 Lokasi simulasi dipindah ke ${originVal}!`);
        }

        async function calculateTextRoute() {
            const originVal = document.getElementById('mapOriginInput').value.trim();
            const destVal = document.getElementById('mapDestInput').value.trim();
            
            if(!originVal || !destVal) {
                alert('Silakan isi Lokasi Anda dan Tujuan terlebih dahulu.');
                return;
            }
            
            let originLatLng;
            if(originVal === "Lokasi GPS Saat Ini" && gpsLocation) {
                originLatLng = gpsLocation;
            } else {
                originLatLng = await geocode(originVal);
            }
            
            let destLatLng = await geocode(destVal);
            
            if(!originLatLng) return alert(`Lokasi "${originVal}" tidak ditemukan.`);
            if(!destLatLng) return alert(`Lokasi "${destVal}" tidak ditemukan.`);

            // Bersihkan rute lama
            clearMapRoute();

            // Pindahkan Taksi (titik awal)
            mapMarker.setLatLng([originLatLng.lat, originLatLng.lng]);
            leafletMap.flyTo([originLatLng.lat, originLatLng.lng], 13);
            mapWaypoints.push(originLatLng);
            
            // Buat Pin untuk tujuan
            let pin = L.circleMarker([destLatLng.lat, destLatLng.lng], {
                radius: 6,
                color: '#b91c1c',     
                fillColor: '#ef4444', 
                fillOpacity: 1
            }).addTo(leafletMap);
            pin.bindPopup(`<div class="text-center p-1"><b class="text-blue-700 text-sm">🚕 Menuju Tujuan!</b><br><span class="text-xs text-gray-500 mt-1 block">Titik Akhir:<br>Lat ${destLatLng.lat.toFixed(5)}, Lng ${destLatLng.lng.toFixed(5)}</span></div>`).openPopup();
            mapWaypointPins.push(pin);
            
            mapWaypoints.push(destLatLng);
            
            // Gambar garis rute jalan
            redrawMapRoute();
        }

        // Fungsi untuk memunculkan Toast Elegan
        function showArrivalModal() {
            // Tampilkan notif di DALAM App Ojek, bukan overlay fullscreen
            const arrivalScreen = document.getElementById('arrivalScreen');
            const btnRideAction = document.getElementById('btnRideAction');
            const rideStatusBox = document.getElementById('rideStatusBox');
            
            if (arrivalScreen) arrivalScreen.classList.remove('hidden');
            if (btnRideAction) btnRideAction.classList.add('hidden');
            if (rideStatusBox) rideStatusBox.classList.add('hidden');
            
            // Pastikan App Ojek terbuka
            document.getElementById('rideHailingModal').classList.remove('hidden');
        }

        function hideArrivalModal() {
            const arrivalScreen = document.getElementById('arrivalScreen');
            const btnRideAction = document.getElementById('btnRideAction');
            const rideStatusBox = document.getElementById('rideStatusBox');
            
            if (arrivalScreen) arrivalScreen.classList.add('hidden');
            if (btnRideAction) btnRideAction.classList.remove('hidden');
            if (rideStatusBox) rideStatusBox.classList.remove('hidden');
        }

        async function confirmArrivalAndContinue() {
            hideArrivalModal();
            await continueRideHailing();
        }

        // Kirim subset waypoints canvas (Phase1 atau Phase2) ke ESP32
        async function sendCanvasRoute(waypoints) {
            if (!IoT.apiKey) { alert('API Key belum diisi!'); return; }
            const canvas = document.getElementById('indoorCanvas');
            const maxDimension = Math.max(canvas.width, canvas.height);
            const scale = 1000 / maxDimension;
            
            const payloadRoute = waypoints.map(wp => ({
                x_cm: wp.x * scale,
                y_cm: wp.y * scale
            }));
            
            try {
                const res = await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_route`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ route: payloadRoute, mode: 'canvas' })
                });
                const data = await res.json();
                if (data.status === 'success') {
                    if (typeof printSerial === 'function') printSerial(`<span class="text-green-400">[IoT Map] Rute canvas (${waypoints.length} titik) berhasil dikirim ke ESP32.</span>`);
                } else {
                    alert('Gagal kirim rute: ' + data.error);
                }
            } catch(e) { console.error(e); alert('Gagal kirim rute canvas.'); }
        }

        async function sendGPSRoute(waypoints) {
            if (!IoT.apiKey) return;
            try {
                const res = await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_route`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ route: waypoints, mode: 'real' })
                });
                const data = await res.json();
                if (data.status === 'success') {
                    if (typeof printSerial === 'function') {
                        printSerial(`<span class="text-green-400">[IoT Map] Rute GPS fase (${waypoints.length} titik) berhasil dikirim ke ESP32.</span>`);
                    }
                }
            } catch (e) {
                console.error("Gagal mengirim rute GPS:", e);
            }
        }

        async function sendStopCommand() {
            if (!IoT.apiKey) return;
            try {
                await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_route`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ route: [], mode: currentMapMode, command: 'STOP' })
                });
                if (typeof printSerial === 'function') {
                    printSerial(`<span class="text-red-400">[IoT Map] Perintah STOP dikirim ke ESP32 - Mobil berhenti di titik jemput.</span>`);
                }
            } catch(e) {
                console.error('Gagal kirim perintah stop:', e);
            }
        }

        function showElegantPopup(message) {
            const popup = document.getElementById('elegantPopup');
            document.getElementById('elegantPopupText').innerText = message;
            
            popup.classList.remove('popup-leave');
            popup.classList.add('popup-enter');
            
            setTimeout(() => {
                popup.classList.remove('popup-enter');
                popup.classList.add('popup-leave');
            }, 5000);
        }

        // Fitur Auto Deteksi Lokasi Pengguna (GPS)
        function autoDetectLocation() {
            showElegantPopup("Baik Kak, kami akan melacak koordinat saat ini. Tolong ijinkan akses lokasinya pada browser ya! 🚕✨");
            
            setTimeout(() => {
                if ("geolocation" in navigator) {
                    const btn = document.querySelector('button[title="Deteksi GPS"]');
                    let oldContent = '';
                    if (btn) {
                        oldContent = btn.innerHTML;
                        btn.innerHTML = '⏳';
                    }
                
                    navigator.geolocation.getCurrentPosition(async function(position) {
                        const lat = position.coords.latitude;
                        const lon = position.coords.longitude;
                        gpsLocation = { lat: lat, lng: lon };
                        
                        document.getElementById('mapOriginInput').value = "Lokasi GPS Saat Ini";
                        
                        lastCarLat = lat;
                        lastCarLng = lon;
                        leafletMap.flyTo([lat, lon], 15, { animate: true, duration: 1.5 });
                        mapMarker.setLatLng([lat, lon]);
                        await updateCarLocationOnServer(lat, lon);
                        
                        if(btn) btn.innerHTML = oldContent;
                    }, function(error) {
                        if(btn) btn.innerHTML = oldContent;
                        alert('Gagal mendapatkan lokasi GPS Anda. Pastikan Anda telah memberikan izin lokasi (Location Permission) pada browser Anda.');
                    }, { enableHighAccuracy: true, timeout: 10000 });
                } else {
                    alert('Browser Anda tidak mendukung fitur deteksi lokasi otomatis.');
                }
            }, 1000); // Delay 1 detik agar pengguna sempat membaca popup
        }
        function toggleSidebar() {
            const sidebar = document.getElementById('toolbox');
            const btn = document.getElementById('btnToggleSidebar');
            if (sidebar.style.display === 'none') {
                sidebar.style.display = 'flex';
                btn.innerHTML = '<span>◀</span> PANEL';
            } else {
                sidebar.style.display = 'none';
                btn.innerHTML = '<span>▶</span> PANEL';
            }
            
            // Adjust Leaflet map size if it's currently active
            if (typeof leafletMap !== 'undefined' && leafletMap) {
                setTimeout(() => leafletMap.invalidateSize(), 100);
            }
            // Adjust canvas size if active
            if (currentMapMode === 'canvas') {
                setTimeout(() => {
                    resizeIndoorCanvas();
                    drawIndoorCanvas();
                }, 100);
            }
        }

        // --- RIDE HAILING APP SIMULATOR ---
        let rideState = 'idle'; // idle, picking_up, waiting_user, dropping_off, finished
        let ridePickupLatLng = null;
        let rideDestLatLng = null;

        // --- MAP PICK MODE (klik peta untuk isi lokasi jemput/tujuan) ---
        let mapPickMode = null; // 'origin' | 'dest' | null
        let mapPickListener = null;
        let mapPickPins = { origin: null, dest: null };

        function startMapPick(field) {
            if (currentMapMode !== 'real') return; // hanya untuk mode GPS
            mapPickMode = field;

            const indicator = document.getElementById('mapPickIndicator');
            const pickText = document.getElementById('mapPickText');
            const leafletEl = document.getElementById('leafletMap');

            indicator.classList.remove('hidden');
            indicator.classList.add('flex');

            if (field === 'origin') {
                indicator.className = indicator.className.replace('bg-blue-600', 'bg-blue-600').replace('bg-red-600', 'bg-blue-600');
                indicator.style.background = '#2563eb';
                pickText.innerText = '🔵 Klik di peta untuk Titik JEMPUT';
                leafletEl.style.outline = '3px solid #3b82f6';
            } else {
                indicator.style.background = '#dc2626';
                pickText.innerText = '🔴 Klik di peta untuk Titik TUJUAN';
                leafletEl.style.outline = '3px solid #ef4444';
            }

            // Pasang listener klik satu kali di peta
            if (mapPickListener) leafletMap.off('click', mapPickListener);
            mapPickListener = async function(e) {
                const lat = e.latlng.lat;
                const lng = e.latlng.lng;

                // Hapus pin lama jika ada
                if (mapPickPins[field]) {
                    leafletMap.removeLayer(mapPickPins[field]);
                }

                // Taruh pin baru
                const color = field === 'origin' ? '#3b82f6' : '#ef4444';
                const pin = L.circleMarker([lat, lng], {
                    radius: 8, color: color, fillColor: color, fillOpacity: 0.9, weight: 3
                }).addTo(leafletMap);

                const label = field === 'origin' ? '📍 Titik Jemput' : '📍 Titik Tujuan';
                pin.bindPopup(`<div class="text-center text-xs font-bold" style="color:${color}">${label}<br><span class="text-gray-500 font-normal">${lat.toFixed(5)}, ${lng.toFixed(5)}</span></div>`).openPopup();
                mapPickPins[field] = pin;

                // Reverse geocode → isi input dengan nama alamat
                const inputId = field === 'origin' ? 'rideOriginInput' : 'rideDestInput';
                const input = document.getElementById(inputId);
                input.value = `${lat.toFixed(6)}, ${lng.toFixed(6)}`; // sementara koordinat
                input.dataset.lat = lat;
                input.dataset.lng = lng;
                input.placeholder = 'Memuat alamat...';
                try {
                    const res = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&accept-language=id`);
                    const data = await res.json();
                    if (data && data.display_name) {
                        const short = data.display_name.split(',').slice(0, 3).join(',');
                        input.value = short;
                    }
                } catch(e) { /* biarkan pakai koordinat */ }

                cancelMapPick();
            };

            leafletMap.once('click', mapPickListener);
        }

        function cancelMapPick() {
            mapPickMode = null;
            if (mapPickListener) { leafletMap.off('click', mapPickListener); mapPickListener = null; }
            const indicator = document.getElementById('mapPickIndicator');
            indicator.classList.add('hidden');
            indicator.classList.remove('flex');
            document.getElementById('leafletMap').style.outline = 'none';
        }

        function openRideHailingApp() {
            document.getElementById('rideHailingModal').classList.remove('hidden');
            const originInput = document.getElementById('rideOriginInput');
            const destInput = document.getElementById('rideDestInput');
            const carInput = document.getElementById('rideCarLocationInput');
            const btnPickOrigin = document.getElementById('btnPickOrigin');
            const btnPickDest = document.getElementById('btnPickDest');
            
            if (currentMapMode === 'canvas') {
                carInput.placeholder = "Lokasi Mobil X, Y (cm) (Opsional)";
                originInput.placeholder = "Jemput X, Y (cm) misal: 100, 100";
                destInput.placeholder = "Tujuan X, Y (cm) (Opsional jika rute digambar)";
                // Sembunyikan tombol pin di mode canvas
                if (btnPickOrigin) btnPickOrigin.classList.add('hidden');
                if (btnPickDest) btnPickDest.classList.add('hidden');

                // Auto-fill dari canvasWaypoints jika ada
                if (typeof canvasWaypoints !== 'undefined' && canvasWaypoints.length > 0) {
                    const canvas = document.getElementById('indoorCanvas');
                    const maxDimension = Math.max(canvas.width, canvas.height);
                    const scale = 1000 / maxDimension;
                    
                    const carWp = canvasWaypoints[0];
                    const startWp = canvasWaypoints[Math.min(1, canvasWaypoints.length - 1)];
                    const endWp = canvasWaypoints[canvasWaypoints.length - 1];
                    
                    carInput.value = `${Math.round(carWp.x * scale)}, ${Math.round(carWp.y * scale)}`;
                    originInput.value = `${Math.round(startWp.x * scale)}, ${Math.round(startWp.y * scale)}`;
                    destInput.value = `${Math.round(endWp.x * scale)}, ${Math.round(endWp.y * scale)}`;
                }
            } else {
                carInput.placeholder = "Lokasi Awal Mobil (Opsional / Otomatis)";
                originInput.placeholder = "Ketik nama atau klik 📍 di peta...";
                destInput.placeholder = "Tujuan (Opsional jika rute digambar)...";
                // Tampilkan tombol pin di mode GPS
                if (btnPickOrigin) btnPickOrigin.classList.remove('hidden');
                if (btnPickDest) btnPickDest.classList.remove('hidden');

                // Auto-fill dari mapWaypoints jika ada
                if (typeof mapWaypoints !== 'undefined' && mapWaypoints.length > 0) {
                    const carWp = mapWaypoints[0];
                    const startWp = mapWaypoints[Math.min(1, mapWaypoints.length - 1)];
                    const endWp = mapWaypoints[mapWaypoints.length - 1];
                    
                    carInput.value = `${carWp.lat.toFixed(6)}, ${carWp.lng.toFixed(6)}`;
                    carInput.dataset.lat = carWp.lat;
                    carInput.dataset.lng = carWp.lng;

                    originInput.value = `${startWp.lat.toFixed(6)}, ${startWp.lng.toFixed(6)}`;
                    originInput.dataset.lat = startWp.lat;
                    originInput.dataset.lng = startWp.lng;

                    destInput.value = `${endWp.lat.toFixed(6)}, ${endWp.lng.toFixed(6)}`;
                    destInput.dataset.lat = endWp.lat;
                    destInput.dataset.lng = endWp.lng;
                } else if (typeof lastCarLat !== 'undefined' && lastCarLat !== 0 && lastCarLng !== 0) {
                    // Jika rute belum ada tetapi mobil sudah diposisikan
                    carInput.value = `${lastCarLat.toFixed(6)}, ${lastCarLng.toFixed(6)}`;
                    carInput.dataset.lat = lastCarLat;
                    carInput.dataset.lng = lastCarLng;
                }
            }
        }

        function closeRideHailingApp() {
            document.getElementById('rideHailingModal').classList.add('hidden');
        }

        async function resolveLocation(val, inputId) {
            if (currentMapMode === 'canvas') {
                // Canvas: parse "X, Y" dalam cm
                let parts = val.split(',');
                if (parts.length >= 2) {
                    return { lat: parseFloat(parts[0].trim()), lng: parseFloat(parts[1].trim()) };
                }
                return null;
            }
            
            // Cek apakah ada data koordinat asli tersimpan di dataset input
            if (inputId) {
                const el = document.getElementById(inputId);
                if (el && el.dataset.lat && el.dataset.lng) {
                    return { lat: parseFloat(el.dataset.lat), lng: parseFloat(el.dataset.lng) };
                }
            }
            
            // GPS mode: cek apakah sudah berupa koordinat dari klik peta ("lat, lng")
            const coordMatch = val.match(/^(-?\d+\.\d+),\s*(-?\d+\.\d+)$/);
            if (coordMatch) {
                return { lat: parseFloat(coordMatch[1]), lng: parseFloat(coordMatch[2]) };
            }
            if (val === "Lokasi GPS Saat Ini" && typeof gpsLocation !== 'undefined' && gpsLocation) {
                return gpsLocation;
            }
            return await geocode(val);
        }

        async function startRideHailing() {
            const originVal = document.getElementById('rideOriginInput').value.trim();
            const destVal = document.getElementById('rideDestInput').value.trim();

            if (!originVal) {
                alert('Silakan isi Lokasi Penjemputan!');
                return;
            }

            const btn = document.getElementById('btnRideAction');
            const oldText = btn.innerHTML;
            btn.innerHTML = 'Mencari rute...';
            btn.disabled = true;

            // Override Lokasi Mobil jika diisi secara dinamis
            const carLocVal = document.getElementById('rideCarLocationInput').value.trim();
            if (carLocVal) {
                let carStartLatLng = await resolveLocation(carLocVal, 'rideCarLocationInput');
                if (carStartLatLng) {
                    lastCarLat = carStartLatLng.lat;
                    lastCarLng = carStartLatLng.lng;
                    if (currentMapMode === 'real' && typeof mapMarker !== 'undefined' && mapMarker) {
                        mapMarker.setLatLng([lastCarLat, lastCarLng]);
                    } else if (currentMapMode === 'canvas') {
                        carCanvasX = lastCarLat;
                        carCanvasY = lastCarLng;
                    }
                    
                    // Simpan ke database server agar langsung ter-sync
                    if (IoT.apiKey) {
                        try {
                            await fetch(`../api/iot_map.php?k=${IoT.apiKey}&d=esp32_car&action=set_location`, {
                                method: 'POST',
                                headers: {'Content-Type': 'application/json'},
                                body: JSON.stringify({lat: lastCarLat, lng: lastCarLng})
                            });
                        } catch (e) { console.error("Gagal sync lokasi mobil:", e); }
                    }
                } else {
                    alert(`Peringatan: Lokasi mobil "${carLocVal}" tidak valid. Menggunakan lokasi otomatis.`);
                }
            }

            // Cari koordinat
            ridePickupLatLng = await resolveLocation(originVal, 'rideOriginInput');
            
            if (destVal) {
                rideDestLatLng = await resolveLocation(destVal, 'rideDestInput');
            } else {
                // Ambil titik terakhir rute sebagai tujuan
                if (currentMapMode === 'real' && mapWaypoints.length >= 2) {
                    rideDestLatLng = mapWaypoints[mapWaypoints.length - 1];
                } else if (currentMapMode === 'canvas' && canvasWaypoints.length >= 2) {
                    const canvas = document.getElementById('indoorCanvas');
                    const maxDimension = Math.max(canvas.width, canvas.height);
                    const scale = 1000 / maxDimension;
                    const lastWp = canvasWaypoints[canvasWaypoints.length - 1];
                    rideDestLatLng = { lat: lastWp.x * scale, lng: lastWp.y * scale };
                }
            }

            btn.disabled = false;
            btn.innerHTML = oldText;

            if (!ridePickupLatLng) return alert(`Lokasi Penjemputan "${originVal}" tidak ditemukan.`);
            if (!rideDestLatLng) return alert('Silakan isi Lokasi Tujuan atau gambar rute di peta terlebih dahulu.');

            // Mulai state menjemput (Mobil dari lokasi saat ini -> lokasi pickup)
            rideState = 'picking_up';
            updateRideAppUI();

            // Pindahkan map view
            if (currentMapMode === 'real' && typeof leafletMap !== 'undefined' && leafletMap) {
                leafletMap.flyTo([ridePickupLatLng.lat, ridePickupLatLng.lng], 15);
            }

            // Buat rute penjemputan
            if (currentMapMode === 'real') {
                // Mode GPS: Gambarkan rute utuh dari awal -> pickup -> tujuan
                clearMapRoute();
                if (lastCarLat !== 0) {
                    mapWaypoints.push({lat: lastCarLat, lng: lastCarLng});
                } else {
                    mapWaypoints.push(ridePickupLatLng); // fallback
                }
                
                // Tambahkan Titik Penjemputan (Pin Hijau)
                let pickupPin = L.circleMarker([ridePickupLatLng.lat, ridePickupLatLng.lng], {
                    radius: 6, color: '#16a34a', fillColor: '#22c55e', fillOpacity: 1
                }).addTo(leafletMap);
                pickupPin.bindPopup(`<div class="text-center p-1"><b class="text-green-700 text-sm">📍 Titik Penjemputan</b></div>`).openPopup();
                mapWaypointPins.push(pickupPin);
                
                // Push lokasi penjemputan ke waypoints
                mapWaypoints.push(ridePickupLatLng);
                
                // Tambahkan Titik Tujuan (Pin Merah)
                let destPin = L.circleMarker([rideDestLatLng.lat, rideDestLatLng.lng], {
                    radius: 6, color: '#b91c1c', fillColor: '#ef4444', fillOpacity: 1
                }).addTo(leafletMap);
                destPin.bindPopup(`<div class="text-center p-1"><b class="text-red-700 text-sm">📍 Titik Tujuan</b></div>`);
                mapWaypointPins.push(destPin);
                
                // Push lokasi tujuan akhir ke waypoints
                mapWaypoints.push(rideDestLatLng);
                
                // Hitung rute lengkap
                await redrawMapRoute();
                
                // Temukan index waypoint di rute nyata yang paling dekat dengan titik penjemputan
                let minDist = Infinity, pickupIdx = fullRoutePath.length - 1;
                fullRoutePath.forEach((wp, i) => {
                    const d = Math.sqrt((wp.lat - ridePickupLatLng.lat)**2 + (wp.lng - ridePickupLatLng.lng)**2);
                    if (d < minDist) { minDist = d; pickupIdx = i; }
                });
                
                // Simpan dua fase rute GPS
                phase1Waypoints = fullRoutePath.slice(0, pickupIdx + 1);
                phase2Waypoints = fullRoutePath.slice(pickupIdx);
                
                console.log(`[Ride GPS] Rute utuh dihitung. Phase1: ${phase1Waypoints.length} titik, Phase2: ${phase2Waypoints.length} titik.`);
                
                // Reset stationary counter
                stationaryCount = 0;
                prevPollLat = null;
                prevPollLng = null;
                
                // Kirim Phase 1 ke ESP32
                await sendGPSRoute(phase1Waypoints);
            } else {
                // Mode Canvas: split rute menjadi Phase1 (start→pickup) dan Phase2 (pickup→dest)
                if (canvasWaypoints.length === 0) {
                    alert('Rute canvas kosong! Gambar dulu rute di Map Ruangan sebelum memesan.');
                    return;
                }

                const cvs = document.getElementById('indoorCanvas');
                const maxDim = Math.max(cvs.width, cvs.height);
                const scl = 1000 / maxDim;

                // Temukan waypoint terdekat ke koordinat pickup
                const pickupX = ridePickupLatLng.lat;
                const pickupY = ridePickupLatLng.lng;
                let minDist = Infinity, pickupIdx = canvasWaypoints.length - 1;
                canvasWaypoints.forEach((wp, i) => {
                    const wx = wp.x * scl;
                    const wy = wp.y * scl;
                    const d = Math.sqrt((pickupX - wx)**2 + (pickupY - wy)**2);
                    if (d < minDist) { minDist = d; pickupIdx = i; }
                });

                // Simpan dua fase rute
                phase1Waypoints = canvasWaypoints.slice(0, pickupIdx + 1);
                phase2Waypoints = canvasWaypoints.slice(pickupIdx);

                console.log(`[Ride] Pickup di waypoint index ${pickupIdx} dari ${canvasWaypoints.length}. Phase1: ${phase1Waypoints.length} titik, Phase2: ${phase2Waypoints.length} titik.`);

                // Reset stationary counter
                stationaryCount = 0;
                prevPollLat = null;
                prevPollLng = null;

                // Kirim hanya Phase1 ke ESP32
                await sendCanvasRoute(phase1Waypoints);
                drawIndoorCanvas();
            }
            showElegantPopup("Mencari driver... Driver sedang menuju lokasi penjemputan Anda!");
        }

        async function continueRideHailing() {
            rideState = 'dropping_off';
            updateRideAppUI();
            
            if (currentMapMode === 'real') {
                if (phase2Waypoints.length === 0) {
                    alert('Rute fase 2 GPS kosong!');
                    return;
                }
                stationaryCount = 0;
                prevPollLat = null;
                prevPollLng = null;
                await sendGPSRoute(phase2Waypoints);
            } else {
                // Mode Canvas: kirim Phase2 (pickup → destination)
                if (phase2Waypoints.length === 0) {
                    alert('Rute fase 2 kosong!');
                    return;
                }
                stationaryCount = 0;
                prevPollLat = null;
                prevPollLng = null;
                await sendCanvasRoute(phase2Waypoints);
                drawIndoorCanvas();
            }
            showElegantPopup("Memulai perjalanan menuju tujuan...");
        }

        function updateRideAppUI() {
            const btn = document.getElementById('btnRideAction');
            const statusText = document.getElementById('rideStatusText');
            const subText = document.getElementById('rideStatusSubtext');
            const originInput = document.getElementById('rideOriginInput');
            const destInput = document.getElementById('rideDestInput');
            const carInput = document.getElementById('rideCarLocationInput');
            
            if (rideState === 'idle') {
                statusText.innerText = "Pilih lokasi jemput & tujuan";
                subText.classList.add('hidden');
                btn.innerText = "Pesan Mobil Sekarang";
                btn.onclick = startRideHailing;
                btn.className = "mt-2 w-full py-3 bg-green-600 hover:bg-green-500 text-white rounded-xl text-sm font-bold transition-all shadow-[0_4px_15px_rgba(22,163,74,0.4)]";
                originInput.disabled = false;
                destInput.disabled = false;
                carInput.disabled = false;
            } else if (rideState === 'picking_up') {
                statusText.innerText = "Driver sedang menjemput...";
                statusText.className = "text-xs font-bold text-blue-600";
                subText.classList.remove('hidden');
                btn.innerText = "Menunggu Driver...";
                btn.onclick = null;
                btn.className = "mt-2 w-full py-3 bg-gray-400 text-white rounded-xl text-sm font-bold transition-all cursor-not-allowed";
                originInput.disabled = true;
                destInput.disabled = true;
                carInput.disabled = true;
            } else if (rideState === 'waiting_user') {
                statusText.innerText = "Mobil telah tiba!";
                statusText.className = "text-sm font-bold text-green-600";
                subText.classList.add('hidden');
                btn.innerText = "Masuk & Lanjutkan Perjalanan";
                btn.onclick = continueRideHailing;
                btn.className = "mt-2 w-full py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-xl text-sm font-bold transition-all shadow-[0_4px_15px_rgba(37,99,235,0.4)] animate-pulse";
            } else if (rideState === 'dropping_off') {
                statusText.innerText = "Menuju ke tujuan...";
                statusText.className = "text-xs font-bold text-blue-600";
                subText.classList.remove('hidden');
                btn.innerText = "Dalam Perjalanan";
                btn.onclick = null;
                btn.className = "mt-2 w-full py-3 bg-gray-400 text-white rounded-xl text-sm font-bold transition-all cursor-not-allowed";
            } else if (rideState === 'finished') {
                statusText.innerText = "Perjalanan Selesai!";
                statusText.className = "text-sm font-bold text-green-600";
                subText.classList.add('hidden');
                btn.innerText = "Pesan Lagi";
                btn.onclick = () => { rideState = 'idle'; updateRideAppUI(); };
                btn.className = "mt-2 w-full py-3 bg-green-600 hover:bg-green-500 text-white rounded-xl text-sm font-bold transition-all shadow-[0_4px_15px_rgba(22,163,74,0.4)]";
                originInput.disabled = false;
                destInput.disabled = false;
                carInput.disabled = false;
                originInput.value = '';
                destInput.value = '';
                carInput.value = '';
            }
        }

        // Haversine formula menghitung jarak meter dari 2 titik lat/lng
        // Otomatis berubah menjadi Euclidean (rumus pythagoras) jika dalam Canvas Mode
        function calculateDistance(lat1, lon1, lat2, lon2) {
            if (currentMapMode === 'canvas') {
                const dx = lat2 - lat1; // lat = X (cm)
                const dy = lon2 - lon1; // lng = Y (cm)
                return Math.sqrt(dx*dx + dy*dy) / 100; // konversi cm ke meter
            }
            
            const R = 6371e3; // metres
            const phi1 = lat1 * Math.PI/180;
            const phi2 = lat2 * Math.PI/180;
            const deltaPhi = (lat2-lat1) * Math.PI/180;
            const deltaLambda = (lon2-lon1) * Math.PI/180;

            const a = Math.sin(deltaPhi/2) * Math.sin(deltaPhi/2) +
                      Math.cos(phi1) * Math.cos(phi2) *
                      Math.sin(deltaLambda/2) * Math.sin(deltaLambda/2);
            const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

            return R * c; // in metres
        }
    </script>
</body>
</html>