
    char SavedSSID[32];
    char SavedPASS[64];

// HTML шаблоны в PROGMEM
const char PAGE_HEADER[] PROGMEM = R"=====(
<!DOCTYPE html><html><head>
<meta charset=UTF-8>
<meta name=viewport content='width=device-width,initial-scale=1'>
<title>PreSetup</title>
<style>
body{font-family:Arial,sans-serif;margin:15px;padding:0;background:#f5f5f5}
h1,h2{color:#333;margin:5px 0}
input,button,select{display:block;width:100%;margin:5px 0;padding:8px;box-sizing:border-box;border:1px solid #ddd;border-radius:3px}
button{background:#007bff;color:white;border:none;cursor:pointer;font-weight:bold}
button:hover{opacity:0.9}
button.danger{background:#dc3545}
button.warning{background:#ffc107}
button.success{background:#28a745}
.sc{background:white;margin:10px 0;padding:15px;border-radius:5px;box-shadow:0 1px 3px rgba(0,0,0,0.1)}
.fl{max-height:300px;overflow-y:auto;border:1px solid #ddd;padding:10px;margin:10px 0;background:#fafafa}
.fi{display:table;width:100%;margin:5px 0;padding:8px;border-bottom:1px solid #eee;box-sizing:border-box;font-size:16px}
.fn{display:table-cell;width:60%;word-break:break-all;vertical-align:middle}
.fs{color:#666;font-size:14px;margin-left:8px}
.dw{display:table-cell;width:20%;padding:6px;font-size:14px;background:#28a745;color:white;border:none;border-radius:4px;cursor:pointer;text-align:center;white-space:nowrap;min-width:50px;margin:0 5px}
.dw:hover{background:#218838}
.db{display:table-cell;width:20%;padding:6px;font-size:14px;background:#c82333;color:white;border:none;border-radius:4px;cursor:pointer;text-align:center;white-space:nowrap;min-width:50px}
.db:hover{background:#a71e2a}
.st{color:#666;font-size:14px;margin:5px 0}
.al{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#28a745;color:white;padding:12px 24px;border-radius:5px;z-index:1000;display:none;font-size:16px}
.eb{background:#6c757d;margin-top:20px;text-align:center;font-size:16px;padding:10px}
.eb:hover{background:#5a6268}
</style>
</head><body>
<div class=al id=al></div>
<div style='max-width:600px;margin:0 auto'>
<h1>⚙️ Logger PreSetup</h1>
)=====";

const char STORAGE_SECTION[] PROGMEM = R"=====(
<div class=sc>
<h3>📊 Storage: %UP%% (%UB% of %TB%)</h3>
<div style='background:#ddd;border-radius:5px;height:20px;margin:10px 0'>
<div style='background:%FC%;height:100%;border-radius:5px;width:%UP%%'></div>
</div>
</div>
)=====";

//  PAGE_FOOTER 
const char PAGE_FOOTER[] PROGMEM = R"=====(
<div style='margin-top:30px;text-align:center'>
<a href='/' style='text-decoration:none'><button class='eb'>🚪 Exit to Main</button></a>
</div>
</div>
<script>
function sa(m,t){
var al=document.getElementById('al');
al.style.display='block';
al.textContent=m;
al.style.background=t==='error'?'#dc3545':'#28a745';
setTimeout(()=>al.style.display='none',3000)}
function sWF(){
var s=document.getElementById('ssid').value;
var p=document.getElementById('pas').value;
fetch('/wifi_save', {
method:'POST',
headers:{'Content-Type':'application/x-www-form-urlencoded'},
body:'ssid='+encodeURIComponent(s)+'&pas='+encodeURIComponent(p)})
.then(r=>r.text())
.then(d=>{
sa(d,'success');
setTimeout(()=>location.reload(),2000);})
.catch(e=>sa('Error: '+e,'error'));
return false;}
function df(f){
window.open('/download?file='+encodeURIComponent(f),'_blank');}
function dlf(f){
if(confirm('Delete '+f+'?')) {
fetch('/delete_file?file='+encodeURIComponent(f), {method:'GET'})
.then(response => {
if (response.ok) {
return response.text();}
throw new Error('Delete failed');})
.then(html => {
var div = document.createElement('div');
div.innerHTML = html;
var msg = div.querySelector('h3') ? div.querySelector('h3').textContent : 'File deleted';
sa(msg, 'success');
setTimeout(()=>location.reload(),1000);})
.catch(e => {
sa('Error: '+e, 'error');
setTimeout(()=>location.reload(),1500);});}
}
function hU(e){
e.preventDefault();
var formData = new FormData(document.getElementById('UpF'));
sa('Uploading...', 'success');
fetch('/upload', {
method:'POST',
body:formData})
.then(response => {
if (!response.ok) {
throw new Error('Upload failed');}
return response.text();})
.then(html => {
var div = document.createElement('div');
div.innerHTML = html;
var msg = div.querySelector('h3') ? div.querySelector('h3').textContent : 'Upload successful';
sa(msg, 'success');
setTimeout(()=>location.reload(),1000);})
.catch(e => {
sa('Upload error: ' + e.message, 'error');
setTimeout(()=>location.reload(),2000);});
}
document.getElementById('UpF').addEventListener('submit',hU);
</script>
</body></html>
)=====";

const char WIFI_SECTION[] PROGMEM = R"=====(
<div class=sc>
<h2>📶 WiFi Settings</h2>
<form onsubmit='return sWF()'>
<input type=text id=ssid placeholder='WiFi SSID' required>
<input type=password id=pas placeholder='pas' required>
<button type=submit>💾 Save & Reboot</button>
</form>
</div>
)=====";

const char FS_SECTION[] PROGMEM = R"=====(
<div class=sc>
<h2>📁 File System</h2>
<h3>📤 Upload Files</h3>
<form id=UpF method=post enctype=multipart/form-data>
<input type=file name=file multiple required>
<button type=submit>⬆️ Upload Selected</button>
</form>

<h3>📋 Existing Files:</h3>
<div class=fl>%FL%</div>
</div>
)=====";
bool LFS_exists(String path) {
    bool yes = false;
    File file = LittleFS.open(path, "r");
    if (file) {
        yes = !file.isDirectory();
        file.close();
    }
    return yes;
}
// Функция для получения технической страницы
void handleTechnicalPage() {
    String page = FPSTR(PAGE_HEADER);
    
    // WiFi секция
    //page += FPSTR(WIFI_SECTION);
        
    // USB Mass Storage статус
    page += "<div class='sc'>";
    page += "<h2>🔌 USB Mode</h2>";
    page += "<div>Current mode: <strong>" + String(usbMassStorageActive ? "USB Mass Storage" : "Normal (Web Server)") + "</strong></div>";
    page += "<button class='" + String(usbMassStorageActive ? "success" : "warning") + "' onclick='location.href=\"/toggle_usb\"'>";
    page += usbMassStorageActive ? "🔙 Switch to Normal Mode" : "🔌 Switch to USB Mass Storage";
    page += "</button>";
    page += "</div>";

    // SD Card секция
    page += "<div class='sc'><h2>📁 SD Card Storage</h2>";
    if (sdEnable && sdMounted && !usbMassStorageActive) {
        uint64_t total = SD_MMC.totalBytes();  // 31134318592
        uint64_t used = SD_MMC.usedBytes();    // 77316096
        uint64_t free = total - used;           // 31057002496
        
        int usedPercent = total > 0 ? (used * 100) / total : 0;  // (77316096 * 100) / 31134318592 = 0.24%
        
        String color = "#28a745";
        if (usedPercent > 90) color = "#dc3545";
        else if (usedPercent > 75) color = "#ffc107";
        
        page += "<div>Total: " + formatBytes(total) + "</div>";  // 29.0 GB
        page += "<div>Used: " + formatBytes(used) + " (" + String(usedPercent) + "%)</div>";  // 73.7 MB (0%)
        page += "<div>Free: " + formatBytes(free) + "</div>";  // 28.9 GB
        page += "<div style='background:#ddd;border-radius:5px;height:20px;margin:10px 0'>";
        page += "<div style='background:" + color + ";height:100%;border-radius:5px;width:" + String(usedPercent) + "%'></div></div>";
    } else if (usbMassStorageActive) {
        page += "<div style='color:orange'>⚠️ SD Card is in USB Mass Storage mode</div>";
    } else {
        page += "<div style='color:red'>❌ SD Card not available</div>";
    }
    page += "</div>";

    // File system секция для LittleFS
    page += "<div class='sc'><h2>💾 Internal Storage (LittleFS)</h2>";
    if (LittleFS.totalBytes()==0) {
        page += "<div style='color:red'>❌ LittleFS not mounted</div>";
    } else {
        size_t total = LittleFS.totalBytes();
        size_t used = LittleFS.usedBytes();
        size_t free = total - used;
        int usedPercent = total > 0 ? (used * 100) / total : 0;
        String color = "#28a745";
        if (usedPercent > 90) color = "#dc3545";
        else if (usedPercent > 75) color = "#ffc107";
        page += "<div>Total: " + formatBytes(total) + "</div>";
        page += "<div>Used: " + formatBytes(used) + " (" + String(usedPercent) + "%)</div>";
        page += "<div>Free: " + formatBytes(free) + "</div>";
        page += "<div style='background:#ddd;border-radius:5px;height:20px;margin:10px 0'>";
        page += "<div style='background:" + color + ";height:100%;border-radius:5px;width:" + String(usedPercent) + "%'></div></div>";
    }
    page += "</div>";

    String fsSection = FPSTR(FS_SECTION);
    vTaskDelay(10/portTICK_PERIOD_MS); 
    String fileList = getFileListHTML();
    vTaskDelay(10/portTICK_PERIOD_MS); 
    fsSection.replace("%FL%", fileList);
    page += fsSection;
    // Футер
    page += FPSTR(PAGE_FOOTER);
    
    server.send(200, "text/html", page);
}
String getFileListHTML() {
  if (LittleFS.totalBytes()==0) {
    return "<div style='color:red'>❌ File system not ready</div>";
  }
  
  String fileList;
  File root = LittleFS.open("/");
  File file = root.openNextFile();
  int fileCount = 0;
  
  while (file && (fileCount < 51)) { //Показываем не более 50 файлов !!!
    if (!file.isDirectory()) {
      fileCount++;
      String filename = file.name();
      String filesize = String(file.size());
      
      fileList += "<div class='fi'>";
      fileList += "<span class='fn'>📄 " + filename + " <span class='fs'>(" + filesize + "b)</span></span>";
      fileList += "<button class='dw' onclick='df(\"" + filename + "\")'>📥</button>";
      fileList += "<button class='db' onclick='dlf(\"" + filename + "\")'>🗑️</button>";
      fileList += "</div>";
    }
    file = root.openNextFile();
  }
  
  if (fileCount == 0) {
    fileList = "<div style='color:#666;text-align:center'>📭 No files found</div>";
  }
  
  return fileList;
}
// Обработчик сохранения WiFi
void handleWifiSave() {
    if (server.hasArg("ssid") && server.hasArg("pas")) {
        String ssid = server.arg("ssid");
        String pas = server.arg("pas");
        
        strlcpy(SavedSSID, ssid.c_str(), sizeof(SavedSSID));
        strlcpy(SavedPASS, pas.c_str(), sizeof(SavedPASS));
        
        save_profile();
        server.send(200, "text/plain", "WiFi saved. Rebooting...");
        delay(1000);
        ESP.restart();
    } else {
        server.send(400, "text/plain", "Need SSID and password");
    }
}

// Обработчик переключения USB режима
void handleToggleUSB() {
    ToggleUSBMassStorage();
    String message = usbMassStorageActive ? 
        "USB Mass Storage activated. Device will appear as USB drive." : 
        "Returned to normal mode. SD Card available for web server.";
    
    String html = "<html><body>";
    html += "<h3>" + message + "</h3>";
    html += "<meta http-equiv='refresh' content='3;url=/technical'>";
    html += "<p>Redirecting to technical page...</p>";
    html += "</body></html>";
    
    server.send(200, "text/html", html);
}

// Обработчик удаления файла из LittleFS
void handleDeleteFile() {
    if (server.hasArg("file")) {
        String filename = server.arg("file");
        if (filename[0] != '/') filename = "/" + filename;
        
        Serial.println("Attempting to delete: " + filename);
        
        if (LittleFS.exists(filename)) {
            if (LittleFS.remove(filename)) {
                Serial.println("File deleted successfully");
                // Отправляем HTML с редиректом через JavaScript
                String html = "<html><body>";
                html += "<h3>✅ File deleted successfully</h3>";
                html += "<script>setTimeout(function() { window.location.href='/technical'; }, 1000);</script>";
                html += "</body></html>";
                server.send(200, "text/html", html);
            } else {
                Serial.println("Failed to delete file");
                server.send(500, "text/html", 
                    "<html><body><h3>❌ Failed to delete file</h3>"
                    "<a href='/technical'>← Back</a></body></html>");
            }
        } else {
            Serial.println("File not found: " + filename);
            server.send(404, "text/html", 
                "<html><body><h3>❌ File not found</h3>"
                "<a href='/technical'>← Back</a></body></html>");
        }
    } else {
        server.send(400, "text/plain", "File parameter required");
    }
}

// Обработчик скачивания файла из LittleFS 
String getDownloadContentType(String filename) {
    String lower = filename;
    lower.toLowerCase();
    
    if (lower.endsWith(".txt")) return "text/plain; charset=utf-8";
    if (lower.endsWith(".htm") || lower.endsWith(".html")) return "text/html";
    if (lower.endsWith(".css")) return "text/css";
    if (lower.endsWith(".js")) return "application/javascript";
    if (lower.endsWith(".json")) return "application/json";
    if (lower.endsWith(".png")) return "image/png";
    if (lower.endsWith(".jpg") || lower.endsWith(".jpeg")) return "image/jpeg";
    if (lower.endsWith(".gif")) return "image/gif";
    if (lower.endsWith(".pdf")) return "application/pdf";
    if (lower.endsWith(".zip")) return "application/zip";
    if (lower.endsWith(".csv")) return "text/csv";
    if (lower.endsWith(".xml")) return "text/xml";
    
    return "application/octet-stream";
}
 
void handleDownloadFile() {
    if (server.hasArg("file")) {
        String filename = server.arg("file");
        if (filename[0] != '/') filename = "/" + filename;
        
        Serial.println("Download requested: " + filename);
        
        if (LittleFS.exists(filename)) {
            File file = LittleFS.open(filename, "r");
            if (file) {
                // Извлекаем только имя файла без пути
                String downloadFilename = filename;
                int lastSlash = downloadFilename.lastIndexOf('/');
                if (lastSlash >= 0) {
                    downloadFilename = downloadFilename.substring(lastSlash + 1);
                }
                
                // Определяем тип контента
                String contentType = getDownloadContentType(downloadFilename);
                
                // Устанавливаем заголовки для скачивания
                server.sendHeader("Content-Disposition", "attachment; filename=\"" + downloadFilename + "\"");
                server.sendHeader("Content-Type", contentType);
                server.sendHeader("Cache-Control", "no-cache");
                
                // Отправляем файл
                size_t sent = server.streamFile(file, contentType);
                file.close();
                
                Serial.println("File sent: " + downloadFilename + " (" + sent + " bytes)");
            } else {
                server.send(500, "text/plain", "Cannot open file");
            }
        } else {
            server.send(404, "text/plain", "File not found");
        }
    } else {
        server.send(400, "text/plain", "File parameter required");
    }
}
 // Обработчик загрузки файлов (для штатного WebServer)
void handleTehFileUpload() {
    if (server.uri() != "/upload") {
        return;
    }
    
    HTTPUpload& upload = server.upload();
    
    if (upload.status == UPLOAD_FILE_START) {
        String filename = upload.filename;
        if (!filename.startsWith("/")) {
            filename = "/" + filename;
        }
        
        Serial.println("Upload to LittleFS: " + filename);
        // Закрываем предыдущий файл если открыт
        if (fsUploadFile) {
            fsUploadFile.close();
        }
        fsUploadFile = LittleFS.open(filename, "w");
        if (!fsUploadFile) {
            Serial.println("Failed to open file for writing");
        }
        
    } else if (upload.status == UPLOAD_FILE_WRITE) {
        if (fsUploadFile) {
            fsUploadFile.write(upload.buf, upload.currentSize);
        }
        
    } else if (upload.status == UPLOAD_FILE_END) {
        if (fsUploadFile) {
            fsUploadFile.close();
            Serial.println("Upload completed: " + String(upload.totalSize) + " bytes");
            // Не отправляем ответ здесь - это сделает основной обработчик
        }
    } else if (upload.status == UPLOAD_FILE_ABORTED) {
        if (fsUploadFile) {
            fsUploadFile.close();
        }
        Serial.println("Upload aborted");
    }
}

void setupTechnicalWebHandlers() {
    server.on("/technical", HTTP_GET, handleTechnicalPage);
    server.on("/wifi_save", HTTP_POST, handleWifiSave);
    server.on("/toggle_usb", HTTP_GET, handleToggleUSB);
    server.on("/delete_file", HTTP_GET, handleDeleteFile);
    server.on("/download", HTTP_GET, handleDownloadFile);
    
    // Важно: только один обработчик POST, который возвращает ответ
    server.on("/upload", HTTP_POST, []() {
        // Этот обработчик вызывается ПОСЛЕ завершения загрузки
        Serial.println("Upload POST completed, sending response");
        server.send(200, "text/html", 
            "<html><body>"
            "<h3>✅ Upload completed</h3>"
            "<script>"
            "setTimeout(function() { window.location.href='/technical'; }, 1000);"
            "</script>"
            "</body></html>"
        );
    }, handleTehFileUpload);  // Это функция для обработки чанков
}