     1→<?php
     2→if (!file_exists(__DIR__ . '/config.php')) {
     3→    header('Location: install.php');
     4→    exit;
     5→}
     6→require_once __DIR__ . '/config.php';
     7→?>
     8→<!DOCTYPE html>
     9→<html lang="ar" dir="rtl">
    10→<head>
    11→  <meta charset="UTF-8">
    12→  <meta name="viewport" content="width=device-width, initial-scale=1.0">
    13→  <title>محرر الصور Fouad AI</title>
    14→  <script src="https://cdn.tailwindcss.com"></script>
    15→  <script>
    16→    tailwind.config = {
    17→      darkMode: 'class',
    18→      theme: {
    19→        extend: {
    20→          fontFamily: { sans: ['Tajawal', 'Cairo', 'system-ui', 'sans-serif'] },
    21→          colors: {
    22→            primary: '#34d399',
    23→            destructive: '#ef4444',
    24→            muted: '#1e1e2e',
    25→            card: '#111118',
    26→            border: 'rgba(255,255,255,0.08)',
    27→          }
    28→        }
    29→      }
    30→    }
    31→  </script>
    32→  <style>
    33→    * { box-sizing: border-box; }
    34→    body { font-family: 'Tajawal', 'Cairo', system-ui, sans-serif; }
    35→    .glass {
    36→      background: rgba(255,255,255,0.03);
    37→      backdrop-filter: blur(20px);
    38→      -webkit-backdrop-filter: blur(20px);
    39→      border-bottom: 1px solid rgba(255,255,255,0.08);
    40→    }
    41→    .glass-card {
    42→      background: rgba(255,255,255,0.03);
    43→      backdrop-filter: blur(20px);
    44→      -webkit-backdrop-filter: blur(20px);
    45→      border: 1px solid rgba(255,255,255,0.08);
    46→      border-radius: 1rem;
    47→    }
    48→    .gradient-text {
    49→      background: linear-gradient(to right, #34d399, #22d3ee);
    50→      -webkit-background-clip: text;
    51→      -webkit-text-fill-color: transparent;
    52→      background-clip: text;
    53→    }
    54→    ::-webkit-scrollbar { width: 6px; height: 6px; }
    55→    ::-webkit-scrollbar-track { background: transparent; }
    56→    ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 3px; }
    57→    ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.2); }
    58→    .modal-backdrop {
    59→      background: rgba(0,0,0,0.6);
    60→      backdrop-filter: blur(4px);
    61→      -webkit-backdrop-filter: blur(4px);
    62→    }
    63→    .toggle-switch {
    64→      position: relative;
    65→      width: 44px;
    66→      height: 24px;
    67→      background: rgba(255,255,255,0.1);
    68→      border-radius: 12px;
    69→      cursor: pointer;
    70→      transition: background 0.3s;
    71→    }
    72→    .toggle-switch.active {
    73→      background: #34d399;
    74→    }
    75→    .toggle-switch::after {
    76→      content: '';
    77→      position: absolute;
    78→      top: 2px;
    79→      right: 2px;
    80→      width: 20px;
    81→      height: 20px;
    82→      background: white;
    83→      border-radius: 50%;
    84→      transition: transform 0.3s;
    85→    }
    86→    .toggle-switch.active::after {
    87→      transform: translateX(-20px);
    88→    }
    89→    .comparison-container { position: relative; overflow: hidden; user-select: none; touch-action: none; }
    90→    .comparison-slider-line {
    91→      position: absolute;
    92→      top: 0;
    93→      bottom: 0;
    94→      width: 3px;
    95→      background: white;
    96→      transform: translateX(-50%);
    97→      z-index: 10;
    98→      pointer-events: none;
    99→    }
   100→    .comparison-slider-handle {
   101→      position: absolute;
   102→      top: 50%;
   103→      left: 50%;
   104→      transform: translate(-50%, -50%);
   105→      width: 36px;
   106→      height: 36px;
   107→      border-radius: 50%;
   108→      background: white;
   109→      display: flex;
   110→      align-items: center;
   111→      justify-content: center;
   112→      box-shadow: 0 2px 8px rgba(0,0,0,0.4);
   113→      pointer-events: auto;
   114→      cursor: ew-resize;
   115→    }
   116→    .fade-in {
   117→      animation: fadeIn 0.3s ease forwards;
   118→    }
   119→    @keyframes fadeIn {
   120→      from { opacity: 0; transform: translateY(10px); }
   121→      to { opacity: 1; transform: translateY(0); }
   122→    }
   123→    @keyframes spin {
   124→      from { transform: rotate(0deg); }
   125→      to { transform: rotate(360deg); }
   126→    }
   127→    .animate-spin { animation: spin 1s linear infinite; }
   128→    @keyframes pulse {
   129→      0%, 100% { opacity: 1; }
   130→      50% { opacity: 0.5; }
   131→    }
   132→    .animate-pulse { animation: pulse 2s ease-in-out infinite; }
   133→    .upload-area-active {
   134→      background: rgba(52, 211, 153, 0.08) !important;
   135→      border-color: #34d399 !important;
   136→    }
   137→    .btn-primary {
   138→      background: linear-gradient(135deg, #34d399, #22d3ee);
   139→      color: #0a0a0f;
   140→      font-weight: 600;
   141→      padding: 0.5rem 1rem;
   142→      border-radius: 0.5rem;
   143→      transition: all 0.2s;
   144→      display: inline-flex;
   145→      align-items: center;
   146→      justify-content: center;
   147→      gap: 0.5rem;
   148→      border: none;
   149→      cursor: pointer;
   150→      font-family: inherit;
   151→      font-size: 0.875rem;
   152→    }
   153→    .btn-primary:hover { opacity: 0.9; transform: translateY(-1px); }
   154→    .btn-primary:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
   155→    .btn-ghost {
   156→      background: transparent;
   157→      color: rgba(255,255,255,0.7);
   158→      padding: 0.5rem 0.75rem;
   159→      border-radius: 0.5rem;
   160→      transition: all 0.2s;
   161→      display: inline-flex;
   162→      align-items: center;
   163→      justify-content: center;
   164→      gap: 0.5rem;
   165→      border: none;
   166→      cursor: pointer;
   167→      font-family: inherit;
   168→      font-size: 0.875rem;
   169→    }
   170→    .btn-ghost:hover { background: rgba(255,255,255,0.05); color: white; }
   171→    .btn-ghost.active { background: rgba(52,211,153,0.15); color: #34d399; }
   172→    .btn-outline {
   173→      background: transparent;
   174→      color: rgba(255,255,255,0.8);
   175→      padding: 0.5rem 1rem;
   176→      border-radius: 0.5rem;
   177→      border: 1px solid rgba(255,255,255,0.15);
   178→      transition: all 0.2s;
   179→      display: inline-flex;
   180→      align-items: center;
   181→      justify-content: center;
   182→      gap: 0.5rem;
   183→      cursor: pointer;
   184→      font-family: inherit;
   185→      font-size: 0.875rem;
   186→    }
   187→    .btn-outline:hover { border-color: rgba(255,255,255,0.3); background: rgba(255,255,255,0.03); }
   188→    .btn-outline:disabled { opacity: 0.5; cursor: not-allowed; }
   189→    .input-field {
   190→      width: 100%;
   191→      background: rgba(255,255,255,0.05);
   192→      border: 1px solid rgba(255,255,255,0.1);
   193→      border-radius: 0.5rem;
   194→      padding: 0.625rem 0.875rem;
   195→      color: white;
   196→      font-family: inherit;
   197→      font-size: 0.875rem;
   198→      outline: none;
   199→      transition: border-color 0.2s;
   200→    }
   201→    .input-field:focus { border-color: #34d399; }
   202→    .input-field::placeholder { color: rgba(255,255,255,0.3); }
   203→    textarea.input-field { resize: none; }
   204→    .progress-bar {
   205→      width: 100%;
   206→      height: 8px;
   207→      background: rgba(255,255,255,0.1);
   208→      border-radius: 4px;
   209→      overflow: hidden;
   210→    }
   211→    .progress-bar-fill {
   212→      height: 100%;
   213→      background: linear-gradient(to right, #34d399, #22d3ee);
   214→      border-radius: 4px;
   215→      transition: width 0.3s ease;
   216→    }
   217→    .tools-scroll {
   218→      display: flex;
   219→      gap: 0.5rem;
   220→      overflow-x: auto;
   221→      padding-bottom: 0.5rem;
   222→      -webkit-overflow-scrolling: touch;
   223→      scrollbar-width: none;
   224→    }
   225→    .tools-scroll::-webkit-scrollbar { display: none; }
   226→    @media (min-width: 1024px) {
   227→      .tools-scroll {
   228→        display: grid;
   229→        grid-template-columns: repeat(2, 1fr);
   230→        overflow-x: visible;
   231→      }
   232→    }
   233→  </style>
   234→  <link href="https://fonts.googleapis.com/css2?family=Tajawal:wght@400;500;700;800&display=swap" rel="stylesheet">
   235→</head>
   236→<body class="bg-gray-950 text-white min-h-screen">
   237→  <div id="app" class="min-h-screen flex flex-col"></div>
   238→
   239→  <script>
   240→  // ===== SVG ICONS =====
   241→  const icons = {
   242→    Upload: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>',
   243→    Image: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
   244→    Sparkles: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z"/><path d="M5 3v4"/><path d="M19 17v4"/><path d="M3 5h4"/><path d="M17 19h4"/></svg>',
   245→    Download: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>',
   246→    ArrowRight: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>',
   247→    Palette: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="13.5" cy="6.5" r=".5" fill="currentColor"/><circle cx="17.5" cy="10.5" r=".5" fill="currentColor"/><circle cx="8.5" cy="7.5" r=".5" fill="currentColor"/><circle cx="6.5" cy="12.5" r=".5" fill="currentColor"/><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 0 1 1.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z"/></svg>',
   248→    Camera: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/></svg>',
   249→    Layers: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/></svg>',
   250→    SlidersHorizontal: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="4" x2="4" y1="21" y2="14"/><line x1="4" x2="4" y1="10" y2="3"/><line x1="12" x2="12" y1="21" y2="12"/><line x1="12" x2="12" y1="8" y2="3"/><line x1="20" x2="20" y1="21" y2="16"/><line x1="20" x2="20" y1="12" y2="3"/><line x1="2" x2="6" y1="14" y2="14"/><line x1="10" x2="14" y1="8" y2="8"/><line x1="18" x2="22" y1="16" y2="16"/></svg>',
   251→    Loader2: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg>',
   252→    Check: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
   253→    Trash2: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>',
   254→    RotateCcw: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>',
   255→    Eye: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12s3-7 10-7 10 7 10 7-3 7-10 7-10-7-10-7Z"/><circle cx="12" cy="12" r="3"/></svg>',
   256→    Wand2: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M21 16h-4"/><path d="M11 3H9"/></svg>',
   257→    Sun: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>',
   258→    Paintbrush: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18.37 2.63-1 .98a2 2 0 0 0 0 2.83l6.19 6.19a2 2 0 0 0 2.83 0l.98-1A2.57 2.57 0 0 0 26.05 8l-4.05-4.05a2.57 2.57 0 0 0-3.63 0Z" transform="scale(0.75) translate(4,4)"/><path d="M9.5 13.5 3 20l1.5 1.5L11 15"/><path d="M14.5 8.5l-5 5"/></svg>',
   259→    Film: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="20" x="2" y="2" rx="2.18" ry="2.18"/><line x1="7" x2="7" y1="2" y2="22"/><line x1="17" x2="17" y1="2" y2="22"/><line x1="2" x2="22" y1="12" y2="12"/><line x1="2" x2="7" y1="7" y2="7"/><line x1="2" x2="7" y1="17" y2="17"/><line x1="17" x2="22" y1="17" y2="17"/><line x1="17" x2="22" y1="7" y2="7"/></svg>',
   260→    Pencil: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/></svg>',
   261→    History: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/><path d="M12 7v5l4 2"/></svg>',
   262→    ChevronLeft: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m15 18-6-6 6-6"/></svg>',
   263→    X: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
   264→    Settings: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/></svg>',
   265→    Megaphone: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 11 18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg>',
   266→    Code: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg>',
   267→    LogIn: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" x2="3" y1="12" y2="12"/></svg>',
   268→    LogOut: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" x2="9" y1="12" y2="12"/></svg>',
   269→    Shield: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/></svg>',
   270→    WandSparkles: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.64 3.64-1.28-1.28a1.21 1.21 0 0 0-1.72 0L2.36 18.64a1.21 1.21 0 0 0 0 1.72l1.28 1.28a1.2 1.2 0 0 0 1.72 0L21.64 5.36a1.2 1.2 0 0 0 0-1.72Z"/><path d="m14 7 3 3"/><path d="M5 6v4"/><path d="M19 14v4"/><path d="M10 2v2"/><path d="M7 8H3"/><path d="M21 16h-4"/><path d="M11 3H9"/></svg>',
   271→    Square: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2"/></svg>',
   272→    RectangleVertical: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="10" height="20" x="7" y="2" rx="2"/></svg>',
   273→    Monitor: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><line x1="8" x2="16" y1="21" y2="21"/><line x1="12" x2="12" y1="17" y2="21"/></svg>',
   274→    GripVertical: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="12" r="1"/><circle cx="9" cy="5" r="1"/><circle cx="9" cy="19" r="1"/><circle cx="15" cy="12" r="1"/><circle cx="15" cy="5" r="1"/><circle cx="15" cy="19" r="1"/></svg>'
   275→  };
   276→
   277→  function icon(name, size) {
   278→    const s = size || 16;
   279→    const svg = icons[name] || icons.Image;
   280→    return svg.replace(/width="24"/g, 'width="'+s+'"').replace(/height="24"/g, 'height="'+s+'"');
   281→  }
   282→
   283→  // ===== EDIT TOOLS =====
   284→  const EDIT_TOOLS = [
   285→    { id: 'oil-painting', label: 'لوحة زيتية', iconName: 'Paintbrush', prompt: 'Transform this image into an oil painting style, with visible brushstrokes and rich, vibrant colors, maintain the composition and subject', color: 'from-amber-500/20 to-orange-600/20' },
   286→    { id: 'cartoon', label: 'رسم كرتوني', iconName: 'Pencil', prompt: 'Transform this image into a cartoon illustration style, with bold outlines, flat colors, and simplified shapes, keep the main subject recognizable', color: 'from-pink-500/20 to-rose-600/20' },
   287→    { id: 'bw', label: 'أبيض وأسود', iconName: 'Camera', prompt: 'Convert this image to a stunning black and white photograph with high contrast and dramatic lighting, professional photography style', color: 'from-gray-500/20 to-gray-700/20' },
   288→    { id: 'golden-hour', label: 'إضاءة ذهبية', iconName: 'Sun', prompt: 'Apply warm golden hour lighting to this image, with beautiful warm tones, soft shadows, and a magical sunset glow, maintain all subjects', color: 'from-yellow-500/20 to-amber-600/20' },
   289→    { id: 'enhance', label: 'تحسين الجودة', iconName: 'Sparkles', prompt: 'Enhance the quality and sharpness of this image, improve colors, add more detail and clarity, make it look professional and high-resolution', color: 'from-emerald-500/20 to-teal-600/20' },
   290→    { id: 'bg-remove', label: 'خلفية احترافية', iconName: 'Layers', prompt: 'Enhance this photo with a clean neutral studio background while preserving the main subject naturally', color: 'from-cyan-500/20 to-blue-600/20' },
   291→    { id: 'cinematic', label: 'تأثير سينمائي', iconName: 'Film', prompt: 'Apply a cinematic color grading to this image, with deep shadows, teal and orange tones, letterbox style, dramatic and moody atmosphere', color: 'from-violet-500/20 to-purple-600/20' },
   292→    { id: 'illustration', label: 'أسلوب رسومي', iconName: 'Palette', prompt: 'Transform this image into a digital illustration style, clean lines, artistic rendering, vibrant but harmonious colors, maintain the scene composition', color: 'from-fuchsia-500/20 to-pink-600/20' },
   293→  ];
   294→
   295→  const SIZE_OPTIONS = [
   296→    { id: 'square', label: 'مربع', iconName: 'Square', desc: '1024×1024', color: 'from-emerald-500/20 to-emerald-600/5' },
   297→    { id: 'portrait', label: 'عمودي', iconName: 'RectangleVertical', desc: '864×1152', color: 'from-amber-500/20 to-amber-600/5' },
   298→    { id: 'landscape', label: 'أفقي', iconName: 'Monitor', desc: '1344×768', color: 'from-emerald-500/20 to-emerald-600/5' },
   299→    { id: 'wide', label: 'عريض', iconName: 'Monitor', desc: '1440×720', color: 'from-violet-500/20 to-violet-600/5' },
   300→  ];
   301→
   302→  const POSITION_OPTIONS = [
   303→    { value: 'top', label: 'أعلى الصفحة' },
   304→    { value: 'above-upload', label: 'فوق منطقة الرفع' },
   305→    { value: 'below-features', label: 'أسفل المميزات' },
   306→  ];
   307→
   308→  const EXAMPLE_PROMPTS = [
   309→    'غروب على شاطئ بصخور ملساء',
   310→    'مدينة مستقبلية بالليل',
   311→    'طائر فيلمنق زاهي الألوان',
   312→    'قرية جبلية وسط الضباب',
   313→  ];
   314→
   315→  // ===== APPLICATION STATE =====
   316→  const App = {
   317→    state: {
   318→      view: 'home',
   319→      isAdmin: false,
   320→      isAdminLoading: true,
   321→      showLoginDialog: false,
   322→      showBannerDialog: false,
   323→      originalImage: null,
   324→      originalFileName: null,
   325→      editedImage: null,
   326→      isEditing: false,
   327→      editProgress: 0,
   328→      editError: null,
   329→      analysis: null,
   330→      customPrompt: '',
   331→      selectedTool: null,
   332→      editCount: 0,
   333→      isAnalyzing: false,
   334→      lastEditArgs: null,
   335→      isUploading: false,
   336→      isDragOver: false,
   337→      projects: [],
   338→      isGalleryLoading: true,
   339→      // Generate view
   340→      genPrompt: '',
   341→      genSelectedSize: 'square',
   342→      genIsGenerating: false,
   343→      genImage: null,
   344→      genError: null,
   345→      genProgress: 0,
   346→      genProgressMsg: '',
   347→      genPollInterval: null,
   348→      // Banner state
   349→      banners: { top: null, 'above-upload': null, 'below-features': null },
   350→      bannerDismissed: {},
   351→      // Banner admin
   352→      bannerAdType: 'image',
   353→      bannerImageUrl: '',
   354→      bannerAdCode: '',
   355→      bannerLinkUrl: '',
   356→      bannerText: '',
   357→      bannerActive: false,
   358→      bannerPosition: 'top',
   359→      bannerSaving: false,
   360→      bannerUploading: false,
   361→      bannerMessage: null,
   362→      // Login
   363→      loginUsername: '',
   364→      loginPassword: '',
   365→      loginLoading: false,
   366→      loginError: null,
   367→      // Toast
   368→      toast: null,
   369→      toastTimer: null,
   370→    },
   371→
   372→    init() {
   373→      this.checkSession();
   374→      this.fetchBanner('top');
   375→      this.fetchBanner('above-upload');
   376→      this.fetchBanner('below-features');
   377→      this.render();
   378→
   379→      // Global event delegation
   380→      document.getElementById('app').addEventListener('click', (e) => {
   381→        this.handleClick(e);
   382→      });
   383→      document.getElementById('app').addEventListener('input', (e) => {
   384→        this.handleInput(e);
   385→      });
   386→      document.getElementById('app').addEventListener('submit', (e) => {
   387→        this.handleSubmit(e);
   388→      });
   389→      document.getElementById('app').addEventListener('dragover', (e) => {
   390→        this.handleDragOver(e);
   391→      });
   392→      document.getElementById('app').addEventListener('dragleave', (e) => {
   393→        this.handleDragLeave(e);
   394→      });
   395→      document.getElementById('app').addEventListener('drop', (e) => {
   396→        this.handleDrop(e);
   397→      });
   398→      document.getElementById('app').addEventListener('change', (e) => {
   399→        this.handleChange(e);
   400→      });
   401→      document.getElementById('app').addEventListener('pointerdown', (e) => {
   402→        this.handlePointerDown(e);
   403→      });
   404→      document.getElementById('app').addEventListener('pointermove', (e) => {
   405→        this.handlePointerMove(e);
   406→      });
   407→      document.getElementById('app').addEventListener('pointerup', (e) => {
   408→        this.handlePointerUp(e);
   409→      });
   410→    },
   411→
   412→    setState(partial) {
   413→      Object.assign(this.state, partial);
   414→    },
   415→
   416→    showToast(message, type) {
   417→      if (this.state.toastTimer) clearTimeout(this.state.toastTimer);
   418→      this.setState({ toast: { message, type: type || 'success' } });
   419→      this.state.toastTimer = setTimeout(() => {
   420→        this.setState({ toast: null });
   421→        this.render();
   422→      }, 5000);
   423→      this.render();
   424→    },
   425→
   426→    async checkSession() {
   427→      try {
   428→        const res = await fetch('/api/auth/session');
   429→        if (!res.ok) {
   430→          this.setState({ isAdmin: false, isAdminLoading: false });
   431→          this.render();
   432→          return;
   433→        }
   434→        const session = await res.json();
   435→        this.setState({ isAdmin: session.user && session.user.role === 'admin', isAdminLoading: false });
   436→      } catch {
   437→        this.setState({ isAdmin: false, isAdminLoading: false });
   438→      }
   439→      this.render();
   440→    },
   441→
   442→    async fetchBanner(position) {
   443→      try {
   444→        const res = await fetch('/api/banner');
   445→        if (!res.ok) return;
   446→        const data = await res.json();
   447→        if (data.banner && data.banner.position === position) {
   448→          const banners = Object.assign({}, this.state.banners);
   449→          banners[position] = data.banner;
   450→          this.setState({ banners });
   451→          this.render();
   452→        }
   453→      } catch {}
   454→    },
   455→
   456→    // ===== RENDERING =====
   457→    render() {
   458→      const app = document.getElementById('app');
   459→      let html = this.renderHeader();
   460→      html += '<div class="w-full px-4 sm:px-6 lg:px-8">' + this.renderBanner('top') + '</div>';
   461→
   462→      if (this.state.view === 'home') {
   463→        html += this.renderHomeView();
   464→      } else if (this.state.view === 'editor') {
   465→        html += this.renderEditorView();
   466→      } else if (this.state.view === 'gallery') {
   467→        html += this.renderGalleryView();
   468→      } else if (this.state.view === 'generate') {
   469→        html += this.renderGenerateView();
   470→      }
   471→
   472→      html += this.renderFooter();
   473→
   474→      if (this.state.showLoginDialog) {
   475→        html += this.renderLoginDialog();
   476→      }
   477→      if (this.state.showBannerDialog) {
   478→        html += this.renderBannerAdminDialog();
   479→      }
   480→      if (this.state.toast) {
   481→        html += this.renderToast();
   482→      }
   483→
   484→      app.innerHTML = html;
   485→
   486→      // Restore input values that may be lost on re-render
   487→      this.restoreInputValues();
   488→
   489→      // Initialize comparison slider if in editor with edited image
   490→      if (this.state.view === 'editor' && this.state.editedImage) {
   491→        this.initComparisonSlider();
   492→      }
   493→    },
   494→
   495→    restoreInputValues() {
   496→      if (this.state.view === 'editor') {
   497→        const ta = document.getElementById('custom-prompt-area');
   498→        if (ta && document.activeElement !== ta) ta.value = this.state.customPrompt;
   499→      }
   500→      if (this.state.view === 'generate') {
   501→        const ta = document.getElementById('gen-prompt-area');
   502→        if (ta && document.activeElement !== ta) ta.value = this.state.genPrompt;
   503→      }
   504→      if (this.state.showLoginDialog) {
   505→        const u = document.getElementById('login-username');
   506→        const p = document.getElementById('login-password');
   507→        if (u && document.activeElement !== u) u.value = this.state.loginUsername;
   508→        if (p && document.activeElement !== p) p.value = this.state.loginPassword;
   509→      }
   510→      if (this.state.showBannerDialog) {
   511→        if (this.state.bannerAdType === 'image') {
   512→          const link = document.getElementById('banner-link-url');
   513→          const text = document.getElementById('banner-text-overlay');
   514→          if (link && document.activeElement !== link) link.value = this.state.bannerLinkUrl;
   515→          if (text && document.activeElement !== text) text.value = this.state.bannerText;
   516→        } else {
   517→          const code = document.getElementById('banner-ad-code');
   518→          if (code && document.activeElement !== code) code.value = this.state.bannerAdCode;
   519→        }
   520→      }
   521→    },
   522→
   523→    // ===== HEADER =====
   524→    renderHeader() {
   525→      const s = this.state;
   526→      const navBtn = (view, label, iconName) => {
   527→        const active = s.view === view;
   528→        return '<button data-action="nav" data-view="' + view + '" class="btn-ghost gap-2 ' + (active ? 'active' : '') + '">' +
   529→          icon(iconName, 16) +
   530→          '<span class="hidden sm:inline">' + label + '</span>' +
   531→          '</button>';
   532→      };
   533→
   534→      let adminBtns = '';
   535→      if (!s.isAdminLoading) {
   536→        if (s.isAdmin) {
   537→          adminBtns =
   538→            '<button data-action="open-banner-admin" class="btn-ghost mr-1" title="إدارة البانر">' + icon('Settings', 16) + '</button>' +
   539→            '<button data-action="logout" class="btn-ghost text-red-400 hover:text-red-300 hover:bg-red-500/10" title="تسجيل الخروج">' + icon('LogOut', 16) + '</button>';
   540→        } else {
   541→          adminBtns =
   542→            '<button data-action="open-login" class="btn-ghost mr-1" title="دخول المدير">' + icon('LogIn', 16) + '</button>';
   543→        }
   544→      }
   545→
   546→      return '<header class="glass sticky top-0 z-50">' +
   547→        '<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">' +
   548→          '<button data-action="nav" data-view="home" class="flex items-center gap-3 group">' +
   549→            '<div class="w-9 h-9 rounded-lg bg-emerald-500/20 flex items-center justify-center">' +
   550→              icon('Wand2', 20) +
   551→            '</div>' +
   552→            '<span class="text-lg font-bold gradient-text">محرر الصور Fouad AI</span>' +
   553→          '</button>' +
   554→          '<nav class="flex items-center gap-1">' +
   555→            navBtn('home', 'الرئيسية', 'Image') +
   556→            navBtn('gallery', 'المعرض', 'History') +
   557→            navBtn('generate', 'توليد الصور', 'WandSparkles') +
   558→            adminBtns +
   559→          '</nav>' +
   560→        '</div>' +
   561→      '</header>';
   562→    },
   563→
   564→    // ===== BANNER DISPLAY =====
   565→    renderBanner(position) {
   566→      const banner = this.state.banners[position];
   567→      if (!banner || !banner.active || this.state.bannerDismissed[position]) return '';
   568→
   569→      if (banner.adType === 'external' && banner.adCode) {
   570→        return '<div class="relative w-full max-w-4xl mx-auto my-3 fade-in">' +
   571→          '<div class="rounded-xl overflow-hidden bg-white/[0.02] border border-white/[0.08] p-1">' +
   572→            '<div class="w-full min-h-[60px] flex items-center justify-center">' + banner.adCode + '</div>' +
   573→          '</div>' +
   574→          '<button data-action="dismiss-banner" data-position="' + position + '" class="absolute -top-2 -left-2 w-6 h-6 rounded-full bg-white/10 text-white/60 flex items-center justify-center hover:bg-white/20 hover:text-white transition-colors border border-white/10">' +
   575→            icon('X', 12) +
   576→          '</button>' +
   577→        '</div>';
   578→      }
   579→
   580→      if (!banner.imageUrl) return '';
   581→
   582→      let wrapper = '<div class="block relative rounded-xl overflow-hidden">';
   583→      let closeBtn = '';
   584→      if (banner.text) {
   585→        wrapper = '<a href="' + (banner.linkUrl || '#') + '" target="_blank" rel="noopener noreferrer" class="block relative rounded-xl overflow-hidden">';
   586→      }
   587→
   588→      closeBtn = '<button data-action="dismiss-banner" data-position="' + position + '" class="absolute top-2 left-2 w-6 h-6 rounded-full bg-black/50 text-white flex items-center justify-center hover:bg-black/70 transition-colors z-10">' + icon('X', 12) + '</button>';
   589→
   590→      return '<div class="relative w-full max-w-4xl mx-auto my-3 fade-in">' +
   591→        wrapper +
   592→          '<img src="' + banner.imageUrl + '" alt="' + (banner.text || 'إعلان') + '" class="w-full h-auto object-cover max-h-48 rounded-xl" />' +
   593→          (banner.text ? '<div class="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent flex items-end p-4"><p class="text-white text-sm sm:text-base font-medium">' + banner.text + '</p></div>' : '') +
   594→        (banner.linkUrl ? '</a>' : '</div>') +
   595→        closeBtn +
   596→      '</div>';
   597→    },
   598→
   599→    // ===== LOGIN DIALOG =====
   600→    renderLoginDialog() {
   601→      const s = this.state;
   602→      const canSubmit = s.loginUsername.trim() && s.loginPassword.trim() && !s.loginLoading;
   603→
   604→      return '<div class="fixed inset-0 z-[100] flex items-center justify-center modal-backdrop fade-in" data-action="close-login-backdrop">' +
   605→        '<div class="bg-gray-900 border border-white/10 rounded-xl p-6 max-w-sm w-full mx-4 shadow-2xl fade-in" onclick="event.stopPropagation()">' +
   606→          '<div class="flex items-center justify-between mb-4">' +
   607→            '<h2 class="flex items-center gap-2 text-lg font-semibold">' +
   608→              icon('Shield', 20) + ' تسجيل دخول المدير' +
   609→            '</h2>' +
   610→            '<button data-action="close-login" class="btn-ghost p-1">' + icon('X', 18) + '</button>' +
   611→          '</div>' +
   612→          '<form id="login-form" class="space-y-4">' +
   613→            '<div class="space-y-2">' +
   614→              '<label class="text-sm text-white/70">اسم المستخدم</label>' +
   615→              '<input id="login-username" type="text" value="' + this.escHtml(s.loginUsername) + '" placeholder="admin" autocomplete="username" autofocus class="input-field" />' +
   616→            '</div>' +
   617→            '<div class="space-y-2">' +
   618→              '<label class="text-sm text-white/70">كلمة المرور</label>' +
   619→              '<input id="login-password" type="password" value="' + this.escHtml(s.loginPassword) + '" placeholder="••••••••" autocomplete="current-password" class="input-field" />' +
   620→            '</div>' +
   621→            (s.loginError ? '<p class="text-sm text-red-400 bg-red-500/10 p-2 rounded-lg text-center">' + this.escHtml(s.loginError) + '</p>' : '') +
   622→            '<button type="submit" class="btn-primary w-full gap-2" ' + (canSubmit ? '' : 'disabled') + '>' +
   623→              (s.loginLoading ? '<span class="animate-spin inline-block">' + icon('Loader2', 16) + '</span> جارٍ التحقق...' : icon('LogIn', 16) + ' تسجيل الدخول') +
   624→            '</button>' +
   625→          '</form>' +
   626→        '</div>' +
   627→      '</div>';
   628→    },
   629→
   630→    // ===== BANNER ADMIN DIALOG =====
   631→    renderBannerAdminDialog() {
   632→      const s = this.state;
   633→      const isImage = s.bannerAdType === 'image';
   634→
   635→      let imageSection = '';
   636→      if (isImage) {
   637→        let uploadContent = '';
   638→        if (s.bannerUploading) {
   639→          uploadContent = '<div class="flex items-center justify-center gap-2 py-2"><span class="animate-spin inline-block text-emerald-400">' + icon('Loader2', 20) + '</span><span class="text-sm text-white/50">جارٍ الرفع...</span></div>';
   640→        } else if (s.bannerImageUrl) {
   641→          uploadContent = '<div class="space-y-2"><img src="' + this.escHtml(s.bannerImageUrl) + '" alt="بانر" class="w-full h-24 object-cover rounded-md" /><p class="text-xs text-white/50">انقر لتغيير الصورة</p></div>';
   642→        } else {
   643→          uploadContent = '<div class="py-2"><div class="mx-auto text-white/20 mb-2">' + icon('Upload', 32) + '</div><p class="text-sm text-white/50">انقر لاختيار صورة البانر</p><p class="text-xs text-white/30 mt-1">PNG, JPG, WEBP — حتى 2 ميجا</p></div>';
   644→        }
   645→
   646→        imageSection = '<div class="space-y-2">' +
   647→          '<label class="text-sm text-white/70">صورة البانر</label>' +
   648→          '<div data-action="banner-upload-click" class="relative rounded-lg border-2 border-dashed p-4 text-center cursor-pointer transition-all ' + (s.bannerImageUrl ? 'border-emerald-500/30' : 'border-white/20 hover:border-emerald-500/50') + '">' +
   649→            '<input id="banner-file-input" type="file" accept="image/*" class="hidden" />' +
   650→            uploadContent +
   651→          '</div>' +
   652→          '<div class="space-y-2 mt-3">' +
   653→            '<label class="text-sm text-white/70">رابط الإعلان (اختياري)</label>' +
   654→            '<input id="banner-link-url" type="text" value="' + this.escHtml(s.bannerLinkUrl) + '" placeholder="https://example.com" dir="ltr" class="input-field" />' +
   655→          '</div>' +
   656→          '<div class="space-y-2 mt-3">' +
   657→            '<label class="text-sm text-white/70">نص على البانر (اختياري)</label>' +
   658→            '<input id="banner-text-overlay" type="text" value="' + this.escHtml(s.bannerText) + '" placeholder="عنوان الإعلان..." class="input-field" />' +
   659→          '</div>' +
   660→        '</div>';
   661→      }
   662→
   663→      let externalSection = '';
   664→      if (!isImage) {
   665→        externalSection = '<div class="space-y-2">' +
   666→          '<label class="text-sm text-white/70">كود الإعلان الخارجي</label>' +
   667→          '<p class="text-xs text-white/40">الصق كود الإعلان من جوجل أدسنس أو أي شبكة إعلانية أخرى</p>' +
   668→          '<textarea id="banner-ad-code" class="input-field min-h-[120px] font-mono text-xs" dir="ltr" placeholder="&lt;script async src=&quot;https://pagead2.googlesyndication.com/...&quot;&gt;&lt;/script&gt;">' + this.escHtml(s.bannerAdCode) + '</textarea>' +
   669→        '</div>';
   670→      }
   671→
   672→      let messageHtml = '';
   673→      if (s.bannerMessage) {
   674→        const msgColor = s.bannerMessage.type === 'success' ? 'bg-emerald-500/10 text-emerald-400' : 'bg-red-500/10 text-red-400';
   675→        messageHtml = '<div class="text-sm p-3 rounded-lg ' + msgColor + '">' + this.escHtml(s.bannerMessage.text) + '</div>';
   676→      }
   677→
   678→      const positionBtns = POSITION_OPTIONS.map(opt => {
   679→        const active = s.bannerPosition === opt.value;
   680→        return '<button data-action="banner-position" data-value="' + opt.value + '" class="p-2 rounded-lg border text-xs font-medium transition-all ' +
   681→          (active ? 'border-emerald-500 bg-emerald-500/10 text-emerald-400' : 'border-white/10 hover:border-emerald-500/30 text-white/50') + '">' + opt.label + '</button>';
   682→      }).join('');
   683→
   684→      const canSave = isImage ? !!s.bannerImageUrl : !!s.bannerAdCode.trim();
   685→
   686→      return '<div class="fixed inset-0 z-[100] flex items-center justify-center modal-backdrop fade-in" data-action="close-banner-backdrop">' +
   687→        '<div class="bg-gray-900 border border-white/10 rounded-xl p-6 max-w-md w-full mx-4 shadow-2xl max-h-[90vh] overflow-y-auto fade-in" onclick="event.stopPropagation()">' +
   688→          '<div class="flex items-center justify-between mb-4">' +
   689→            '<h2 class="flex items-center gap-2 text-lg font-semibold">' + icon('Megaphone', 20) + ' إدارة البانر الإعلاني</h2>' +
   690→            '<button data-action="close-banner" class="btn-ghost p-1">' + icon('X', 18) + '</button>' +
   691→          '</div>' +
   692→          '<div class="space-y-4">' +
   693→            // Ad type
   694→            '<div class="space-y-2">' +
   695→              '<label class="text-sm text-white/70">نوع الإعلان</label>' +
   696→              '<div class="grid grid-cols-2 gap-2">' +
   697→                '<button data-action="banner-type" data-type="image" class="p-3 rounded-lg border text-sm font-medium transition-all flex flex-col items-center gap-1.5 ' +
   698→                  (isImage ? 'border-emerald-500 bg-emerald-500/10 text-emerald-400' : 'border-white/10 hover:border-emerald-500/30 text-white/50') + '">' +
   699→                  icon('Image', 20) + ' صورة مرفوعة</button>' +
   700→                '<button data-action="banner-type" data-type="external" class="p-3 rounded-lg border text-sm font-medium transition-all flex flex-col items-center gap-1.5 ' +
   701→                  (!isImage ? 'border-emerald-500 bg-emerald-500/10 text-emerald-400' : 'border-white/10 hover:border-emerald-500/30 text-white/50') + '">' +
   702→                  icon('Code', 20) + ' كود خارجي</button>' +
   703→              '</div>' +
   704→            '</div>' +
   705→            imageSection +
   706→            externalSection +
   707→            // Position
   708→            '<div class="space-y-2">' +
   709→              '<label class="text-sm text-white/70">موضع البانر</label>' +
   710→              '<div class="grid grid-cols-3 gap-2">' + positionBtns + '</div>' +
   711→            '</div>' +
   712→            // Active toggle
   713→            '<div class="flex items-center justify-between p-3 rounded-lg border border-white/10">' +
   714→              '<label class="cursor-pointer text-sm">عرض البانر</label>' +
   715→              '<div data-action="toggle-banner-active" class="toggle-switch ' + (s.bannerActive ? 'active' : '') + '"></div>' +
   716→            '</div>' +
   717→            messageHtml +
   718→            // Save
   719→            '<button data-action="save-banner" class="btn-primary w-full gap-2" ' + (s.bannerSaving || !canSave ? 'disabled' : '') + '>' +
   720→              (s.bannerSaving ? '<span class="animate-spin inline-block">' + icon('Loader2', 16) + '</span> جارٍ الحفظ...' : icon('Check', 16) + ' حفظ البانر') +
   721→            '</button>' +
   722→          '</div>' +
   723→        '</div>' +
   724→      '</div>';
   725→    },
   726→
   727→    // ===== HOME VIEW =====
   728→    renderHomeView() {
   729→      return '<main class="flex-1 flex flex-col items-center px-4 py-12">' +
   730→        '<div class="my-6 w-full max-w-4xl mx-auto">' + this.renderBanner('above-upload') + '</div>' +
   731→        '<div class="text-center mb-10 fade-in">' +
   732→          '<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold mb-4">' +
   733→            '<span class="gradient-text">محرر الصور Fouad AI</span>' +
   734→          '</h1>' +
   735→          '<p class="text-lg sm:text-xl text-white/50 max-w-xl mx-auto">ارفع صورتك واختر التأثير المطلوب — وشاهد الذكاء الاصطناعي يُبدع</p>' +
   736→        '</div>' +
   737→        this.renderUploadArea() +
   738→        this.renderFeatureCards() +
   739→        '<div class="my-6 w-full max-w-4xl mx-auto">' + this.renderBanner('below-features') + '</div>' +
   740→      '</main>';
   741→    },
   742→
   743→    // ===== UPLOAD AREA =====
   744→    renderUploadArea() {
   745→      const s = this.state;
   746→      if (s.isUploading) {
   747→        return '<div class="w-full max-w-2xl mx-auto fade-in">' +
   748→          '<div class="rounded-2xl border-2 border-dashed border-emerald-500/30 p-12 sm:p-16 text-center">' +
   749→            '<div class="flex flex-col items-center gap-4">' +
   750→              '<span class="animate-spin inline-block text-emerald-400">' + icon('Loader2', 48) + '</span>' +
   751→              '<p class="text-lg text-white/50">جارٍ رفع الصورة...</p>' +
   752→            '</div>' +
   753→          '</div>' +
   754→        '</div>';
   755→      }
   756→
   757→      return '<div class="w-full max-w-2xl mx-auto fade-in">' +
   758→        '<div data-action="upload-click" class="relative cursor-pointer rounded-2xl border-2 border-dashed p-12 sm:p-16 transition-all duration-300 text-center ' +
   759→          (s.isDragOver ? 'upload-area-active border-emerald-500' : 'border-white/20 hover:border-emerald-500/50 hover:bg-emerald-500/5') + '">' +
   760→          '<input id="upload-file-input" type="file" accept="image/*" class="hidden" />' +
   761→          '<div class="flex flex-col items-center gap-4">' +
   762→            '<div class="w-20 h-20 rounded-full bg-emerald-500/10 flex items-center justify-center">' +
   763→              '<span class="text-emerald-400">' + icon('Upload', 40) + '</span>' +
   764→            '</div>' +
   765→            '<div>' +
   766→              '<p class="text-xl font-semibold mb-2">اسحب الصورة هنا أو انقر للاختيار</p>' +
   767→              '<p class="text-white/40 text-sm">يدعم: PNG, JPG, WEBP — حتى 10 ميجا</p>' +
   768→            '</div>' +
   769→          '</div>' +
   770→        '</div>' +
   771→      '</div>';
   772→    },
   773→
   774→    // ===== FEATURE CARDS =====
   775→    renderFeatureCards() {
   776→      const features = [
   777→        { iconName: 'Sparkles', title: 'تحرير بالذكاء', desc: 'أدوات تعديل ذكية تفهم صورتك وتحسّنها' },
   778→        { iconName: 'Palette', title: 'أنماط فنية', desc: 'حوّل صورك إلى لوحات زيتية أو رسوم كرتونية' },
   779→        { iconName: 'SlidersHorizontal', title: 'مقارنة فورية', desc: 'قارن بين الأصل والنسخة المحرّرة بسهولة' },
   780→        { iconName: 'Download', title: 'تحميل مباشر', desc: 'حمّل الصور المحرّرة بجودة عالية' },
   781→      ];
   782→
   783→      const cards = features.map((f, i) => {
   784→        return '<div class="glass-card h-full text-center p-4 sm:p-6 fade-in" style="animation-delay:' + (0.2 + i * 0.1) + 's">' +
   785→          '<div class="flex flex-col items-center gap-3">' +
   786→            '<div class="w-12 h-12 rounded-xl bg-emerald-500/15 flex items-center justify-center">' +
   787→              '<span class="text-emerald-400">' + icon(f.iconName, 24) + '</span>' +
   788→            '</div>' +
   789→            '<h3 class="font-semibold text-sm sm:text-base">' + f.title + '</h3>' +
   790→            '<p class="text-xs sm:text-sm text-white/50">' + f.desc + '</p>' +
   791→          '</div>' +
   792→        '</div>';
   793→      }).join('');
   794→
   795→      return '<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 max-w-5xl mx-auto mt-16">' + cards + '</div>';
   796→    },
   797→
   798→    // ===== EDITOR VIEW =====
   799→    renderEditorView() {
   800→      const s = this.state;
   801→      const hasEdited = !!s.editedImage;
   802→
   803→      let topBar = '<div class="flex items-center justify-between mb-6 fade-in">' +
   804→        '<div class="flex items-center gap-2">' +
   805→          '<button data-action="new-image" class="btn-ghost gap-2">' + icon('ChevronLeft', 16) + ' صورة جديدة</button>' +
   806→          (hasEdited ? '<button data-action="reset-original" class="btn-outline gap-1.5 text-xs">' + icon('RotateCcw', 14) + ' العودة للأصل</button>' : '') +
   807→          (s.editCount > 0 ? '<span class="text-xs text-white/50 bg-white/5 px-2 py-1 rounded-full">' + s.editCount + ' تعديل' + (s.editCount > 1 ? 'ات' : '') + '</span>' : '') +
   808→        '</div>' +
   809→        (hasEdited ? '<button data-action="download-edited" class="btn-primary gap-2">' + icon('Download', 16) + ' تحميل الصورة</button>' : '') +
   810→      '</div>';
   811→
   812→      // Image preview
   813→      let imageContent = '';
   814→      if (s.isEditing) {
   815→        imageContent = '<div class="absolute inset-0 z-20 flex flex-col items-center justify-center bg-black/50 backdrop-blur-sm">' +
   816→          '<span class="animate-spin inline-block text-emerald-400 mb-4">' + icon('Loader2', 48) + '</span>' +
   817→          '<p class="text-lg font-semibold mb-2">جارٍ التعديل بالذكاء الاصطناعي...</p>' +
   818→          '<div class="progress-bar w-48"><div class="progress-bar-fill" style="width:' + s.editProgress + '%"></div></div>' +
   819→          '<p class="text-sm text-white/50 mt-2">' + s.editProgress + '%</p>' +
   820→        '</div>';
   821→      }
   822→
   823→      let imageDisplay = '';
   824→      if (hasEdited) {
   825→        imageDisplay = '<div id="comparison-container" class="comparison-container rounded-xl overflow-hidden bg-black/50 relative w-full">' +
   826→          '<img src="' + this.escHtml(s.editedImage) + '" alt="بعد" class="w-full h-auto block" draggable="false" />' +
   827→          '<div id="comparison-original-clip" class="absolute inset-0 overflow-hidden" style="width:50%">' +
   828→            '<img src="' + this.escHtml(s.originalImage) + '" alt="قبل" class="w-full h-auto block" id="comparison-original-img" draggable="false" />' +
   829→          '</div>' +
   830→          '<div id="comparison-line" class="comparison-slider-line" style="left:50%">' +
   831→            '<div class="comparison-slider-handle">' + icon('GripVertical', 20) + '</div>' +
   832→          '</div>' +
   833→          '<div class="absolute top-3 right-3 bg-black/60 text-white text-xs px-2 py-1 rounded-md">الأصلي</div>' +
   834→          '<div class="absolute top-3 left-3 bg-emerald-500/80 text-white text-xs px-2 py-1 rounded-md">المحرّر</div>' +
   835→        '</div>';
   836→      } else if (s.originalImage) {
   837→        imageDisplay = '<img src="' + this.escHtml(s.originalImage) + '" alt="Original" class="max-w-full max-h-[70vh] object-contain mx-auto" />';
   838→      }
   839→
   840→      // Info bar
   841→      let infoBar = '<div class="p-3 border-t border-white/10 flex items-center justify-between text-sm text-white/50">' +
   842→        '<span class="truncate max-w-[60%]">' + (s.originalFileName || 'صورة') + '</span>' +
   843→        '<button data-action="reupload" class="btn-ghost gap-1 text-xs h-8">' + icon('RotateCcw', 12) + ' تغيير الصورة</button>' +
   844→        '<input id="reupload-file-input" type="file" accept="image/*" class="hidden" />' +
   845→      '</div>';
   846→
   847→      // Analysis section
   848→      let analysisSection = '<div class="mt-4">' +
   849→        '<button data-action="analyze-image" class="btn-outline w-full gap-2 mb-3" ' + (s.isAnalyzing || !s.originalImage ? 'disabled' : '') + '>' +
   850→          (s.isAnalyzing ? '<span class="animate-spin inline-block">' + icon('Loader2', 16) + '</span>' : icon('Eye', 16)) +
   851→          ' تحليل الصورة بالذكاء الاصطناعي</button>';
   852→
   853→      if (s.analysis) {
   854→        analysisSection += '<div class="glass-card p-4 fade-in">' +
   855→          '<h3 class="text-sm font-semibold mb-2 flex items-center gap-2">' + icon('Eye', 16) + ' <span class="text-emerald-400">نتيجة التحليل</span></h3>' +
   856→          '<p class="text-sm text-white/50 leading-relaxed whitespace-pre-wrap">' + this.escHtml(s.analysis) + '</p>' +
   857→        '</div>';
   858→      }
   859→      analysisSection += '</div>';
   860→
   861→      // Edit tools
   862→      const toolCards = EDIT_TOOLS.map(tool => {
   863→        const isLoading = s.isEditing && s.selectedTool === tool.id;
   864→        return '<button data-action="use-tool" data-tool-id="' + tool.id + '" class="tool-card relative flex flex-col items-center gap-2 p-3 rounded-xl border border-white/10 text-center transition-all ' +
   865→          (isLoading ? 'ring-2 ring-emerald-500' : 'hover:border-emerald-500/40') + ' ' +
   866→          (s.isEditing ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer') + '" ' + (s.isEditing ? 'disabled' : '') + '>' +
   867→          '<div class="w-10 h-10 rounded-lg bg-gradient-to-br ' + tool.color + ' flex items-center justify-center">' +
   868→            (isLoading ? '<span class="animate-spin inline-block text-emerald-400">' + icon('Loader2', 20) + '</span>' : icon(tool.iconName, 20)) +
   869→          '</div>' +
   870→          '<span class="text-xs font-medium">' + tool.label + '</span>' +
   871→        '</button>';
   872→      }).join('');
   873→
   874→      const toolsPanel = '<div class="glass-card p-4">' +
   875→        '<h3 class="font-semibold mb-3 flex items-center gap-2">' + icon('Wand2', 16) + ' <span class="text-emerald-400">أدوات التحرير السريعة</span></h3>' +
   876→        '<div class="tools-scroll">' + toolCards + '</div>' +
   877→      '</div>';
   878→
   879→      // Custom prompt
   880→      const customPromptPanel = '<div class="glass-card p-4">' +
   881→        '<h3 class="font-semibold mb-3 flex items-center gap-2">' + icon('Pencil', 16) + ' <span class="text-emerald-400">تعديل مخصص</span></h3>' +
   882→        '<textarea id="custom-prompt-area" class="input-field min-h-[100px] resize-none mb-3" placeholder="صِف التعديل الذي تريده... مثال: غيّر الخلفية إلى شاطئ عند الغروب" ' + (s.isEditing ? 'disabled' : '') + '>' + this.escHtml(s.customPrompt) + '</textarea>' +
   883→        '<button data-action="custom-edit" class="btn-primary w-full gap-2" ' + (s.isEditing || !s.customPrompt.trim() ? 'disabled' : '') + '>' +
   884→          (s.isEditing ? '<span class="animate-spin inline-block">' + icon('Loader2', 16) + '</span>' : icon('Sparkles', 16)) +
   885→          ' تطبيق التعديل</button>' +
   886→      '</div>';
   887→
   888→      // Error
   889→      let errorSection = '';
   890→      if (s.editError) {
   891→        let retryBtn = '';
   892→        if (s.lastEditArgs) {
   893→          retryBtn = '<button data-action="retry-edit" class="btn-outline shrink-0 gap-1 text-xs">' + icon('RotateCcw', 12) + ' إعادة المحاولة</button>';
   894→        }
   895→        errorSection = '<div class="border-red-500/50 bg-red-500/10 rounded-xl p-4 flex items-start gap-3 fade-in">' +
   896→          '<span class="text-red-400 mt-0.5 shrink-0">' + icon('X', 20) + '</span>' +
   897→          '<div class="flex-1">' +
   898→            '<p class="text-sm font-medium text-red-400">حدث خطأ</p>' +
   899→            '<p class="text-xs text-white/50 mt-1">' + this.escHtml(s.editError) + '</p>' +
   900→          '</div>' +
   901→          retryBtn +
   902→        '</div>';
   903→      }
   904→
   905→      return '<main class="flex-1 px-4 py-6 max-w-7xl mx-auto w-full">' +
   906→        topBar +
   907→        '<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">' +
   908→          '<div class="lg:col-span-2">' +
   909→            '<div class="glass-card overflow-hidden">' +
   910→              '<div class="relative min-h-[300px] sm:min-h-[400px] flex items-center justify-center bg-black/30">' +
   911→                imageContent +
   912→                imageDisplay +
   913→              '</div>' +
   914→              infoBar +
   915→            '</div>' +
   916→            analysisSection +
   917→          '</div>' +
   918→          '<div class="lg:col-span-1 space-y-4">' +
   919→            toolsPanel +
   920→            customPromptPanel +
   921→            errorSection +
   922→          '</div>' +
   923→        '</div>' +
   924→      '</main>';
   925→    },
   926→
   927→    // ===== GALLERY VIEW =====
   928→    renderGalleryView() {
   929→      const s = this.state;
   930→
   931→      if (s.isGalleryLoading) {
   932→        return '<main class="flex-1 flex items-center justify-center">' +
   933→          '<span class="animate-spin inline-block text-emerald-400">' + icon('Loader2', 32) + '</span>' +
   934→        '</main>';
   935→      }
   936→
   937→      let content = '';
   938→      if (s.projects.length === 0) {
   939→        content = '<div class="text-center py-20 fade-in">' +
   940→          '<div class="w-20 h-20 rounded-full bg-white/5 flex items-center justify-center mx-auto mb-4">' +
   941→            '<span class="text-white/30">' + icon('Image', 40) + '</span>' +
   942→          '</div>' +
   943→          '<p class="text-lg text-white/50 mb-4">لا توجد مشاريع بعد</p>' +
   944→          '<button data-action="nav" data-view="home" class="btn-primary gap-2">' + icon('Upload', 16) + ' ابدأ بتعديل صورة</button>' +
   945→        '</div>';
   946→      } else {
   947→        const cards = s.projects.map((project, i) => {
   948→          const lastEdit = project.edits && project.edits.length > 0 ? project.edits[project.edits.length - 1] : null;
   949→          const thumbnail = lastEdit ? (lastEdit.editedPath || lastEdit.originalPath || '') : '';
   950→          const dateStr = new Date(project.createdAt).toLocaleDateString('ar-EG');
   951→          const editCountStr = (project.edits && project.edits.length) ? (' — ' + project.edits.length + ' تعديل') : '';
   952→
   953→          let thumbContent = '';
   954→          if (thumbnail) {
   955→            thumbContent = '<img src="' + this.escHtml(thumbnail) + '" alt="' + this.escHtml(project.title) + '" class="w-full h-full object-cover" />';
   956→          } else {
   957→            thumbContent = '<div class="w-full h-full flex items-center justify-center"><span class="text-white/30">' + icon('Image', 32) + '</span></div>';
   958→          }
   959→
   960→          return '<div class="glass-card overflow-hidden group cursor-pointer hover:border-emerald-500/40 transition-all fade-in" style="animation-delay:' + (i * 0.05) + 's" data-action="open-project" data-project-index="' + i + '">' +
   961→            '<div class="aspect-video bg-black/30 relative overflow-hidden">' +
   962→              thumbContent +
   963→              '<div class="absolute inset-0 bg-black/0 group-hover:bg-black/30 transition-colors flex items-center justify-center">' +
   964→                '<span class="opacity-0 group-hover:opacity-100 transition-opacity bg-white/20 backdrop-blur-sm px-3 py-1.5 rounded-lg text-sm font-medium">فتح المشروع</span>' +
   965→              '</div>' +
   966→            '</div>' +
   967→            '<div class="p-3 flex items-center justify-between">' +
   968→              '<div>' +
   969→                '<p class="font-medium text-sm truncate">' + this.escHtml(project.title) + '</p>' +
   970→                '<p class="text-xs text-white/50">' + dateStr + editCountStr + '</p>' +
   971→              '</div>' +
   972→              '<button data-action="delete-project" data-project-index="' + i + '" class="btn-ghost h-8 w-8 text-white/50 hover:text-red-400 shrink-0 p-0">' + icon('Trash2', 16) + '</button>' +
   973→            '</div>' +
   974→          '</div>';
   975→        }).join('');
   976→
   977→        content = '<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">' + cards + '</div>';
   978→      }
   979→
   980→      return '<main class="flex-1 px-4 py-8 max-w-6xl mx-auto w-full">' +
   981→        '<div class="text-center mb-8 fade-in">' +
   982→          '<h2 class="text-3xl font-bold mb-2"><span class="gradient-text">المعرض</span></h2>' +
   983→          '<p class="text-white/50">جميع مشاريع تعديل الصور السابقة</p>' +
   984→        '</div>' +
   985→        content +
   986→      '</main>';
   987→    },
   988→
   989→    // ===== GENERATE VIEW =====
   990→    renderGenerateView() {
   991→      const s = this.state;
   992→      const hasImage = !!s.genImage;
   993→
   994→      const sizeBtns = SIZE_OPTIONS.map(opt => {
   995→        const active = s.genSelectedSize === opt.id;
   996→        return '<button data-action="gen-size" data-size-id="' + opt.id + '" class="flex flex-col items-center gap-1.5 p-2.5 rounded-xl border text-center transition-all ' +
   997→          (active ? 'border-emerald-500 bg-emerald-500/10 ring-1 ring-emerald-500/30' : 'border-white/10 hover:border-emerald-500/30') + ' ' +
   998→          (s.genIsGenerating ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer') + '" ' + (s.genIsGenerating ? 'disabled' : '') + '>' +
   999→          icon(opt.iconName, 16) +
  1000→          '<span class="text-xs font-medium">' + opt.label + '</span>' +
  1001→          '<span class="text-[10px] text-white/50">' + opt.desc + '</span>' +
  1002→        '</button>';
  1003→      }).join('');
  1004→
  1005→      // Result section
  1006→      let resultContent = '';
  1007→      if (s.genIsGenerating) {
  1008→        resultContent = '<div class="absolute inset-0 z-10 flex flex-col items-center justify-center bg-black/50 backdrop-blur-sm gap-4 px-8">' +
  1009→          '<span class="animate-spin inline-block text-emerald-400">' + icon('Loader2', 64) + '</span>' +
  1010→          '<p class="text-sm font-medium animate-pulse">' + this.escHtml(s.genProgressMsg || 'جارٍ رسم الصورة بالذكاء الاصطناعي...') + '</p>' +
  1011→          '<div class="progress-bar w-64"><div class="progress-bar-fill" style="width:' + s.genProgress + '%"></div></div>' +
  1012→          '<p class="text-xs text-white/50">' + s.genProgress + '%</p>' +
  1013→        '</div>';
  1014→      }
  1015→
  1016→      let imageArea = '';
  1017→      if (hasImage) {
  1018→        imageArea = '<img src="' + this.escHtml(s.genImage) + '" alt="Generated" class="w-full h-auto block" />';
  1019→      } else if (!s.genIsGenerating) {
  1020→        imageArea = '<div class="flex flex-col items-center gap-4 py-16">' +
  1021→          '<div class="w-20 h-20 rounded-full bg-white/5 flex items-center justify-center">' +
  1022→            '<span class="text-white/20">' + icon('WandSparkles', 40) + '</span>' +
  1023→          '</div>' +
  1024→          '<p class="text-white/50 text-center px-4">الصورة المرسومة ستظهر هنا</p>' +
  1025→        '</div>';
  1026→      }
  1027→
  1028→      let actionBtns = '';
  1029→      if (hasImage) {
  1030→        actionBtns = '<div class="p-3 border-t border-white/10 flex items-center gap-2">' +
  1031→          '<button data-action="send-to-editor" class="btn-primary flex-1 gap-2">' + icon('ArrowRight', 16) + ' تعديل الصورة</button>' +
  1032→          '<button data-action="download-generated" class="btn-outline gap-2">' + icon('Download', 16) + ' تحميل</button>' +
  1033→        '</div>';
  1034→      }
  1035→
  1036→      // Suggestion prompts
  1037→      let suggestions = '';
  1038→      if (!hasImage && !s.genIsGenerating) {
  1039→        const sugBtns = EXAMPLE_PROMPTS.map(ex => {
  1040→          return '<button data-action="gen-example" data-prompt="' + this.escHtml(ex) + '" class="text-xs px-3 py-1.5 rounded-full border border-white/10 hover:border-emerald-500/40 hover:bg-emerald-500/5 transition-all cursor-pointer">' + ex + '</button>';
  1041→        }).join('');
  1042→
  1043→        suggestions = '<div class="glass-card mt-4 p-4">' +
  1044→          '<h4 class="text-sm font-medium mb-3 flex items-center gap-2">' + icon('Sparkles', 16) + ' <span class="text-emerald-400">أفكار للتجربة</span></h4>' +
  1045→          '<div class="flex flex-wrap gap-2">' + sugBtns + '</div>' +
  1046→        '</div>';
  1047→      }
  1048→
  1049→      // Error
  1050→      let errorHtml = '';
  1051→      if (s.genError) {
  1052→        errorHtml = '<div class="border-red-500/50 bg-red-500/10 rounded-xl p-4 flex items-start gap-3 fade-in">' +
  1053→          '<span class="text-red-400 mt-0.5 shrink-0">' + icon('X', 20) + '</span>' +
  1054→          '<div class="flex-1">' +
  1055→            '<p class="text-sm font-medium text-red-400">خطأ</p>' +
  1056→            '<p class="text-xs text-white/50 mt-1">' + this.escHtml(s.genError) + '</p>' +
  1057→          '</div>' +
  1058→          '<button data-action="retry-generate" class="btn-outline shrink-0 gap-1 text-xs">' + icon('RotateCcw', 12) + ' إعادة المحاولة</button>' +
  1059→        '</div>';
  1060→      }
  1061→
  1062→      return '<main class="flex-1 px-4 py-6 max-w-5xl mx-auto w-full">' +
  1063→        '<div class="text-center mb-8 fade-in">' +
  1064→          '<h2 class="text-3xl font-bold mb-2"><span class="gradient-text">رسم الصور بالذكاء</span></h2>' +
  1065→          '<p class="text-white/50">صِف الصورة التي تريدها وسنرسمها لك</p>' +
  1066→        '</div>' +
  1067→        '<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">' +
  1068→          '<div class="space-y-4">' +
  1069→            '<div class="glass-card p-4">' +
  1070→              '<h3 class="font-semibold mb-3 flex items-center gap-2">' + icon('Pencil', 16) + ' <span class="text-emerald-400">وصف الصورة</span></h3>' +
  1071→              '<textarea id="gen-prompt-area" class="input-field min-h-[120px] resize-none mb-3" placeholder="مثال: قطة جميلة تجلس في حديقة مليئة بالزهور عند الغروب..." ' + (s.genIsGenerating ? 'disabled' : '') + '>' + this.escHtml(s.genPrompt) + '</textarea>' +
  1072→              '<p class="text-xs text-white/40 mb-3">يمكنك الكتابة بالعربية أو الإنجليزية</p>' +
  1073→              '<h4 class="text-sm font-medium mb-2">حجم الصورة</h4>' +
  1074→              '<div class="grid grid-cols-4 gap-2">' + sizeBtns + '</div>' +
  1075→              '<button data-action="generate-image" class="btn-primary w-full gap-2 mt-4 text-base py-3" ' + (s.genIsGenerating || !s.genPrompt.trim() ? 'disabled' : '') + '>' +
  1076→                (s.genIsGenerating ? '<span class="animate-spin inline-block">' + icon('Loader2', 20) + '</span> جارٍ الرسم...' : icon('WandSparkles', 20) + ' ارسم الصورة') +
  1077→              '</button>' +
  1078→            '</div>' +
  1079→            errorHtml +
  1080→          '</div>' +
  1081→          '<div>' +
  1082→            '<div class="glass-card overflow-hidden">' +
  1083→              '<div class="relative min-h-[400px] flex items-center justify-center bg-black/30">' +
  1084→                resultContent +
  1085→                imageArea +
  1086→              '</div>' +
  1087→              actionBtns +
  1088→            '</div>' +
  1089→            suggestions +
  1090→          '</div>' +
  1091→        '</div>' +
  1092→      '</main>';
  1093→    },
  1094→
  1095→    // ===== FOOTER =====
  1096→    renderFooter() {
  1097→      return '<footer class="glass mt-auto">' +
  1098→        '<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex flex-col sm:flex-row items-center justify-between gap-2 text-sm text-white/50">' +
  1099→          '<p>محرر الصور Fouad AI — قوّة الذكاء الاصطناعي بين يديك</p>' +
  1100→          '<p>© ' + new Date().getFullYear() + ' جميع الحقوق محفوظة</p>' +
  1101→        '</div>' +
  1102→      '</footer>';
  1103→    },
  1104→
  1105→    // ===== TOAST =====
  1106→    renderToast() {
  1107→      const t = this.state.toast;
  1108→      if (!t) return '';
  1109→      const bgColor = t.type === 'success' ? 'bg-emerald-500/90' : 'bg-red-500/90';
  1110→      return '<div class="fixed bottom-4 left-4 z-[200] ' + bgColor + ' text-white px-4 py-3 rounded-xl shadow-lg backdrop-blur-sm text-sm font-medium fade-in max-w-xs">' +
  1111→        this.escHtml(t.message) +
  1112→      '</div>';
  1113→    },
  1114→
  1115→    // ===== COMPARISON SLIDER =====
  1116→    initComparisonSlider() {
  1117→      const container = document.getElementById('comparison-container');
  1118→      if (!container) return;
  1119→      this._compDragging = false;
  1120→      this._compContainer = container;
  1121→
  1122→      const updateWidth = () => {
  1123→        const img = document.getElementById('comparison-original-img');
  1124→        if (img && container) {
  1125→          img.style.width = container.offsetWidth + 'px';
  1126→        }
  1127→      };
  1128→      updateWidth();
  1129→
  1130→      const ro = new ResizeObserver(updateWidth);
  1131→      ro.observe(container);
  1132→      this._compRO = ro;
  1133→    },
  1134→
  1135→    _updateComparisonPosition(clientX) {
  1136→      const container = this._compContainer;
  1137→      if (!container) return;
  1138→      const rect = container.getBoundingClientRect();
  1139→      const x = clientX - rect.left;
  1140→      const pct = Math.min(100, Math.max(0, (x / rect.width) * 100));
  1141→      const clip = document.getElementById('comparison-original-clip');
  1142→      const line = document.getElementById('comparison-line');
  1143→      if (clip) clip.style.width = pct + '%';
  1144→      if (line) line.style.left = pct + '%';
  1145→    },
  1146→
  1147→    // ===== EVENT HANDLERS =====
  1148→    handleClick(e) {
  1149→      const btn = e.target.closest('[data-action]');
  1150→      if (!btn) return;
  1151→      const action = btn.dataset.action;
  1152→
  1153→      switch (action) {
  1154→        case 'nav':
  1155→          this.setState({ view: btn.dataset.view });
  1156→          if (btn.dataset.view === 'gallery') this.loadGallery();
  1157→          this.render();
  1158→          break;
  1159→
  1160→        case 'open-login':
  1161→          this.setState({ showLoginDialog: true, loginError: null });
  1162→          this.render();
  1163→          break;
  1164→
  1165→        case 'close-login':
  1166→        case 'close-login-backdrop':
  1167→          if (action === 'close-login-backdrop' && e.target !== btn) return;
  1168→          this.setState({ showLoginDialog: false, loginError: null, loginUsername: '', loginPassword: '' });
  1169→          this.render();
  1170→          break;
  1171→
  1172→        case 'close-banner':
  1173→        case 'close-banner-backdrop':
  1174→          if (action === 'close-banner-backdrop' && e.target !== btn) return;
  1175→          this.setState({ showBannerDialog: false });
  1176→          this.render();
  1177→          break;
  1178→
  1179→        case 'open-banner-admin':
  1180→          if (this.state.isAdmin) {
  1181→            this.loadBannerAdmin();
  1182→            this.setState({ showBannerDialog: true });
  1183→          } else {
  1184→            this.setState({ showLoginDialog: true, loginError: null });
  1185→          }
  1186→          this.render();
  1187→          break;
  1188→
  1189→        case 'dismiss-banner':
  1190→          e.stopPropagation();
  1191→          const pos = btn.dataset.position;
  1192→          const dismissed = Object.assign({}, this.state.bannerDismissed);
  1193→          dismissed[pos] = true;
  1194→          this.setState({ bannerDismissed: dismissed });
  1195→          this.render();
  1196→          break;
  1197→
  1198→        case 'upload-click':
  1199→          document.getElementById('upload-file-input').click();
  1200→          break;
  1201→
  1202→        case 'new-image':
  1203→          this.setState({ originalImage: null, originalFileName: null, editedImage: null, isEditing: false, editProgress: 0, editError: null, analysis: null, customPrompt: '', selectedTool: null, editCount: 0, lastEditArgs: null });
  1204→          this.render();
  1205→          break;
  1206→
  1207→        case 'reset-original':
  1208→          this.setState({ editedImage: null, editCount: 0, editError: null });
  1209→          this.render();
  1210→          break;
  1211→
  1212→        case 'download-edited':
  1213→          this.downloadImage(this.state.editedImage, this.state.originalFileName);
  1214→          break;
  1215→
  1216→        case 'download-generated':
  1217→          this.downloadImage(this.state.genImage, 'Fouad-AI-generated.png');
  1218→          break;
  1219→
  1220→        case 'reupload':
  1221→          document.getElementById('reupload-file-input').click();
  1222→          break;
  1223→
  1224→        case 'analyze-image':
  1225→          this.analyzeImage();
  1226→          break;
  1227→
  1228→        case 'use-tool':
  1229→          if (this.state.isEditing) return;
  1230→          const toolId = btn.dataset.toolId;
  1231→          const tool = EDIT_TOOLS.find(t => t.id === toolId);
  1232→          if (tool) {
  1233→            this.setState({ selectedTool: toolId });
  1234→            this.startEdit(tool.prompt, tool.label, 'quick-tool');
  1235→          }
  1236→          break;
  1237→
  1238→        case 'custom-edit':
  1239→          if (this.state.isEditing || !this.state.customPrompt.trim()) return;
  1240→          this.startEdit(this.state.customPrompt.trim(), 'تعديل مخصص', 'custom');
  1241→          break;
  1242→
  1243→        case 'retry-edit':
  1244→          if (this.state.lastEditArgs) {
  1245→            this.setState({ editError: null });
  1246→            this.startEdit(this.state.lastEditArgs.prompt, this.state.lastEditArgs.label, this.state.lastEditArgs.toolType);
  1247→          }
  1248→          break;
  1249→
  1250→        case 'send-to-editor':
  1251→          if (!this.state.genImage) return;
  1252→          this.setState({
  1253→            originalImage: this.state.genImage,
  1254→            originalFileName: 'Fouad-AI-generated.png',
  1255→            editedImage: null,
  1256→            analysis: null,
  1257→            view: 'editor',
  1258→            editCount: 0,
  1259→            editError: null,
  1260→            customPrompt: '',
  1261→            selectedTool: null,
  1262→            lastEditArgs: null,
  1263→          });
  1264→          this.render();
  1265→          break;
  1266→
  1267→        case 'generate-image':
  1268→          this.handleGenerate();
  1269→          break;
  1270→
  1271→        case 'gen-size':
  1272→          this.setState({ genSelectedSize: btn.dataset.sizeId });
  1273→          this.render();
  1274→          break;
  1275→
  1276→        case 'gen-example':
  1277→          this.setState({ genPrompt: btn.dataset.prompt });
  1278→          this.render();
  1279→          const ta = document.getElementById('gen-prompt-area');
  1280→          if (ta) ta.focus();
  1281→          break;
  1282→
  1283→        case 'retry-generate':
  1284→          this.handleGenerate();
  1285→          break;
  1286→
  1287→        case 'banner-type':
  1288→          this.setState({ bannerAdType: btn.dataset.type });
  1289→          this.render();
  1290→          break;
  1291→
  1292→        case 'banner-position':
  1293→          this.setState({ bannerPosition: btn.dataset.value });
  1294→          this.render();
  1295→          break;
  1296→
  1297→        case 'toggle-banner-active':
  1298→          this.setState({ bannerActive: !this.state.bannerActive });
  1299→          this.render();
  1300→          break;
  1301→
  1302→        case 'banner-upload-click':
  1303→          if (!this.state.bannerUploading) {
  1304→            document.getElementById('banner-file-input').click();
  1305→          }
  1306→          break;
  1307→
  1308→        case 'save-banner':
  1309→          this.saveBanner();
  1310→          break;
  1311→
  1312→        case 'open-project':
  1313→          const idx = parseInt(btn.dataset.projectIndex);
  1314→          this.openProject(idx);
  1315→          break;
  1316→
  1317→        case 'delete-project':
  1318→          e.stopPropagation();
  1319→          const delIdx = parseInt(btn.dataset.projectIndex);
  1320→          this.deleteProject(delIdx);
  1321→          break;
  1322→
  1323→        case 'logout':
  1324→          this.handleLogout();
  1325→          break;
  1326→      }
  1327→    },
  1328→
  1329→    handleInput(e) {
  1330→      const el = e.target;
  1331→      if (el.id === 'custom-prompt-area') {
  1332→        this.state.customPrompt = el.value;
  1333→      } else if (el.id === 'gen-prompt-area') {
  1334→        this.state.genPrompt = el.value;
  1335→      } else if (el.id === 'login-username') {
  1336→        this.state.loginUsername = el.value;
  1337→      } else if (el.id === 'login-password') {
  1338→        this.state.loginPassword = el.value;
  1339→      } else if (el.id === 'banner-link-url') {
  1340→        this.state.bannerLinkUrl = el.value;
  1341→      } else if (el.id === 'banner-text-overlay') {
  1342→        this.state.bannerText = el.value;
  1343→      } else if (el.id === 'banner-ad-code') {
  1344→        this.state.bannerAdCode = el.value;
  1345→      }
  1346→    },
  1347→
  1348→    async handleSubmit(e) {
  1349→      if (e.target.id === 'login-form') {
  1350→        e.preventDefault();
  1351→        await this.handleLogin();
  1352→      }
  1353→    },
  1354→
  1355→    handleDragOver(e) {
  1356→      const uploadArea = e.target.closest('[data-action="upload-click"]');
  1357→      if (uploadArea) {
  1358→        e.preventDefault();
  1359→        if (!this.state.isDragOver) {
  1360→          this.setState({ isDragOver: true });
  1361→          this.render();
  1362→        }
  1363→      }
  1364→    },
  1365→
  1366→    handleDragLeave(e) {
  1367→      const uploadArea = e.target.closest('[data-action="upload-click"]');
  1368→      if (uploadArea) {
  1369→        const rect = uploadArea.getBoundingClientRect();
  1370→        const x = e.clientX;
  1371→        const y = e.clientY;
  1372→        if (x < rect.left || x > rect.right || y < rect.top || y > rect.bottom) {
  1373→          this.setState({ isDragOver: false });
  1374→          this.render();
  1375→        }
  1376→      }
  1377→    },
  1378→
  1379→    handleDrop(e) {
  1380→      const uploadArea = e.target.closest('[data-action="upload-click"]');
  1381→      if (uploadArea) {
  1382→        e.preventDefault();
  1383→        this.setState({ isDragOver: false });
  1384→        const file = e.dataTransfer.files[0];
  1385→        if (file && file.type.startsWith('image/')) {
  1386→          this.uploadFile(file);
  1387→        }
  1388→      }
  1389→    },
  1390→
  1391→    async handleChange(e) {
  1392→      const el = e.target;
  1393→      if (el.id === 'upload-file-input') {
  1394→        const file = el.files[0];
  1395→        if (file && file.type.startsWith('image/')) {
  1396→          await this.uploadFile(file);
  1397→        }
  1398→        el.value = '';
  1399→      } else if (el.id === 'reupload-file-input') {
  1400→        const file = el.files[0];
  1401→        if (file && file.type.startsWith('image/')) {
  1402→          await this.uploadFile(file, true);
  1403→        }
  1404→        el.value = '';
  1405→      } else if (el.id === 'banner-file-input') {
  1406→        const file = el.files[0];
  1407→        if (file && file.type.startsWith('image/')) {
  1408→          await this.uploadBannerImage(file);
  1409→        }
  1410→        el.value = '';
  1411→      }
  1412→    },
  1413→
  1414→    handlePointerDown(e) {
  1415→      const container = e.target.closest('#comparison-container') || e.target.closest('.comparison-slider-handle');
  1416→      if (container) {
  1417→        this._compDragging = true;
  1418→        this._updateComparisonPosition(e.clientX);
  1419→        e.preventDefault();
  1420→      }
  1421→    },
  1422→
  1423→    handlePointerMove(e) {
  1424→      if (this._compDragging) {
  1425→        this._updateComparisonPosition(e.clientX);
  1426→        e.preventDefault();
  1427→      }
  1428→    },
  1429→
  1430→    handlePointerUp(e) {
  1431→      if (this._compDragging) {
  1432→        this._compDragging = false;
  1433→      }
  1434→    },
  1435→
  1436→    // ===== ACTIONS =====
  1437→    async uploadFile(file, isReupload) {
  1438→      this.setState({ isUploading: true, editError: null });
  1439→      if (!isReupload) {
  1440→        this.setState({ editedImage: null, analysis: null });
  1441→      }
  1442→      this.render();
  1443→
  1444→      try {
  1445→        const formData = new FormData();
  1446→        formData.append('image', file);
  1447→        const res = await fetch('/api/upload', { method: 'POST', body: formData });
  1448→        const data = await res.json();
  1449→        if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة');
  1450→
  1451→        this.setState({
  1452→          originalImage: data.imageUrl,
  1453→          originalFileName: file.name,
  1454→          isUploading: false,
  1455→          view: 'editor',
  1456→          editCount: 0,
  1457→          editError: null,
  1458→          analysis: null,
  1459→          customPrompt: '',
  1460→          selectedTool: null,
  1461→          lastEditArgs: null,
  1462→        });
  1463→        if (isReupload) {
  1464→          this.setState({ editedImage: null, analysis: null });
  1465→        }
  1466→        this.render();
  1467→      } catch (err) {
  1468→        this.setState({
  1469→          isUploading: false,
  1470→          editError: err.message || 'حدث خطأ',
  1471→        });
  1472→        this.render();
  1473→      }
  1474→    },
  1475→
  1476→    async startEdit(prompt, label, toolType) {
  1477→      const baseImage = this.state.editedImage || this.state.originalImage;
  1478→      if (!baseImage) return;
  1479→
  1480→      this.setState({
  1481→        lastEditArgs: { prompt, label, toolType },
  1482→        isEditing: true,
  1483→        editError: null,
  1484→        editedImage: null,
  1485→        editProgress: 0,
  1486→      });
  1487→      this.render();
  1488→
  1489→      try {
  1490→        const res = await fetch('/api/edit', {
  1491→          method: 'POST',
  1492→          headers: { 'Content-Type': 'application/json' },
  1493→          body: JSON.stringify({ imageUrl: baseImage, prompt, label, toolType }),
  1494→        });
  1495→
  1496→        if (!res.ok) {
  1497→          const data = await res.json();
  1498→          throw new Error(data.error || 'فشل التعديل');
  1499→        }
  1500→
  1501→        const reader = res.body ? res.body.getReader() : null;
  1502→        if (!reader) throw new Error('لا يمكن قراءة الاستجابة');
  1503→
  1504→        const decoder = new TextDecoder();
  1505→        let buffer = '';
  1506→
  1507→        while (true) {
  1508→          const { done, value } = await reader.read();
  1509→          if (done) break;
  1510→
  1511→          buffer += decoder.decode(value, { stream: true });
  1512→          const lines = buffer.split('\n');
  1513→          buffer = lines.pop() || '';
  1514→
  1515→          for (const line of lines) {
  1516→            if (line.startsWith('data: ')) {
  1517→              const data = line.slice(6);
  1518→              let parsed;
  1519→              try { parsed = JSON.parse(data); } catch { continue; }
  1520→
  1521→              if (parsed.type === 'progress') {
  1522→                this.setState({ editProgress: parsed.value });
  1523→                this.render();
  1524→              } else if (parsed.type === 'done') {
  1525→                this.setState({
  1526→                  editedImage: parsed.editedImageUrl,
  1527→                  editProgress: 100,
  1528→                  editCount: this.state.editCount + 1,
  1529→                });
  1530→                this.render();
  1531→                // Refresh gallery
  1532→                try {
  1533→                  const projectsRes = await fetch('/api/projects');
  1534→                  if (projectsRes.ok) {
  1535→                    const projectsData = await projectsRes.json();
  1536→                    this.setState({ projects: projectsData });
  1537→                  }
  1538→                } catch {}
  1539→              } else if (parsed.type === 'error') {
  1540→                throw new Error(parsed.message);
  1541→              }
  1542→            }
  1543→          }
  1544→        }
  1545→      } catch (err) {
  1546→        this.setState({ editError: err.message || 'حدث خطأ أثناء التعديل' });
  1547→        this.render();
  1548→      } finally {
  1549→        this.setState({ isEditing: false, selectedTool: null });
  1550→        this.render();
  1551→      }
  1552→    },
  1553→
  1554→    async analyzeImage() {
  1555→      if (!this.state.originalImage) return;
  1556→      this.setState({ isAnalyzing: true, editError: null });
  1557→      this.render();
  1558→
  1559→      try {
  1560→        const res = await fetch('/api/analyze', {
  1561→          method: 'POST',
  1562→          headers: { 'Content-Type': 'application/json' },
  1563→          body: JSON.stringify({ imageUrl: this.state.originalImage }),
  1564→        });
  1565→        const data = await res.json();
  1566→        if (!res.ok) throw new Error(data.error || 'فشل التحليل');
  1567→        this.setState({ analysis: data.analysis });
  1568→        this.render();
  1569→      } catch (err) {
  1570→        this.setState({ editError: err.message || 'حدث خطأ' });
  1571→        this.render();
  1572→      } finally {
  1573→        this.setState({ isAnalyzing: false });
  1574→        this.render();
  1575→      }
  1576→    },
  1577→
  1578→    async handleGenerate() {
  1579→      if (!this.state.genPrompt.trim() || this.state.genIsGenerating) return;
  1580→
  1581→      this.setState({
  1582→        genIsGenerating: true,
  1583→        genError: null,
  1584→        genImage: null,
  1585→        genProgress: 0,
  1586→        genProgressMsg: 'جارٍ البدء...',
  1587→      });
  1588→      this.render();
  1589→
  1590→      try {
  1591→        const startRes = await fetch('/api/generate', {
  1592→          method: 'POST',
  1593→          headers: { 'Content-Type': 'application/json' },
  1594→          body: JSON.stringify({ prompt: this.state.genPrompt.trim(), sizeId: this.state.genSelectedSize }),
  1595→        });
  1596→        const startData = await startRes.json();
  1597→
  1598→        if (!startRes.ok || startData.error) {
  1599→          throw new Error(startData.error || 'حدث خطأ غير متوقع');
  1600→        }
  1601→
  1602→        const { jobId } = startData;
  1603→        if (!jobId) throw new Error('لم يتم إنشاء العملية');
  1604→
  1605→        const result = await new Promise((resolve, reject) => {
  1606→          let attempts = 0;
  1607→          const maxAttempts = 120;
  1608→
  1609→          const pollInterval = setInterval(async () => {
  1610→            attempts++;
  1611→            if (attempts > maxAttempts) {
  1612→              clearInterval(pollInterval);
  1613→              reject(new Error('انتهت المهلة. حاول مرة أخرى.'));
  1614→              return;
  1615→            }
  1616→
  1617→            try {
  1618→              const pollRes = await fetch('/api/generate?jobId=' + jobId);
  1619→              const pollData = await pollRes.json();
  1620→
  1621→              if (pollData.error && !pollData.status) {
  1622→                clearInterval(pollInterval);
  1623→                reject(new Error(pollData.error));
  1624→                return;
  1625→              }
  1626→
  1627→              if (pollData.progress !== undefined) {
  1628→                this.setState({ genProgress: Math.max(this.state.genProgress, pollData.progress) });
  1629→              }
  1630→              if (pollData.message) {
  1631→                this.setState({ genProgressMsg: pollData.message });
  1632→              }
  1633→
  1634→              if (pollData.status === 'done' && pollData.imageUrl) {
  1635→                clearInterval(pollInterval);
  1636→                resolve({ imageUrl: pollData.imageUrl });
  1637→              } else if (pollData.status === 'error') {
  1638→                clearInterval(pollInterval);
  1639→                reject(new Error(pollData.error || 'فشل توليد الصورة'));
  1640→              }
  1641→            } catch (pollErr) {
  1642→              console.error('[GEN] Poll error, retrying...', pollErr);
  1643→            }
  1644→          }, 3000);
  1645→
  1646→          this.state.genPollInterval = pollInterval;
  1647→        });
  1648→
  1649→        this.setState({ genImage: result.imageUrl });
  1650→        this.render();
  1651→      } catch (err) {
  1652→        this.setState({ genError: err.message || 'حدث خطأ' });
  1653→        this.render();
  1654→      } finally {
  1655→        if (this.state.genPollInterval) {
  1656→          clearInterval(this.state.genPollInterval);
  1657→          this.state.genPollInterval = null;
  1658→        }
  1659→        this.setState({ genIsGenerating: false });
  1660→        this.render();
  1661→      }
  1662→    },
  1663→
  1664→    async handleLogin() {
  1665→      const s = this.state;
  1666→      if (!s.loginUsername.trim() || !s.loginPassword.trim() || s.loginLoading) return;
  1667→
  1668→      this.setState({ loginLoading: true, loginError: null });
  1669→      this.render();
  1670→
  1671→      try {
  1672→        const res = await fetch('/api/auth', {
  1673→          method: 'POST',
  1674→          headers: { 'Content-Type': 'application/json' },
  1675→          body: JSON.stringify({ username: s.loginUsername, password: s.loginPassword }),
  1676→        });
  1677→        const data = await res.json();
  1678→
  1679→        if (!res.ok) {
  1680→          this.setState({ loginError: data.error || 'اسم المستخدم أو كلمة المرور غير صحيحة', loginLoading: false });
  1681→          this.render();
  1682→          return;
  1683→        }
  1684→
  1685→        this.setState({
  1686→          isAdmin: true,
  1687→          loginLoading: false,
  1688→          loginUsername: '',
  1689→          loginPassword: '',
  1690→          showLoginDialog: false,
  1691→        });
  1692→        this.showToast('تم تسجيل الدخول بنجاح', 'success');
  1693→        this.loadBannerAdmin();
  1694→        this.setState({ showBannerDialog: true });
  1695→        this.render();
  1696→      } catch {
  1697→        this.setState({ loginError: 'حدث خطأ أثناء تسجيل الدخول', loginLoading: false });
  1698→        this.render();
  1699→      }
  1700→    },
  1701→
  1702→    async handleLogout() {
  1703→      try {
  1704→        await fetch('/api/auth/session', { method: 'DELETE' });
  1705→      } catch {}
  1706→      this.setState({ isAdmin: false });
  1707→      this.showToast('تم تسجيل الخروج', 'success');
  1708→      this.render();
  1709→    },
  1710→
  1711→    async loadBannerAdmin() {
  1712→      try {
  1713→        const res = await fetch('/api/banner');
  1714→        if (!res.ok) return;
  1715→        const data = await res.json();
  1716→        if (data.banner) {
  1717→          this.setState({
  1718→            bannerAdType: data.banner.adType || 'image',
  1719→            bannerImageUrl: data.banner.imageUrl || '',
  1720→            bannerAdCode: data.banner.adCode || '',
  1721→            bannerLinkUrl: data.banner.linkUrl || '',
  1722→            bannerText: data.banner.text || '',
  1723→            bannerActive: data.banner.active,
  1724→            bannerPosition: data.banner.position || 'top',
  1725→            bannerMessage: null,
  1726→          });
  1727→        } else {
  1728→          this.setState({
  1729→            bannerAdType: 'image',
  1730→            bannerImageUrl: '',
  1731→            bannerAdCode: '',
  1732→            bannerLinkUrl: '',
  1733→            bannerText: '',
  1734→            bannerActive: false,
  1735→            bannerPosition: 'top',
  1736→            bannerMessage: null,
  1737→          });
  1738→        }
  1739→      } catch {}
  1740→    },
  1741→
  1742→    async uploadBannerImage(file) {
  1743→      this.setState({ bannerUploading: true, bannerMessage: null });
  1744→      this.render();
  1745→
  1746→      try {
  1747→        const formData = new FormData();
  1748→        formData.append('image', file);
  1749→        const res = await fetch('/api/banner', { method: 'POST', body: formData });
  1750→        const data = await res.json();
  1751→        if (!res.ok) throw new Error(data.error || 'فشل رفع الصورة');
  1752→        this.setState({ bannerImageUrl: data.imageUrl });
  1753→        this.render();
  1754→      } catch (err) {
  1755→        this.setState({ bannerMessage: { type: 'error', text: err.message || 'خطأ' } });
  1756→        this.render();
  1757→      } finally {
  1758→        this.setState({ bannerUploading: false });
  1759→        this.render();
  1760→      }
  1761→    },
  1762→
  1763→    async saveBanner() {
  1764→      const s = this.state;
  1765→      if (s.bannerAdType === 'image' && !s.bannerImageUrl) {
  1766→        this.setState({ bannerMessage: { type: 'error', text: 'يرجى رفع صورة البانر أولاً' } });
  1767→        this.render();
  1768→        return;
  1769→      }
  1770→      if (s.bannerAdType === 'external' && !s.bannerAdCode.trim()) {
  1771→        this.setState({ bannerMessage: { type: 'error', text: 'يرجى لصق كود الإعلان الخارجي' } });
  1772→        this.render();
  1773→        return;
  1774→      }
  1775→
  1776→      this.setState({ bannerSaving: true, bannerMessage: null });
  1777→      this.render();
  1778→
  1779→      try {
  1780→        const res = await fetch('/api/banner', {
  1781→          method: 'PUT',
  1782→          headers: { 'Content-Type': 'application/json' },
  1783→          body: JSON.stringify({
  1784→            adType: s.bannerAdType,
  1785→            imageUrl: s.bannerImageUrl,
  1786→            adCode: s.bannerAdCode,
  1787→            linkUrl: s.bannerLinkUrl,
  1788→            text: s.bannerText,
  1789→            active: s.bannerActive,
  1790→            position: s.bannerPosition,
  1791→          }),
  1792→        });
  1793→        const data = await res.json();
  1794→        if (!res.ok) throw new Error(data.error || 'فشل الحفظ');
  1795→
  1796→        this.setState({ bannerMessage: { type: 'success', text: 'تم حفظ البانر بنجاح' } });
  1797→        // Refresh banner displays
  1798→        this.fetchBanner(s.bannerPosition);
  1799→        this.render();
  1800→      } catch (err) {
  1801→        this.setState({ bannerMessage: { type: 'error', text: err.message || 'خطأ' } });
  1802→        this.render();
  1803→      } finally {
  1804→        this.setState({ bannerSaving: false });
  1805→        this.render();
  1806→      }
  1807→    },
  1808→
  1809→    async loadGallery() {
  1810→      this.setState({ isGalleryLoading: true });
  1811→      this.render();
  1812→
  1813→      try {
  1814→        const res = await fetch('/api/projects');
  1815→        if (res.ok) {
  1816→          const data = await res.json();
  1817→          this.setState({ projects: data });
  1818→        }
  1819→      } catch {}
  1820→      this.setState({ isGalleryLoading: false });
  1821→      this.render();
  1822→    },
  1823→
  1824→    async deleteProject(index) {
  1825→      const project = this.state.projects[index];
  1826→      if (!project) return;
  1827→
  1828→      try {
  1829→        const res = await fetch('/api/projects?id=' + project.id, { method: 'DELETE' });
  1830→        if (res.ok) {
  1831→          const projects = this.state.projects.filter(p => p.id !== project.id);
  1832→          this.setState({ projects });
  1833→          this.render();
  1834→          this.showToast('تم حذف المشروع', 'success');
  1835→        }
  1836→      } catch {}
  1837→    },
  1838→
  1839→    openProject(index) {
  1840→      const project = this.state.projects[index];
  1841→      if (!project) return;
  1842→
  1843→      const lastEdit = project.edits && project.edits.length > 0 ? project.edits[project.edits.length - 1] : null;
  1844→
  1845→      let editedImage = null;
  1846→      let originalImage = null;
  1847→
  1848→      if (lastEdit) {
  1849→        originalImage = lastEdit.originalPath;
  1850→        editedImage = lastEdit.editedPath || null;
  1851→      }
  1852→
  1853→      if (!originalImage) return;
  1854→
  1855→      this.setState({
  1856→        originalImage,
  1857→        editedImage,
  1858→        originalFileName: project.title,
  1859→        analysis: null,
  1860→        view: 'editor',
  1861→        editCount: project.edits ? project.edits.length : 0,
  1862→        editError: null,
  1863→        customPrompt: '',
  1864→        selectedTool: null,
  1865→        lastEditArgs: null,
  1866→      });
  1867→      this.render();
  1868→    },
  1869→
  1870→    downloadImage(url, fileName) {
  1871→      if (!url) return;
  1872→      const a = document.createElement('a');
  1873→      a.href = url;
  1874→      let ext = 'png';
  1875→      let baseName = 'image';
  1876→      if (fileName) {
  1877→        const parts = fileName.split('.');
  1878→        if (parts.length > 1) ext = parts.pop();
  1879→        baseName = parts.join('.');
  1880→      }
  1881→      a.download = 'Fouad-AI-' + baseName + '.' + ext;
  1882→      a.click();
  1883→    },
  1884→
  1885→    escHtml(str) {
  1886→      if (!str) return '';
  1887→      const div = document.createElement('div');
  1888→      div.textContent = str;
  1889→      return div.innerHTML;
  1890→    },
  1891→  };
  1892→
  1893→  // ===== INIT =====
  1894→  document.addEventListener('DOMContentLoaded', () => {
  1895→    App.init();
  1896→  });
  1897→  </script>
  1898→</body>
  1899→</html>