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