CORVO-AI commited on
Commit
f799773
·
verified ·
1 Parent(s): f9ae9c1

Upload templates/index.html

Browse files
Files changed (1) hide show
  1. templates/index.html +2145 -0
templates/index.html ADDED
@@ -0,0 +1,2145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>SHKIKEL GROUP - OBD-II</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
9
+ <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.js"></script>
10
+ <style>
11
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
12
+
13
+ :root {
14
+ --bg: #ffffff;
15
+ --surface: #f8f8f8;
16
+ --surface2: #f0f0f0;
17
+ --surface3: #e8e8e8;
18
+ --border: rgba(0,0,0,0.08);
19
+ --border2: rgba(0,0,0,0.14);
20
+ --accent: #dc2626;
21
+ --accent2: #b91c1c;
22
+ --accent-dim: rgba(220,38,38,0.08);
23
+ --accent-glow: rgba(220,38,38,0.22);
24
+ --text: #0a0a0a;
25
+ --text2: #444444;
26
+ --muted: #999999;
27
+ --green: #16a34a;
28
+ --red: #dc2626;
29
+ --dark: #111111;
30
+ --dark2: #1a1a1a;
31
+ --radius: 14px;
32
+ }
33
+
34
+ html, body {
35
+ height: 100%;
36
+ height: 100dvh;
37
+ background: var(--bg);
38
+ color: var(--text);
39
+ font-family: 'Cairo', sans-serif;
40
+ overflow: hidden;
41
+ }
42
+
43
+ /* ══════════════════════════════
44
+ LAYOUT
45
+ ══════════════════════════════ */
46
+ .app {
47
+ display: flex;
48
+ height: 100vh;
49
+ height: 100dvh;
50
+ position: relative;
51
+ }
52
+
53
+ /* ══════════════════════════════
54
+ SIDEBAR
55
+ ══════════════════════════════ */
56
+ .sidebar {
57
+ width: 270px;
58
+ background: var(--dark);
59
+ border-left: 1px solid rgba(220,38,38,0.18);
60
+ display: flex;
61
+ flex-direction: column;
62
+ flex-shrink: 0;
63
+ overflow: hidden;
64
+ position: relative;
65
+ }
66
+
67
+ .sidebar::before {
68
+ content: '';
69
+ position: absolute;
70
+ top: 0;
71
+ left: 0;
72
+ right: 0;
73
+ height: 3px;
74
+ background: linear-gradient(90deg, var(--accent), #ff6b6b, var(--accent));
75
+ background-size: 200% 100%;
76
+ animation: shimmer 3s linear infinite;
77
+ }
78
+
79
+ @keyframes shimmer {
80
+ 0% { background-position: 200% 0; }
81
+ 100% { background-position: -200% 0; }
82
+ }
83
+
84
+ /* Logo area */
85
+ .sidebar-logo {
86
+ padding: 32px 20px 22px;
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ gap: 10px;
91
+ border-bottom: 1px solid rgba(255,255,255,0.06);
92
+ background: linear-gradient(180deg, rgba(220,38,38,0.06) 0%, transparent 100%);
93
+ }
94
+
95
+ .logo-ring {
96
+ position: relative;
97
+ width: 88px;
98
+ height: 88px;
99
+ display: flex;
100
+ align-items: center;
101
+ justify-content: center;
102
+ }
103
+
104
+ .logo-ring::before {
105
+ content: '';
106
+ position: absolute;
107
+ inset: -3px;
108
+ border-radius: 50%;
109
+ background: conic-gradient(var(--accent), transparent, var(--accent));
110
+ animation: spin 4s linear infinite;
111
+ }
112
+
113
+ .logo-ring::after {
114
+ content: '';
115
+ position: absolute;
116
+ inset: -1px;
117
+ border-radius: 50%;
118
+ background: var(--dark);
119
+ }
120
+
121
+ @keyframes spin { to { transform: rotate(360deg); } }
122
+
123
+ .logo-img {
124
+ width: 72px;
125
+ height: 72px;
126
+ object-fit: contain;
127
+ filter: drop-shadow(0 0 18px rgba(220,38,38,0.5));
128
+ position: relative;
129
+ z-index: 1;
130
+ }
131
+
132
+ .logo-name {
133
+ font-size: 13px;
134
+ font-weight: 800;
135
+ letter-spacing: 2.5px;
136
+ color: #ffffff;
137
+ text-align: center;
138
+ }
139
+
140
+ .logo-sub {
141
+ font-size: 8.5px;
142
+ color: var(--accent);
143
+ font-family: 'JetBrains Mono', monospace;
144
+ letter-spacing: 2px;
145
+ text-align: center;
146
+ margin-top: -5px;
147
+ }
148
+
149
+ .status-pill {
150
+ display: inline-flex;
151
+ align-items: center;
152
+ gap: 6px;
153
+ background: rgba(22,163,74,0.1);
154
+ border: 1px solid rgba(22,163,74,0.25);
155
+ border-radius: 20px;
156
+ padding: 4px 12px;
157
+ font-size: 10px;
158
+ color: #4ade80;
159
+ font-family: 'JetBrains Mono', monospace;
160
+ letter-spacing: 1px;
161
+ margin-top: 2px;
162
+ }
163
+
164
+ .s-dot {
165
+ width: 6px;
166
+ height: 6px;
167
+ border-radius: 50%;
168
+ background: #4ade80;
169
+ animation: blink 2s infinite;
170
+ box-shadow: 0 0 6px #4ade80;
171
+ }
172
+
173
+ @keyframes blink {
174
+ 0%,100% { opacity:1; }
175
+ 50% { opacity:0.3; }
176
+ }
177
+
178
+ /* Sidebar buttons */
179
+ .sidebar-btns {
180
+ padding: 14px 16px;
181
+ display: flex;
182
+ flex-direction: column;
183
+ gap: 7px;
184
+ border-bottom: 1px solid rgba(255,255,255,0.05);
185
+ }
186
+
187
+ .sb-btn {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 9px;
191
+ padding: 10px 14px;
192
+ border-radius: 10px;
193
+ border: 1px solid rgba(255,255,255,0.07);
194
+ background: rgba(255,255,255,0.03);
195
+ color: rgba(255,255,255,0.65);
196
+ font-family: 'Cairo', sans-serif;
197
+ font-size: 13px;
198
+ font-weight: 600;
199
+ cursor: pointer;
200
+ transition: all 0.2s;
201
+ width: 100%;
202
+ text-align: right;
203
+ }
204
+
205
+ .sb-btn svg { width: 14px; height: 14px; flex-shrink: 0; }
206
+
207
+ .sb-btn:hover {
208
+ background: rgba(220,38,38,0.12);
209
+ color: #ffffff;
210
+ border-color: rgba(220,38,38,0.3);
211
+ transform: translateX(-2px);
212
+ }
213
+
214
+ .sb-btn.red:hover {
215
+ background: rgba(220,38,38,0.15);
216
+ color: #ff6b6b;
217
+ border-color: rgba(220,38,38,0.35);
218
+ }
219
+
220
+ /* Quick prompts */
221
+ .sidebar-quick {
222
+ flex: 1;
223
+ overflow-y: auto;
224
+ padding: 16px 16px;
225
+ }
226
+
227
+ .sidebar-quick::-webkit-scrollbar { width: 3px; }
228
+ .sidebar-quick::-webkit-scrollbar-thumb { background: rgba(220,38,38,0.3); border-radius: 2px; }
229
+
230
+ .ql {
231
+ font-size: 9px;
232
+ color: rgba(255,255,255,0.25);
233
+ font-family: 'JetBrains Mono', monospace;
234
+ letter-spacing: 2.5px;
235
+ text-transform: uppercase;
236
+ margin-bottom: 12px;
237
+ padding-right: 2px;
238
+ }
239
+
240
+ .qp-btn {
241
+ display: flex;
242
+ align-items: flex-start;
243
+ gap: 9px;
244
+ padding: 10px 12px;
245
+ border-radius: 10px;
246
+ border: 1px solid rgba(255,255,255,0.05);
247
+ background: rgba(255,255,255,0.02);
248
+ color: rgba(255,255,255,0.55);
249
+ font-family: 'Cairo', sans-serif;
250
+ font-size: 12px;
251
+ cursor: pointer;
252
+ transition: all 0.2s;
253
+ width: 100%;
254
+ text-align: right;
255
+ margin-bottom: 6px;
256
+ line-height: 1.55;
257
+ }
258
+
259
+ .qp-btn svg { width: 13px; height: 13px; flex-shrink: 0; margin-top: 2px; color: var(--accent); }
260
+
261
+ .qp-btn:hover {
262
+ background: rgba(220,38,38,0.1);
263
+ border-color: rgba(220,38,38,0.25);
264
+ color: #ffffff;
265
+ transform: translateX(-2px);
266
+ }
267
+
268
+ /* ══════════════════════════════
269
+ MAIN CHAT AREA
270
+ ══════════════════════════════ */
271
+ .main {
272
+ flex: 1;
273
+ display: flex;
274
+ flex-direction: column;
275
+ min-width: 0;
276
+ background: var(--bg);
277
+ position: relative;
278
+ }
279
+
280
+ /* Subtle grid pattern on bg */
281
+ .main::before {
282
+ content: '';
283
+ position: absolute;
284
+ inset: 0;
285
+ background-image:
286
+ linear-gradient(rgba(220,38,38,0.03) 1px, transparent 1px),
287
+ linear-gradient(90deg, rgba(220,38,38,0.03) 1px, transparent 1px);
288
+ background-size: 40px 40px;
289
+ pointer-events: none;
290
+ z-index: 0;
291
+ }
292
+
293
+ /* Messages scroll area */
294
+ .messages {
295
+ flex: 1;
296
+ overflow-y: auto;
297
+ padding: 28px 28px 16px;
298
+ scroll-behavior: smooth;
299
+ position: relative;
300
+ z-index: 1;
301
+ }
302
+
303
+ .messages::-webkit-scrollbar { width: 4px; }
304
+ .messages::-webkit-scrollbar-thumb { background: rgba(220,38,38,0.2); border-radius: 2px; }
305
+
306
+ /* ══════════════════════════════
307
+ WELCOME SCREEN
308
+ ══════════════════════════════ */
309
+ .welcome {
310
+ display: flex;
311
+ flex-direction: column;
312
+ align-items: center;
313
+ justify-content: center;
314
+ min-height: 55vh;
315
+ text-align: center;
316
+ padding: 40px 20px;
317
+ gap: 14px;
318
+ }
319
+
320
+ .welcome-logo-wrap {
321
+ position: relative;
322
+ width: 120px;
323
+ height: 120px;
324
+ display: flex;
325
+ align-items: center;
326
+ justify-content: center;
327
+ }
328
+
329
+ .welcome-logo-wrap::before {
330
+ content: '';
331
+ position: absolute;
332
+ inset: 0;
333
+ border-radius: 50%;
334
+ background: radial-gradient(circle, rgba(220,38,38,0.15) 0%, transparent 70%);
335
+ animation: pulse 3s ease-in-out infinite;
336
+ }
337
+
338
+ @keyframes pulse {
339
+ 0%,100% { transform: scale(1); opacity: 1; }
340
+ 50% { transform: scale(1.15); opacity: 0.6; }
341
+ }
342
+
343
+ .welcome-logo {
344
+ width: 100px;
345
+ height: 100px;
346
+ object-fit: contain;
347
+ filter: drop-shadow(0 0 30px rgba(220,38,38,0.45));
348
+ animation: float 4s ease-in-out infinite;
349
+ position: relative;
350
+ z-index: 1;
351
+ }
352
+
353
+ @keyframes float {
354
+ 0%,100% { transform: translateY(0); }
355
+ 50% { transform: translateY(-8px); }
356
+ }
357
+
358
+ .welcome h1 {
359
+ font-size: 28px;
360
+ font-weight: 800;
361
+ background: linear-gradient(135deg, var(--dark) 0%, var(--accent) 100%);
362
+ -webkit-background-clip: text;
363
+ -webkit-text-fill-color: transparent;
364
+ background-clip: text;
365
+ }
366
+
367
+ .welcome p {
368
+ color: var(--text2);
369
+ font-size: 13px;
370
+ max-width: 400px;
371
+ line-height: 1.85;
372
+ }
373
+
374
+ .ready-tag {
375
+ display: inline-flex;
376
+ align-items: center;
377
+ gap: 7px;
378
+ padding: 6px 16px;
379
+ background: var(--dark);
380
+ border: 1px solid rgba(220,38,38,0.3);
381
+ border-radius: 20px;
382
+ font-size: 10px;
383
+ color: var(--accent);
384
+ font-family: 'JetBrains Mono', monospace;
385
+ letter-spacing: 1.5px;
386
+ box-shadow: 0 4px 20px rgba(220,38,38,0.12);
387
+ }
388
+
389
+ .welcome-chips {
390
+ display: flex;
391
+ flex-wrap: wrap;
392
+ gap: 8px;
393
+ justify-content: center;
394
+ max-width: 480px;
395
+ margin-top: 4px;
396
+ }
397
+
398
+ .chip {
399
+ padding: 6px 14px;
400
+ background: var(--surface);
401
+ border: 1px solid var(--border2);
402
+ border-radius: 20px;
403
+ font-size: 11px;
404
+ color: var(--text2);
405
+ cursor: pointer;
406
+ transition: all 0.18s;
407
+ font-family: 'Cairo', sans-serif;
408
+ }
409
+
410
+ .chip:hover {
411
+ background: var(--accent);
412
+ color: #fff;
413
+ border-color: var(--accent);
414
+ transform: translateY(-1px);
415
+ box-shadow: 0 4px 12px rgba(220,38,38,0.25);
416
+ }
417
+
418
+ /* ══════════════════════════════
419
+ MESSAGES
420
+ ══════════════════════════════ */
421
+ .msg {
422
+ display: flex;
423
+ gap: 10px;
424
+ margin-bottom: 20px;
425
+ animation: fadeUp 0.25s ease;
426
+ }
427
+
428
+ @keyframes fadeUp {
429
+ from { opacity:0; transform:translateY(10px); }
430
+ to { opacity:1; transform:translateY(0); }
431
+ }
432
+
433
+ .msg.user { flex-direction: row-reverse; }
434
+
435
+ .msg-av {
436
+ width: 36px;
437
+ height: 36px;
438
+ border-radius: 10px;
439
+ display: flex;
440
+ align-items: center;
441
+ justify-content: center;
442
+ flex-shrink: 0;
443
+ overflow: hidden;
444
+ }
445
+
446
+ .msg.user .msg-av {
447
+ background: linear-gradient(135deg, var(--accent), #ff6b6b);
448
+ box-shadow: 0 4px 12px rgba(220,38,38,0.3);
449
+ }
450
+
451
+ .msg.assistant .msg-av {
452
+ background: var(--dark);
453
+ border: 1px solid rgba(220,38,38,0.2);
454
+ }
455
+
456
+ .msg-av svg { width: 17px; height: 17px; color: #fff; }
457
+ .msg-av img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
458
+
459
+ .msg-body {
460
+ max-width: 72%;
461
+ min-width: 0;
462
+ display: flex;
463
+ flex-direction: column;
464
+ }
465
+
466
+ .msg.user .msg-body { align-items: flex-end; }
467
+
468
+ .msg-bubble {
469
+ padding: 12px 16px;
470
+ border-radius: 16px;
471
+ font-size: 14px;
472
+ line-height: 1.75;
473
+ word-break: break-word;
474
+ }
475
+
476
+ .msg.user .msg-bubble {
477
+ background: linear-gradient(135deg, var(--accent), #ff6b6b);
478
+ color: #fff;
479
+ border-bottom-left-radius: 4px;
480
+ box-shadow: 0 4px 16px rgba(220,38,38,0.25);
481
+ }
482
+
483
+ .msg.assistant .msg-bubble {
484
+ background: #ffffff;
485
+ border: 1px solid var(--border2);
486
+ color: var(--text);
487
+ border-bottom-right-radius: 4px;
488
+ box-shadow: 0 2px 12px rgba(0,0,0,0.06);
489
+ }
490
+
491
+ .msg-time {
492
+ font-size: 10px;
493
+ color: var(--muted);
494
+ margin-top: 5px;
495
+ font-family: 'JetBrains Mono', monospace;
496
+ padding: 0 3px;
497
+ }
498
+
499
+ /* Bubble content styles */
500
+ .msg-bubble h1,.msg-bubble h2,.msg-bubble h3,.msg-bubble h4 {
501
+ font-weight: 700; margin: 10px 0 5px; color: var(--accent);
502
+ }
503
+ .msg-bubble h1{font-size:17px} .msg-bubble h2{font-size:15px}
504
+ .msg-bubble h3{font-size:14px} .msg-bubble h4{font-size:13px}
505
+ .msg-bubble p { margin: 5px 0; }
506
+ .msg-bubble ul,.msg-bubble ol { padding-right: 18px; margin: 5px 0; }
507
+ .msg-bubble li { margin: 3px 0; }
508
+ .msg-bubble strong { color: var(--accent); font-weight: 700; }
509
+ .msg-bubble em { color: #7c3aed; }
510
+ .msg-bubble a { color: var(--accent); text-decoration: underline; }
511
+ .msg-bubble blockquote {
512
+ border-right: 3px solid var(--accent);
513
+ padding-right: 10px;
514
+ color: var(--text2);
515
+ margin: 6px 0;
516
+ background: var(--accent-dim);
517
+ border-radius: 0 6px 6px 0;
518
+ padding: 6px 10px;
519
+ }
520
+ .msg-bubble hr { border:none; border-top:1px solid var(--border2); margin:8px 0; }
521
+
522
+ .msg-bubble code {
523
+ background: rgba(220,38,38,0.08);
524
+ border: 1px solid rgba(220,38,38,0.18);
525
+ padding: 1px 6px;
526
+ border-radius: 4px;
527
+ font-family: 'JetBrains Mono', monospace;
528
+ font-size: 12px;
529
+ color: var(--accent);
530
+ }
531
+
532
+ .msg-bubble pre {
533
+ background: var(--dark);
534
+ border: 1px solid rgba(220,38,38,0.15);
535
+ border-radius: 10px;
536
+ overflow: hidden;
537
+ margin: 8px 0;
538
+ position: relative;
539
+ }
540
+
541
+ .msg-bubble pre .code-header {
542
+ display: flex;
543
+ align-items: center;
544
+ justify-content: space-between;
545
+ padding: 7px 12px;
546
+ background: rgba(220,38,38,0.08);
547
+ border-bottom: 1px solid rgba(220,38,38,0.12);
548
+ }
549
+
550
+ .msg-bubble pre .code-lang {
551
+ font-size: 10px;
552
+ color: var(--accent);
553
+ font-family: 'JetBrains Mono', monospace;
554
+ letter-spacing: 1px;
555
+ }
556
+
557
+ .msg-bubble pre code {
558
+ display: block;
559
+ padding: 12px;
560
+ background: transparent;
561
+ border: none;
562
+ font-size: 12px;
563
+ color: #e2e8f0;
564
+ overflow-x: auto;
565
+ white-space: pre;
566
+ }
567
+
568
+ .copy-btn {
569
+ background: transparent;
570
+ border: 1px solid rgba(255,255,255,0.12);
571
+ color: rgba(255,255,255,0.4);
572
+ padding: 3px 8px;
573
+ border-radius: 5px;
574
+ font-size: 10px;
575
+ cursor: pointer;
576
+ font-family: 'JetBrains Mono', monospace;
577
+ transition: all 0.15s;
578
+ display: flex;
579
+ align-items: center;
580
+ gap: 4px;
581
+ }
582
+
583
+ .copy-btn:hover {
584
+ color: #fff;
585
+ border-color: rgba(220,38,38,0.4);
586
+ background: rgba(220,38,38,0.1);
587
+ }
588
+
589
+ .copy-btn svg { width: 11px; height: 11px; }
590
+
591
+ /* DTC badge */
592
+ .dtc-badge {
593
+ display: inline-flex;
594
+ align-items: center;
595
+ gap: 5px;
596
+ background: rgba(220,38,38,0.08);
597
+ border: 1px solid rgba(220,38,38,0.25);
598
+ border-radius: 6px;
599
+ padding: 2px 8px;
600
+ font-family: 'JetBrains Mono', monospace;
601
+ font-size: 12px;
602
+ font-weight: 600;
603
+ color: var(--accent);
604
+ }
605
+
606
+ .dtc-led {
607
+ width: 5px;
608
+ height: 5px;
609
+ border-radius: 50%;
610
+ background: var(--accent);
611
+ animation: blink 1.5s infinite;
612
+ box-shadow: 0 0 5px var(--accent);
613
+ }
614
+
615
+ /* Message images */
616
+ .msg-images {
617
+ display: flex;
618
+ flex-wrap: wrap;
619
+ gap: 6px;
620
+ margin-bottom: 8px;
621
+ }
622
+
623
+ .msg-images img {
624
+ width: 120px;
625
+ height: 90px;
626
+ object-fit: cover;
627
+ border-radius: 8px;
628
+ cursor: pointer;
629
+ border: 1px solid var(--border2);
630
+ transition: all 0.2s;
631
+ }
632
+
633
+ .msg-images img:hover {
634
+ opacity: 0.85;
635
+ transform: scale(1.02);
636
+ box-shadow: 0 4px 14px rgba(220,38,38,0.2);
637
+ }
638
+
639
+ /* Web results */
640
+ .web-results {
641
+ margin-top: 10px;
642
+ background: var(--surface);
643
+ border: 1px solid var(--border2);
644
+ border-radius: 10px;
645
+ padding: 12px;
646
+ font-size: 12px;
647
+ }
648
+
649
+ .web-results h4 {
650
+ color: var(--accent) !important;
651
+ font-size: 12px !important;
652
+ margin: 0 0 8px !important;
653
+ display: flex;
654
+ align-items: center;
655
+ gap: 6px;
656
+ }
657
+
658
+ .web-results h4 svg { width: 13px; height: 13px; }
659
+
660
+ .web-result-item {
661
+ padding: 7px 0;
662
+ border-bottom: 1px solid var(--border);
663
+ }
664
+
665
+ .web-result-item:last-child { border-bottom: none; }
666
+
667
+ .web-result-title {
668
+ font-weight: 700;
669
+ color: var(--text);
670
+ margin-bottom: 3px;
671
+ font-size: 12px;
672
+ }
673
+
674
+ .web-result-snippet {
675
+ color: var(--text2);
676
+ font-size: 11px;
677
+ line-height: 1.5;
678
+ margin-bottom: 3px;
679
+ }
680
+
681
+ .web-result-url {
682
+ color: var(--accent);
683
+ font-size: 10px;
684
+ text-decoration: none;
685
+ font-family: 'JetBrains Mono', monospace;
686
+ display: flex;
687
+ align-items: center;
688
+ gap: 4px;
689
+ }
690
+
691
+ .web-result-url svg { width: 10px; height: 10px; }
692
+
693
+ /* Image results */
694
+ .img-results {
695
+ margin-top: 10px;
696
+ }
697
+
698
+ .img-results-header {
699
+ font-size: 11px;
700
+ color: var(--text2);
701
+ margin-bottom: 8px;
702
+ display: flex;
703
+ align-items: center;
704
+ gap: 6px;
705
+ font-family: 'JetBrains Mono', monospace;
706
+ }
707
+
708
+ .img-results-header svg { width: 12px; height: 12px; color: var(--accent); }
709
+
710
+ .img-grid {
711
+ display: grid;
712
+ grid-template-columns: repeat(3, 1fr);
713
+ gap: 6px;
714
+ }
715
+
716
+ .img-card {
717
+ aspect-ratio: 4/3;
718
+ border-radius: 8px;
719
+ overflow: hidden;
720
+ background: var(--surface2);
721
+ border: 1px solid var(--border2);
722
+ cursor: pointer;
723
+ position: relative;
724
+ }
725
+
726
+ .img-card img {
727
+ width: 100%;
728
+ height: 100%;
729
+ object-fit: cover;
730
+ transition: transform 0.2s;
731
+ }
732
+
733
+ .img-card:hover img { transform: scale(1.05); }
734
+
735
+ .img-card-overlay {
736
+ position: absolute;
737
+ inset: 0;
738
+ background: rgba(220,38,38,0.55);
739
+ display: flex;
740
+ align-items: center;
741
+ justify-content: center;
742
+ opacity: 0;
743
+ transition: opacity 0.2s;
744
+ font-size: 11px;
745
+ color: #fff;
746
+ gap: 4px;
747
+ }
748
+
749
+ .img-card-overlay svg { width: 13px; height: 13px; }
750
+ .img-card:hover .img-card-overlay { opacity: 1; }
751
+
752
+ .img-card.errored { cursor: default; }
753
+ .img-card.errored img { display: none; }
754
+
755
+ .img-err {
756
+ position: absolute;
757
+ inset: 0;
758
+ display: none;
759
+ flex-direction: column;
760
+ align-items: center;
761
+ justify-content: center;
762
+ gap: 4px;
763
+ color: var(--muted);
764
+ font-size: 10px;
765
+ }
766
+
767
+ .img-err svg { width: 16px; height: 16px; }
768
+ .img-card.errored .img-err { display: flex; }
769
+
770
+ /* Typing indicator */
771
+ .typing-wrap {
772
+ display: flex;
773
+ gap: 10px;
774
+ margin-bottom: 18px;
775
+ align-items: flex-start;
776
+ }
777
+
778
+ .typing-av {
779
+ width: 36px;
780
+ height: 36px;
781
+ border-radius: 10px;
782
+ background: var(--dark);
783
+ border: 1px solid rgba(220,38,38,0.2);
784
+ display: flex;
785
+ align-items: center;
786
+ justify-content: center;
787
+ flex-shrink: 0;
788
+ overflow: hidden;
789
+ }
790
+
791
+ .typing-av img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
792
+
793
+ .typing-dots {
794
+ background: #ffffff;
795
+ border: 1px solid var(--border2);
796
+ border-radius: 16px;
797
+ border-bottom-right-radius: 4px;
798
+ padding: 13px 18px;
799
+ display: flex;
800
+ gap: 5px;
801
+ align-items: center;
802
+ box-shadow: 0 2px 12px rgba(0,0,0,0.06);
803
+ }
804
+
805
+ .typing-dots span {
806
+ width: 7px;
807
+ height: 7px;
808
+ border-radius: 50%;
809
+ background: var(--muted);
810
+ animation: bounce 1.2s infinite;
811
+ }
812
+
813
+ .typing-dots span:nth-child(2) { animation-delay: 0.2s; }
814
+ .typing-dots span:nth-child(3) { animation-delay: 0.4s; }
815
+
816
+ @keyframes bounce {
817
+ 0%,60%,100% { transform: translateY(0); background: var(--muted); }
818
+ 30% { transform: translateY(-6px); background: var(--accent); }
819
+ }
820
+
821
+ /* ══════════════════════════════
822
+ INPUT AREA
823
+ ══════════════════════════════ */
824
+ .input-area {
825
+ padding: 12px 24px 18px;
826
+ background: var(--bg);
827
+ border-top: 1px solid var(--border2);
828
+ position: relative;
829
+ z-index: 1;
830
+ }
831
+
832
+ .input-area::before {
833
+ content: '';
834
+ position: absolute;
835
+ top: -1px;
836
+ left: 0;
837
+ right: 0;
838
+ height: 1px;
839
+ background: linear-gradient(90deg, transparent, rgba(220,38,38,0.3), transparent);
840
+ }
841
+
842
+ .file-prev {
843
+ margin-bottom: 8px;
844
+ }
845
+
846
+ .file-prev-inner {
847
+ display: inline-flex;
848
+ align-items: center;
849
+ gap: 8px;
850
+ background: var(--surface);
851
+ border: 1px solid var(--border2);
852
+ border-radius: 8px;
853
+ padding: 6px 10px;
854
+ font-size: 12px;
855
+ color: var(--text2);
856
+ }
857
+
858
+ .file-prev-inner svg { width: 13px; height: 13px; color: var(--accent); }
859
+
860
+ .file-rm {
861
+ background: transparent;
862
+ border: none;
863
+ color: var(--muted);
864
+ cursor: pointer;
865
+ padding: 0 2px;
866
+ display: flex;
867
+ align-items: center;
868
+ transition: color 0.15s;
869
+ }
870
+
871
+ .file-rm:hover { color: var(--red); }
872
+ .file-rm svg { width: 13px; height: 13px; }
873
+
874
+ /* Image previews */
875
+ .img-prev-header {
876
+ display: flex;
877
+ align-items: center;
878
+ gap: 6px;
879
+ font-size: 11px;
880
+ color: var(--text2);
881
+ margin-bottom: 6px;
882
+ font-family: 'JetBrains Mono', monospace;
883
+ }
884
+
885
+ .img-prev-header svg { width: 12px; height: 12px; color: var(--accent); }
886
+
887
+ .img-prev-grid {
888
+ display: flex;
889
+ flex-wrap: wrap;
890
+ gap: 6px;
891
+ }
892
+
893
+ .img-prev-item {
894
+ position: relative;
895
+ width: 62px;
896
+ height: 62px;
897
+ border-radius: 9px;
898
+ overflow: hidden;
899
+ border: 1px solid rgba(220,38,38,0.2);
900
+ box-shadow: 0 2px 8px rgba(220,38,38,0.1);
901
+ }
902
+
903
+ .img-prev-item img {
904
+ width: 100%;
905
+ height: 100%;
906
+ object-fit: cover;
907
+ }
908
+
909
+ .img-prev-rm {
910
+ position: absolute;
911
+ top: 2px;
912
+ left: 2px;
913
+ width: 18px;
914
+ height: 18px;
915
+ border-radius: 50%;
916
+ background: rgba(220,38,38,0.85);
917
+ border: none;
918
+ color: #fff;
919
+ cursor: pointer;
920
+ display: flex;
921
+ align-items: center;
922
+ justify-content: center;
923
+ transition: background 0.15s;
924
+ }
925
+
926
+ .img-prev-rm:hover { background: var(--accent); }
927
+ .img-prev-rm svg { width: 10px; height: 10px; }
928
+
929
+ .img-uploading {
930
+ position: absolute;
931
+ inset: 0;
932
+ background: rgba(0,0,0,0.6);
933
+ display: flex;
934
+ flex-direction: column;
935
+ align-items: center;
936
+ justify-content: center;
937
+ gap: 3px;
938
+ font-size: 9px;
939
+ color: #fff;
940
+ }
941
+
942
+ /* Input row */
943
+ .input-row {
944
+ display: flex;
945
+ gap: 8px;
946
+ align-items: flex-end;
947
+ }
948
+
949
+ .input-box {
950
+ flex: 1;
951
+ background: #ffffff;
952
+ border: 1.5px solid var(--border2);
953
+ border-radius: 14px;
954
+ display: flex;
955
+ align-items: flex-end;
956
+ gap: 4px;
957
+ padding: 9px 12px;
958
+ transition: all 0.2s;
959
+ box-shadow: 0 2px 10px rgba(0,0,0,0.05);
960
+ }
961
+
962
+ .input-box:focus-within {
963
+ border-color: var(--accent);
964
+ box-shadow: 0 0 0 3px rgba(220,38,38,0.08), 0 2px 10px rgba(0,0,0,0.05);
965
+ }
966
+
967
+ .input-box textarea {
968
+ flex: 1;
969
+ background: transparent;
970
+ border: none;
971
+ outline: none;
972
+ color: var(--text);
973
+ font-family: 'Cairo', sans-serif;
974
+ font-size: 14px;
975
+ resize: none;
976
+ line-height: 1.5;
977
+ max-height: 130px;
978
+ min-height: 24px;
979
+ padding: 0;
980
+ direction: rtl;
981
+ }
982
+
983
+ .input-box textarea::placeholder { color: var(--muted); }
984
+
985
+ .input-icons {
986
+ display: flex;
987
+ align-items: center;
988
+ gap: 2px;
989
+ flex-shrink: 0;
990
+ }
991
+
992
+ .ic-btn {
993
+ width: 30px;
994
+ height: 30px;
995
+ border-radius: 7px;
996
+ border: none;
997
+ background: transparent;
998
+ color: var(--muted);
999
+ cursor: pointer;
1000
+ display: flex;
1001
+ align-items: center;
1002
+ justify-content: center;
1003
+ transition: all 0.15s;
1004
+ flex-shrink: 0;
1005
+ }
1006
+
1007
+ .ic-btn:hover {
1008
+ background: var(--accent-dim);
1009
+ color: var(--accent);
1010
+ }
1011
+
1012
+ .ic-btn svg { width: 15px; height: 15px; }
1013
+ .ic-btn.active { color: var(--accent); }
1014
+
1015
+ input[type="file"] { display: none; }
1016
+
1017
+ .send-btn {
1018
+ width: 46px;
1019
+ height: 46px;
1020
+ border-radius: 13px;
1021
+ border: none;
1022
+ background: linear-gradient(135deg, var(--accent), #ff6b6b);
1023
+ color: #fff;
1024
+ cursor: pointer;
1025
+ display: flex;
1026
+ align-items: center;
1027
+ justify-content: center;
1028
+ flex-shrink: 0;
1029
+ transition: all 0.2s;
1030
+ box-shadow: 0 4px 16px rgba(220,38,38,0.35);
1031
+ }
1032
+
1033
+ .send-btn:hover {
1034
+ transform: scale(1.06);
1035
+ box-shadow: 0 6px 22px rgba(220,38,38,0.45);
1036
+ }
1037
+
1038
+ .send-btn:disabled {
1039
+ opacity: 0.5;
1040
+ transform: none;
1041
+ cursor: not-allowed;
1042
+ box-shadow: none;
1043
+ }
1044
+
1045
+ .send-btn svg { width: 18px; height: 18px; }
1046
+
1047
+ /* Spinner */
1048
+ .spinner {
1049
+ width: 18px;
1050
+ height: 18px;
1051
+ border: 2px solid rgba(255,255,255,0.3);
1052
+ border-top-color: #fff;
1053
+ border-radius: 50%;
1054
+ animation: spinAnim 0.7s linear infinite;
1055
+ }
1056
+
1057
+ .spinner-sm {
1058
+ width: 12px;
1059
+ height: 12px;
1060
+ border: 2px solid rgba(255,255,255,0.3);
1061
+ border-top-color: #fff;
1062
+ border-radius: 50%;
1063
+ animation: spinAnim 0.7s linear infinite;
1064
+ }
1065
+
1066
+ @keyframes spinAnim { to { transform: rotate(360deg); } }
1067
+
1068
+ /* Status toast */
1069
+ .toast {
1070
+ display: flex;
1071
+ align-items: center;
1072
+ gap: 8px;
1073
+ padding: 9px 14px;
1074
+ border-radius: 9px;
1075
+ font-size: 12px;
1076
+ margin-bottom: 10px;
1077
+ animation: fadeUp 0.2s ease;
1078
+ }
1079
+
1080
+ .toast.success {
1081
+ background: rgba(22,163,74,0.08);
1082
+ border: 1px solid rgba(22,163,74,0.2);
1083
+ color: var(--green);
1084
+ }
1085
+
1086
+ .toast.error {
1087
+ background: rgba(220,38,38,0.08);
1088
+ border: 1px solid rgba(220,38,38,0.2);
1089
+ color: var(--red);
1090
+ }
1091
+
1092
+ .toast svg { width: 13px; height: 13px; flex-shrink: 0; }
1093
+
1094
+ /* ══════════════════════════════
1095
+ IMAGE MODAL
1096
+ ══════════════════════════════ */
1097
+ .modal {
1098
+ position: fixed;
1099
+ inset: 0;
1100
+ background: rgba(0,0,0,0.94);
1101
+ z-index: 1000;
1102
+ display: flex;
1103
+ align-items: center;
1104
+ justify-content: center;
1105
+ opacity: 0;
1106
+ pointer-events: none;
1107
+ transition: opacity 0.2s;
1108
+ }
1109
+
1110
+ .modal.active { opacity: 1; pointer-events: all; }
1111
+
1112
+ .modal img {
1113
+ max-width: 90vw;
1114
+ max-height: 88vh;
1115
+ border-radius: 12px;
1116
+ object-fit: contain;
1117
+ box-shadow: 0 20px 60px rgba(0,0,0,0.6);
1118
+ }
1119
+
1120
+ .modal-close {
1121
+ position: absolute;
1122
+ top: 16px;
1123
+ left: 16px;
1124
+ width: 38px;
1125
+ height: 38px;
1126
+ border-radius: 50%;
1127
+ background: rgba(220,38,38,0.15);
1128
+ border: 1px solid rgba(220,38,38,0.3);
1129
+ color: #fff;
1130
+ cursor: pointer;
1131
+ display: flex;
1132
+ align-items: center;
1133
+ justify-content: center;
1134
+ transition: all 0.15s;
1135
+ }
1136
+
1137
+ .modal-close:hover {
1138
+ background: var(--accent);
1139
+ border-color: var(--accent);
1140
+ }
1141
+
1142
+ .modal-close svg { width: 16px; height: 16px; }
1143
+
1144
+ /* ══════════════════════════════
1145
+ MOBILE TOGGLE BUTTON
1146
+ ══════════════════════════════ */
1147
+ .mob-toggle {
1148
+ display: none;
1149
+ position: fixed;
1150
+ top: 12px;
1151
+ right: 12px;
1152
+ z-index: 200;
1153
+ width: 40px;
1154
+ height: 40px;
1155
+ border-radius: 11px;
1156
+ background: var(--dark);
1157
+ border: 1px solid rgba(220,38,38,0.25);
1158
+ color: #ffffff;
1159
+ cursor: pointer;
1160
+ align-items: center;
1161
+ justify-content: center;
1162
+ transition: all 0.15s;
1163
+ box-shadow: 0 4px 14px rgba(0,0,0,0.2);
1164
+ }
1165
+
1166
+ .mob-toggle:hover {
1167
+ background: var(--accent);
1168
+ border-color: var(--accent);
1169
+ }
1170
+
1171
+ .mob-toggle svg { width: 17px; height: 17px; }
1172
+
1173
+ /* Sidebar overlay for mobile */
1174
+ .sidebar-overlay {
1175
+ display: none;
1176
+ position: fixed;
1177
+ inset: 0;
1178
+ background: rgba(0,0,0,0.65);
1179
+ z-index: 150;
1180
+ backdrop-filter: blur(2px);
1181
+ }
1182
+
1183
+ /* ══════════════════════════════
1184
+ RESPONSIVE — MOBILE
1185
+ ══════════════════════════════ */
1186
+ @media (max-width: 768px) {
1187
+
1188
+ .mob-toggle { display: flex; }
1189
+
1190
+ .sidebar {
1191
+ position: fixed;
1192
+ top: 0;
1193
+ right: 0;
1194
+ height: 100%;
1195
+ z-index: 160;
1196
+ transform: translateX(100%);
1197
+ transition: transform 0.28s cubic-bezier(0.4,0,0.2,1);
1198
+ width: 265px;
1199
+ }
1200
+
1201
+ .sidebar.open { transform: translateX(0); }
1202
+
1203
+ .sidebar-overlay.active { display: block; }
1204
+
1205
+ .main { width: 100%; }
1206
+
1207
+ .messages { padding: 60px 14px 12px; }
1208
+
1209
+ .msg-body { max-width: 84%; }
1210
+
1211
+ .msg-bubble { font-size: 13px; padding: 10px 13px; }
1212
+
1213
+ .welcome { min-height: 50vh; padding: 60px 16px 20px; }
1214
+
1215
+ .welcome-logo { width: 80px; height: 80px; }
1216
+
1217
+ .welcome h1 { font-size: 21px; }
1218
+
1219
+ .welcome p { font-size: 12px; }
1220
+
1221
+ .input-area { padding: 8px 12px 14px; }
1222
+
1223
+ .input-box textarea { font-size: 13px; }
1224
+
1225
+ .send-btn { width: 42px; height: 42px; border-radius: 11px; }
1226
+
1227
+ .img-grid { grid-template-columns: repeat(2, 1fr); }
1228
+
1229
+ .welcome-chips { display: none; }
1230
+ }
1231
+
1232
+ @media (max-width: 400px) {
1233
+ .msg-body { max-width: 90%; }
1234
+ .msg-bubble { font-size: 12px; }
1235
+ .welcome h1 { font-size: 18px; }
1236
+ }
1237
+ </style>
1238
+ </head>
1239
+ <body>
1240
+
1241
+ <div class="app">
1242
+
1243
+ <!-- ── SIDEBAR ── -->
1244
+ <aside class="sidebar" id="sidebar">
1245
+
1246
+ <div class="sidebar-logo">
1247
+ <div class="logo-ring">
1248
+ <img src="{{ url_for('static', filename='logo.png') }}" alt="SHKIKEL" class="logo-img">
1249
+ </div>
1250
+ <div class="logo-name">SHKIKEL GROUP</div>
1251
+ <div class="logo-sub">OBD-II DIAGNOSTIC SYSTEM</div>
1252
+ <div class="status-pill" id="statusPill">
1253
+ <div class="s-dot" id="sDot"></div>
1254
+ <span id="sLabel">ONLINE</span>
1255
+ </div>
1256
+ </div>
1257
+
1258
+ <div class="sidebar-btns">
1259
+ <button class="sb-btn" onclick="exportChat()">
1260
+ <i data-lucide="download"></i>
1261
+ <span>تصدير السجل</span>
1262
+ </button>
1263
+ <button class="sb-btn red" onclick="clearChat()">
1264
+ <i data-lucide="trash-2"></i>
1265
+ <span>مسح المحادثة</span>
1266
+ </button>
1267
+ </div>
1268
+
1269
+ <div class="sidebar-quick">
1270
+ <div class="ql">اقتراحات سريعة</div>
1271
+
1272
+ <button class="qp-btn" onclick="qs('عندي كود P0300 على تويوتا كامري 2019. شو يعني وكيف أصلحه؟')">
1273
+ <i data-lucide="wrench"></i>
1274
+ تشخيص كود عطل OBD-II
1275
+ </button>
1276
+
1277
+ <button class="qp-btn" onclick="qs('لمبة الماكينة مولعة والسيارة بترجف على الرالنتي. شو المشكلة؟')">
1278
+ <i data-lucide="alert-triangle"></i>
1279
+ وصف أعراض السيارة
1280
+ </button>
1281
+
1282
+ <button class="qp-btn" onclick="document.getElementById('imageInput').click()">
1283
+ <i data-lucide="camera"></i>
1284
+ تحليل صورة / شاشة ماسح
1285
+ </button>
1286
+
1287
+ <button class="qp-btn" onclick="qs('شو هي ال��كواد من P0100 لـ P0199 وعلى شو بتدل؟')">
1288
+ <i data-lucide="list"></i>
1289
+ مرجع أكواد OBD-II
1290
+ </button>
1291
+
1292
+ <button class="qp-btn" onclick="qs('كيف أعمل صيانة دورية لسيارتي وشو أهم النقاط؟')">
1293
+ <i data-lucide="settings"></i>
1294
+ نصائح الصيانة الدورية
1295
+ </button>
1296
+ </div>
1297
+
1298
+ </aside>
1299
+
1300
+ <!-- Overlay for mobile -->
1301
+ <div class="sidebar-overlay" id="sidebarOverlay" onclick="closeSidebar()"></div>
1302
+
1303
+ <!-- Mobile toggle -->
1304
+ <button class="mob-toggle" id="mobToggle" onclick="toggleSidebar()">
1305
+ <i data-lucide="menu"></i>
1306
+ </button>
1307
+
1308
+ <!-- ── MAIN ── -->
1309
+ <main class="main">
1310
+
1311
+ <div class="messages" id="messages">
1312
+ <div class="welcome" id="welcomeScreen">
1313
+ <div class="welcome-logo-wrap">
1314
+ <img src="{{ url_for('static', filename='logo.png') }}" alt="SHKIKEL" class="welcome-logo
1315
+ You: complete from the same point you sttoped dont back to 0
1316
+ AI THINKING...AI: Continuing from where I stopped:
1317
+
1318
+ ```html
1319
+ ">
1320
+ </div>
1321
+ <h1>مساعد التشخيص الذكي</h1>
1322
+ <p>أدخل رمز العطل OBD-II أو صوّر أعراض سيارتك. سأشخّص المشكلة وأوضح السبب وأريك موقع القطعة بالصور.</p>
1323
+ <div class="ready-tag">
1324
+ <div class="s-dot"></div>
1325
+ READY FOR DIAGNOSTICS
1326
+ </div>
1327
+ <div class="welcome-chips">
1328
+ <span class="chip" onclick="qs('شو يعني كود P0420؟')">P0420 كود</span>
1329
+ <span class="chip" onclick="qs('سيارتي بتدخن دخان أبيض')">دخان أبيض</span>
1330
+ <span class="chip" onclick="qs('السيارة ما بتشتغل بالصبح')">مشكلة تشغيل</span>
1331
+ <span class="chip" onclick="qs('شو يعني كود P0171؟')">P0171 كود</span>
1332
+ <span class="chip" onclick="qs('فرامل السيارة بتصرصر')">صوت فرامل</span>
1333
+ <span class="chip" onclick="qs('كيف أغير زيت المحرك؟')">تغيير زيت</span>
1334
+ </div>
1335
+ </div>
1336
+ </div>
1337
+
1338
+ <div class="input-area">
1339
+ <div class="file-prev" id="filePrev"></div>
1340
+ <div id="imgPrevContainer"></div>
1341
+ <div class="input-row">
1342
+ <div class="input-box">
1343
+ <textarea id="msgInput" placeholder="أدخل كود العطل (مثال: P0420) أو اوصف المشكلة..." rows="1"></textarea>
1344
+ <div class="input-icons">
1345
+ <button class="ic-btn" onclick="document.getElementById('fileInput').click()" title="رفع ملف">
1346
+ <i data-lucide="paperclip"></i>
1347
+ </button>
1348
+ <button class="ic-btn" onclick="document.getElementById('imageInput').click()" title="رفع صورة">
1349
+ <i data-lucide="image"></i>
1350
+ </button>
1351
+ <button class="ic-btn" id="micBtn" title="إدخال صوتي">
1352
+ <i data-lucide="mic"></i>
1353
+ </button>
1354
+ </div>
1355
+ </div>
1356
+ <button class="send-btn" id="sendBtn" onclick="sendMessage()">
1357
+ <i data-lucide="send"></i>
1358
+ </button>
1359
+ </div>
1360
+ <input type="file" id="fileInput" accept=".txt,.json,.csv,.md,.log" onchange="handleFileSelect(event)">
1361
+ <input type="file" id="imageInput" accept="image/png,image/jpeg,image/jpg,image/gif,image/webp,image/bmp" multiple onchange="handleImageSelect(event)">
1362
+ </div>
1363
+
1364
+ </main>
1365
+ </div>
1366
+
1367
+ <!-- Image Modal -->
1368
+ <div class="modal" id="imgModal" onclick="closeModal()">
1369
+ <button class="modal-close" onclick="closeModal()"><i data-lucide="x"></i></button>
1370
+ <img id="modalImg" src="" alt="" onclick="event.stopPropagation()">
1371
+ </div>
1372
+ <script>
1373
+ /* ── INIT ── */
1374
+ document.addEventListener('DOMContentLoaded', () => {
1375
+ if (typeof lucide !== 'undefined') lucide.createIcons();
1376
+ initSpeech();
1377
+ checkHealth();
1378
+ msgInput.focus();
1379
+ });
1380
+
1381
+ function ri() { if (typeof lucide !== 'undefined') setTimeout(() => lucide.createIcons(), 50); }
1382
+
1383
+ const sessionId = 'session_' + Date.now();
1384
+ let selectedFile = null;
1385
+ let selectedImages = [];
1386
+ let uploadedImageUrls = [];
1387
+ let isSending = false;
1388
+
1389
+ /* ── TEXTAREA AUTO-RESIZE ── */
1390
+ const msgInput = document.getElementById('msgInput');
1391
+ msgInput.addEventListener('input', function () {
1392
+ this.style.height = 'auto';
1393
+ this.style.height = Math.min(this.scrollHeight, 130) + 'px';
1394
+ });
1395
+ msgInput.addEventListener('keydown', function (e) {
1396
+ if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
1397
+ });
1398
+
1399
+ /* ── SIDEBAR TOGGLE ── */
1400
+ function toggleSidebar() {
1401
+ const sb = document.getElementById('sidebar');
1402
+ const ov = document.getElementById('sidebarOverlay');
1403
+ sb.classList.toggle('open');
1404
+ ov.classList.toggle('active');
1405
+ }
1406
+
1407
+ function closeSidebar() {
1408
+ document.getElementById('sidebar').classList.remove('open');
1409
+ document.getElementById('sidebarOverlay').classList.remove('active');
1410
+ }
1411
+
1412
+ /* ── UTILS ── */
1413
+ function escHtml(t) { const d = document.createElement('div'); d.textContent = t; return d.innerHTML; }
1414
+
1415
+ function qs(text) { msgInput.value = text; closeSidebar(); sendMessage(); }
1416
+
1417
+ /* ── IMAGE HANDLING ── */
1418
+ async function handleImageSelect(e) {
1419
+ const files = Array.from(e.target.files);
1420
+ if (!files.length) return;
1421
+
1422
+ // Mobile fix: reset input so same file can be selected again
1423
+ const inputEl = document.getElementById('imageInput');
1424
+
1425
+ const valid = ['image/png','image/jpeg','image/jpg','image/gif','image/webp','image/bmp'];
1426
+
1427
+ const invalidFiles = files.filter(f => !valid.includes(f.type.toLowerCase()));
1428
+ if (invalidFiles.length > 0) {
1429
+ showToast('نوع ملف غير مدعوم. استخدم: PNG, JPG, GIF, WEBP', 'error');
1430
+ inputEl.value = '';
1431
+ return;
1432
+ }
1433
+
1434
+ const oversized = files.filter(f => f.size > 10 * 1024 * 1024);
1435
+ if (oversized.length > 0) {
1436
+ showToast('حجم الصورة يجب أن يكون أقل من 10MB', 'error');
1437
+ inputEl.value = '';
1438
+ return;
1439
+ }
1440
+
1441
+ selectedImages = files;
1442
+ renderImgPreviews(files);
1443
+ await uploadAllImages();
1444
+ }
1445
+ function renderImgPreviews(files) {
1446
+ const c = document.getElementById('imgPrevContainer');
1447
+ c.innerHTML = '';
1448
+ if (!files.length) return;
1449
+ const wrap = document.createElement('div');
1450
+ wrap.className = 'img-prev-wrap';
1451
+ const hdr = document.createElement('div');
1452
+ hdr.className = 'img-prev-header';
1453
+ hdr.innerHTML = `<i data-lucide="image"></i><span>الصور المحددة (${files.length})</span>`;
1454
+ wrap.appendChild(hdr);
1455
+ const grid = document.createElement('div');
1456
+ grid.className = 'img-prev-grid';
1457
+ files.forEach((file, i) => {
1458
+ const item = document.createElement('div');
1459
+ item.className = 'img-prev-item';
1460
+ item.dataset.index = i;
1461
+ const img = document.createElement('img');
1462
+ img.src = URL.createObjectURL(file);
1463
+ item.appendChild(img);
1464
+ const rm = document.createElement('button');
1465
+ rm.className = 'img-prev-rm';
1466
+ rm.innerHTML = '<i data-lucide="x"></i>';
1467
+ rm.onclick = (ev) => { ev.stopPropagation(); removeImage(i); };
1468
+ item.appendChild(rm);
1469
+ const upl = document.createElement('div');
1470
+ upl.className = 'img-uploading';
1471
+ upl.innerHTML = '<div class="spinner-sm"></div><span>رفع...</span>';
1472
+ item.appendChild(upl);
1473
+ grid.appendChild(item);
1474
+ });
1475
+ wrap.appendChild(grid);
1476
+ c.appendChild(wrap);
1477
+ ri();
1478
+ }
1479
+
1480
+ async function uploadAllImages() {
1481
+ uploadedImageUrls = [];
1482
+
1483
+ for (let i = 0; i < selectedImages.length; i++) {
1484
+ const file = selectedImages[i];
1485
+ const item = document.querySelector(`.img-prev-item[data-index="${i}"]`);
1486
+
1487
+ try {
1488
+ // Mobile fix: convert to blob first to ensure proper encoding
1489
+ const blob = await readFileAsBlob(file);
1490
+
1491
+ const fd = new FormData();
1492
+ fd.append('image', blob, file.name || `image_${i}.jpg`);
1493
+
1494
+ const res = await fetch('/api/upload-image', {
1495
+ method: 'POST',
1496
+ body: fd,
1497
+ // Mobile fix: do NOT set Content-Type header manually
1498
+ // Let the browser set it with the correct boundary
1499
+ });
1500
+
1501
+ if (!res.ok) {
1502
+ throw new Error(`HTTP error: ${res.status}`);
1503
+ }
1504
+
1505
+ const data = await res.json();
1506
+
1507
+ if (data.success && data.url) {
1508
+ uploadedImageUrls.push(data.url);
1509
+ if (item) {
1510
+ const u = item.querySelector('.img-uploading');
1511
+ if (u) u.remove();
1512
+ }
1513
+ } else {
1514
+ throw new Error(data.error || 'فشل رفع الصورة');
1515
+ }
1516
+
1517
+ } catch (err) {
1518
+ console.error('Upload error:', err);
1519
+ showToast(`فشل رفع الصورة ${i+1}: ${err.message}`, 'error');
1520
+ if (item) {
1521
+ item.style.opacity = '0.5';
1522
+ item.style.border = '2px solid var(--red)';
1523
+ }
1524
+ }
1525
+ }
1526
+
1527
+ if (uploadedImageUrls.length > 0) {
1528
+ showToast(`تم رفع ${uploadedImageUrls.length} صورة بنجاح`, 'success');
1529
+ }
1530
+ }
1531
+ function readFileAsBlob(file) {
1532
+ return new Promise((resolve, reject) => {
1533
+ const reader = new FileReader();
1534
+ reader.onload = (e) => {
1535
+ const arr = new Uint8Array(e.target.result);
1536
+ const blob = new Blob([arr], { type: file.type || 'image/jpeg' });
1537
+ resolve(blob);
1538
+ };
1539
+ reader.onerror = () => reject(new Error('فشل قراءة الملف'));
1540
+ reader.readAsArrayBuffer(file);
1541
+ });
1542
+ }
1543
+ function removeImage(index) {
1544
+ selectedImages = selectedImages.filter((_,i) => i !== index);
1545
+ uploadedImageUrls = uploadedImageUrls.filter((_,i) => i !== index);
1546
+ if (!selectedImages.length) {
1547
+ document.getElementById('imgPrevContainer').innerHTML = '';
1548
+ document.getElementById('imageInput').value = '';
1549
+ } else {
1550
+ renderImgPreviews(selectedImages);
1551
+ uploadAllImages();
1552
+ }
1553
+ }
1554
+
1555
+ function clearImgPreviews() {
1556
+ selectedImages = [];
1557
+ uploadedImageUrls = [];
1558
+ document.getElementById('imgPrevContainer').innerHTML = '';
1559
+ document.getElementById('imageInput').value = '';
1560
+ }
1561
+
1562
+ /* ── FILE HANDLING ── */
1563
+ function handleFileSelect(e) {
1564
+ const file = e.target.files[0];
1565
+ if (!file) return;
1566
+ selectedFile = file;
1567
+ const prev = document.getElementById('filePrev');
1568
+ prev.innerHTML = '';
1569
+ const inner = document.createElement('div');
1570
+ inner.className = 'file-prev-inner';
1571
+ inner.innerHTML = `<i data-lucide="file-text"></i><span>${escHtml(file.name)} (${(file.size/1024).toFixed(1)} KB)</span>`;
1572
+ const rm = document.createElement('button');
1573
+ rm.className = 'file-rm';
1574
+ rm.innerHTML = '<i data-lucide="x"></i>';
1575
+ rm.onclick = removeFile;
1576
+ inner.appendChild(rm);
1577
+ prev.appendChild(inner);
1578
+ ri();
1579
+ }
1580
+
1581
+ function removeFile() {
1582
+ selectedFile = null;
1583
+ document.getElementById('filePrev').innerHTML = '';
1584
+ document.getElementById('fileInput').value = '';
1585
+ }
1586
+
1587
+ /* ── FORMAT MESSAGE ── */
1588
+ function formatMsg(text) {
1589
+ if (!text) return '';
1590
+ let f = text;
1591
+ f = f.replace(/```(\w*)\n?([\s\S]*?)```/g, (m, lang, code) => {
1592
+ const l = lang || 'code';
1593
+ return `<pre><div class="code-header"><span class="code-lang">${l}</span><button class="copy-btn" onclick="copyCode(this)"><i data-lucide="copy"></i> نسخ</button></div><code>${escHtml(code.trim())}</code></pre>`;
1594
+ });
1595
+ f = f.replace(/`([^`]+)`/g, '<code>$1</code>');
1596
+ f = f.replace(/^#### (.+)$/gm, '<h4>$1</h4>');
1597
+ f = f.replace(/^### (.+)$/gm, '<h3>$1</h3>');
1598
+ f = f.replace(/^## (.+)$/gm, '<h2>$1</h2>');
1599
+ f = f.replace(/^# (.+)$/gm, '<h1>$1</h1>');
1600
+ f = f.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
1601
+ f = f.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g, '<em>$1</em>');
1602
+ f = f.replace(/~~([^~]+)~~/g, '<del>$1</del>');
1603
+ f = f.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>');
1604
+ f = f.replace(/<\/blockquote>\n?<blockquote>/g, '<br>');
1605
+ f = f.replace(/^---$/gm, '<hr>');
1606
+ f = f.replace(/\b([PBCU][0-9]{4})\b/g, '<span class="dtc-badge"><span class="dtc-led"></span>$1</span>');
1607
+ f = f.replace(/^[\-\*] (.+)$/gm, '<li>$1</li>');
1608
+ f = f.replace(/((?:<li>.*<\/li>\n?)+)/g, m => '<ul>' + m + '</ul>');
1609
+ f = f.replace(/^\d+\. (.+)$/gm, '<li>$1</li>');
1610
+ f = f.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
1611
+ f = f.replace(/(?<!href="|">)(https?:\/\/[^\s<]+)/g, '<a href="$1" target="_blank" rel="noopener">$1</a>');
1612
+ f = f.replace(/\n\n/g, '</p><p>');
1613
+ f = f.replace(/\n/g, '<br>');
1614
+ f = f.replace(/<\/(h[1-6]|pre|ul|ol|blockquote|hr)><br>/g, '</$1>');
1615
+ f = f.replace(/<br><(h[1-6]|pre|ul|ol|blockquote|hr)/g, '<$1');
1616
+ f = f.replace(/<p><\/p>/g, '');
1617
+ if (!/^<(h[1-6]|pre|ul|ol|blockquote|hr|p|div)/.test(f.trim())) f = '<p>' + f + '</p>';
1618
+ return f;
1619
+ }
1620
+
1621
+ function copyCode(btn) {
1622
+ const code = btn.closest('pre').querySelector('code');
1623
+ if (!code) return;
1624
+ navigator.clipboard.writeText(code.textContent).then(() => {
1625
+ btn.innerHTML = '<i data-lucide="check"></i> تم';
1626
+ ri();
1627
+ setTimeout(() => { btn.innerHTML = '<i data-lucide="copy"></i> نسخ'; ri(); }, 2000);
1628
+ });
1629
+ }
1630
+
1631
+ /* ── SEND MESSAGE ── */
1632
+ async function sendMessage() {
1633
+ if (isSending) return;
1634
+ const message = msgInput.value.trim();
1635
+ if (!message && !uploadedImageUrls.length && !selectedFile) return;
1636
+ isSending = true;
1637
+
1638
+ const welcome = document.getElementById('welcomeScreen');
1639
+ if (welcome) welcome.remove();
1640
+
1641
+ setSendLoading(true);
1642
+ msgInput.disabled = true;
1643
+
1644
+ if (message || uploadedImageUrls.length > 0) {
1645
+ addMessage('user', message, null, null, uploadedImageUrls);
1646
+ }
1647
+
1648
+ msgInput.value = '';
1649
+ msgInput.style.height = 'auto';
1650
+
1651
+ const typingId = showTyping();
1652
+
1653
+ try {
1654
+ let data;
1655
+ if (selectedFile) {
1656
+ data = await uploadFile(selectedFile, message);
1657
+ selectedFile = null;
1658
+ document.getElementById('filePrev').innerHTML = '';
1659
+ } else {
1660
+ const res = await fetch('/api/chat', {
1661
+ method: 'POST',
1662
+ headers: { 'Content-Type': 'application/json' },
1663
+ body: JSON.stringify({
1664
+ message: message || 'ما هذا في الصورة؟ حلل المشكلة.',
1665
+ session_id: sessionId,
1666
+ image_urls: uploadedImageUrls
1667
+ })
1668
+ });
1669
+ data = await res.json();
1670
+ }
1671
+
1672
+ removeTyping(typingId);
1673
+
1674
+ if (data && data.status === 'success') {
1675
+ addMessage('assistant', data.message, data.web_searches, data.image_searches);
1676
+ } else if (data && data.error) {
1677
+ throw new Error(data.error);
1678
+ }
1679
+
1680
+ clearImgPreviews();
1681
+
1682
+ } catch (err) {
1683
+ removeTyping(typingId);
1684
+ addMessage('assistant', `**خطأ في الاتصال**\n\n${err.message}\n\nيرجى التحقق من الاتصال والمحاولة مرة أخرى.`);
1685
+ } finally {
1686
+ setSendLoading(false);
1687
+ msgInput.disabled = false;
1688
+ msgInput.focus();
1689
+ isSending = false;
1690
+ }
1691
+ }
1692
+
1693
+ async function uploadFile(file, comment) {
1694
+ const fd = new FormData();
1695
+ fd.append('file', file);
1696
+ fd.append('comment', comment);
1697
+ fd.append('session_id', sessionId);
1698
+ const r = await fetch('/api/upload', { method: 'POST', body: fd });
1699
+ const d = await r.json();
1700
+ if (d.status === 'success') showToast(`تم رفع الملف "${d.filename}" بنجاح`, 'success');
1701
+ return d;
1702
+ }
1703
+
1704
+ /* ── ADD MESSAGE ── */
1705
+ function addMessage(role, content, webSearches, imageSearches, userImages) {
1706
+ const container = document.getElementById('messages');
1707
+
1708
+ const msg = document.createElement('div');
1709
+ msg.className = `msg ${role}`;
1710
+
1711
+ /* Avatar */
1712
+ const av = document.createElement('div');
1713
+ av.className = 'msg-av';
1714
+ if (role === 'user') {
1715
+ av.innerHTML = '<i data-lucide="user"></i>';
1716
+ } else {
1717
+ const img = document.createElement('img');
1718
+ img.src = "{{ url_for('static', filename='logo.png') }}";
1719
+ img.alt = 'AI';
1720
+ img.onerror = function() {
1721
+ this.parentElement.innerHTML = '<i data-lucide="key" style="color:var(--accent);width:17px;height:17px;"></i>';
1722
+ ri();
1723
+ };
1724
+ av.appendChild(img);
1725
+ }
1726
+
1727
+ /* Body */
1728
+ const body = document.createElement('div');
1729
+ body.className = 'msg-body';
1730
+
1731
+ const bubble = document.createElement('div');
1732
+ bubble.className = 'msg-bubble';
1733
+
1734
+ /* User images */
1735
+ if (userImages && userImages.length > 0) {
1736
+ const imgDiv = document.createElement('div');
1737
+ imgDiv.className = 'msg-images';
1738
+ userImages.forEach(url => {
1739
+ const im = document.createElement('img');
1740
+ im.src = url;
1741
+ im.alt = 'صورة مرفقة';
1742
+ im.onclick = () => openModal(url);
1743
+ imgDiv.appendChild(im);
1744
+ });
1745
+ bubble.appendChild(imgDiv);
1746
+ }
1747
+
1748
+ /* Text content */
1749
+ if (content) {
1750
+ const textDiv = document.createElement('div');
1751
+ textDiv.innerHTML = formatMsg(content);
1752
+ bubble.appendChild(textDiv);
1753
+ }
1754
+
1755
+ /* Web results */
1756
+ if (webSearches && webSearches.length > 0) {
1757
+ webSearches.forEach(s => bubble.appendChild(buildWebResults(s)));
1758
+ }
1759
+
1760
+ /* Image results */
1761
+ if (imageSearches && imageSearches.length > 0) {
1762
+ imageSearches.forEach(s => bubble.appendChild(buildImgResults(s)));
1763
+ }
1764
+
1765
+ body.appendChild(bubble);
1766
+
1767
+ /* Time */
1768
+ const time = document.createElement('div');
1769
+ time.className = 'msg-time';
1770
+ time.textContent = new Date().toLocaleTimeString('ar-SA', { hour: '2-digit', minute: '2-digit' });
1771
+ body.appendChild(time);
1772
+
1773
+ msg.appendChild(av);
1774
+ msg.appendChild(body);
1775
+ container.appendChild(msg);
1776
+ container.scrollTop = container.scrollHeight;
1777
+ ri();
1778
+ }
1779
+
1780
+ /* ── WEB RESULTS ── */
1781
+ function buildWebResults(search) {
1782
+ const d = document.createElement('div');
1783
+ d.className = 'web-results';
1784
+ let h = `<h4><i data-lucide="search"></i> بحث: ${escHtml(search.query)}</h4>`;
1785
+ if (search.results && search.results.length > 0) {
1786
+ search.results.forEach((r, i) => {
1787
+ h += `<div class="web-result-item">
1788
+ <div class="web-result-title">${i+1}. ${escHtml(r.name)}</div>
1789
+ <div class="web-result-snippet">${escHtml(r.snippet)}</div>
1790
+ <a href="${escHtml(r.url)}" target="_blank" rel="noopener" class="web-result-url">
1791
+ <i data-lucide="external-link"></i> ${escHtml(r.url)}
1792
+ </a>
1793
+ </div>`;
1794
+ });
1795
+ }
1796
+ d.innerHTML = h;
1797
+ return d;
1798
+ }
1799
+
1800
+ /* ── IMAGE RESULTS ── */
1801
+ function buildImgResults(search) {
1802
+ const section = document.createElement('div');
1803
+ section.className = 'img-results';
1804
+
1805
+ const hdr = document.createElement('div');
1806
+ hdr.className = 'img-results-header';
1807
+ hdr.innerHTML = `<i data-lucide="image"></i> صور: ${escHtml(search.query)}`;
1808
+ section.appendChild(hdr);
1809
+
1810
+ const grid = document.createElement('div');
1811
+ grid.className = 'img-grid';
1812
+
1813
+ const images = search.images || [];
1814
+ if (!images.length) {
1815
+ const p = document.createElement('p');
1816
+ p.style.cssText = 'color:var(--muted);font-size:11px;grid-column:1/-1;text-align:center;padding:16px;';
1817
+ p.textContent = 'لم يتم العثور على صور';
1818
+ grid.appendChild(p);
1819
+ section.appendChild(grid);
1820
+ return section;
1821
+ }
1822
+
1823
+ images.forEach(item => {
1824
+ let displayUrl = '', originalUrl = '';
1825
+ if (typeof item === 'object' && item !== null) {
1826
+ displayUrl = item.proxy_url || item.contentUrl || item.thumbnailUrl || item.url || item.src || '';
1827
+ originalUrl = item.original_url || item.contentUrl || item.url || displayUrl;
1828
+ } else if (typeof item === 'string') {
1829
+ displayUrl = originalUrl = item;
1830
+ }
1831
+ if (!displayUrl) return;
1832
+
1833
+ const card = document.createElement('div');
1834
+ card.className = 'img-card';
1835
+
1836
+ const img = document.createElement('img');
1837
+ img.alt = 'صورة تشخيصية';
1838
+ img.loading = 'lazy';
1839
+ img.onload = () => card.classList.add('loaded');
1840
+ img.onerror = function() {
1841
+ if (!this.dataset.retried && originalUrl && this.src !== originalUrl) {
1842
+ this.dataset.retried = 'true';
1843
+ this.src = originalUrl;
1844
+ return;
1845
+ }
1846
+ card.classList.add('errored', 'loaded');
1847
+ };
1848
+ img.src = displayUrl;
1849
+ card.appendChild(img);
1850
+
1851
+ const overlay = document.createElement('div');
1852
+ overlay.className = 'img-card-overlay';
1853
+ overlay.innerHTML = '<i data-lucide="maximize-2"></i> تكبير';
1854
+ card.appendChild(overlay);
1855
+
1856
+ const err = document.createElement('div');
1857
+ err.className = 'img-err';
1858
+ err.innerHTML = '<i data-lucide="image-off"></i><span>تعذر التحميل</span>';
1859
+ card.appendChild(err);
1860
+
1861
+ card.addEventListener('click', () => {
1862
+ if (!card.classList.contains('errored')) openModal(originalUrl || displayUrl);
1863
+ });
1864
+
1865
+ grid.appendChild(card);
1866
+ });
1867
+
1868
+ section.appendChild(grid);
1869
+ return section;
1870
+ }
1871
+
1872
+ /* ── TYPING INDICATOR ── */
1873
+ function showTyping() {
1874
+ const container = document.getElementById('messages');
1875
+ const id = 'typing_' + Date.now();
1876
+ const wrap = document.createElement('div');
1877
+ wrap.className = 'typing-wrap';
1878
+ wrap.id = id;
1879
+
1880
+ const av = document.createElement('div');
1881
+ av.className = 'typing-av';
1882
+ const img = document.createElement('img');
1883
+ img.src = "{{ url_for('static', filename='logo.png') }}";
1884
+ img.onerror = function() {
1885
+ this.parentElement.innerHTML = '<i data-lucide="key" style="color:var(--accent);width:17px;height:17px;"></i>';
1886
+ ri();
1887
+ };
1888
+ av.appendChild(img);
1889
+
1890
+ const dots = document.createElement('div');
1891
+ dots.className = 'typing-dots';
1892
+ dots.innerHTML = '<span></span><span></span><span></span>';
1893
+
1894
+ wrap.appendChild(av);
1895
+ wrap.appendChild(dots);
1896
+ container.appendChild(wrap);
1897
+ container.scrollTop = container.scrollHeight;
1898
+ ri();
1899
+ return id;
1900
+ }
1901
+
1902
+ function removeTyping(id) {
1903
+ const el = document.getElementById(id);
1904
+ if (el) el.remove();
1905
+ }
1906
+
1907
+ /* ── SEND BUTTON STATE ── */
1908
+ function setSendLoading(v) {
1909
+ const btn = document.getElementById('sendBtn');
1910
+ if (v) {
1911
+ btn.innerHTML = '<div class="spinner"></div>';
1912
+ btn.disabled = true;
1913
+ } else {
1914
+ btn.innerHTML = '<i data-lucide="send"></i>';
1915
+ btn.disabled = false;
1916
+ ri();
1917
+ }
1918
+ }
1919
+
1920
+ /* ── TOAST ── */
1921
+ function showToast(message, type) {
1922
+ const container = document.getElementById('messages');
1923
+ const t = document.createElement('div');
1924
+ t.className = `toast ${type}`;
1925
+ t.innerHTML = `<i data-lucide="${type === 'success' ? 'check-circle' : 'alert-circle'}"></i><span>${escHtml(message)}</span>`;
1926
+ container.appendChild(t);
1927
+ container.scrollTop = container.scrollHeight;
1928
+ ri();
1929
+ setTimeout(() => {
1930
+ t.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
1931
+ t.style.opacity = '0';
1932
+ t.style.transform = 'translateY(-8px)';
1933
+ setTimeout(() => t.remove(), 300);
1934
+ }, 3000);
1935
+ }
1936
+
1937
+ /* ── CLEAR CHAT ── */
1938
+ async function clearChat() {
1939
+ if (!confirm('هل تريد مسح سجل التشخيص بالكامل؟')) return;
1940
+ try {
1941
+ await fetch('/api/clear', {
1942
+ method: 'POST',
1943
+ headers: { 'Content-Type': 'application/json' },
1944
+ body: JSON.stringify({ session_id: sessionId })
1945
+ });
1946
+ } catch(e) {}
1947
+
1948
+ const container = document.getElementById('messages');
1949
+ container.innerHTML = '';
1950
+
1951
+ const welcome = document.createElement('div');
1952
+ welcome.className = 'welcome';
1953
+ welcome.id = 'welcomeScreen';
1954
+ welcome.innerHTML = `
1955
+ <img src="{{ url_for('static', filename='logo.png') }}" alt="SHKIKEL" class="welcome-logo">
1956
+ <h1>مساعد التشخيص الذكي</h1>
1957
+ <p>أدخل رمز العطل OBD-II أو صوّر أعراض سيارتك. سأشخّص المشكلة وأوضح السبب وأريك موقع القطعة بالصور.</p>
1958
+ <div class="ready-tag">
1959
+ <div class="s-dot"></div>
1960
+ READY FOR DIAGNOSTICS
1961
+ </div>`;
1962
+ container.appendChild(welcome);
1963
+
1964
+ clearImgPreviews();
1965
+ removeFile();
1966
+ closeSidebar();
1967
+ showToast('تم مسح سجل التشخيص', 'success');
1968
+ ri();
1969
+ }
1970
+
1971
+ /* ── EXPORT CHAT ── */
1972
+ function exportChat() {
1973
+ const msgs = document.querySelectorAll('.msg');
1974
+ if (!msgs.length) { showToast('لا توجد رسائل للتصدير', 'error'); return; }
1975
+
1976
+ let text = '═══════════════════════════════════════════\n';
1977
+ text += ' SHKIKEL GROUP - سجل التشخيص\n';
1978
+ text += ' ' + new Date().toLocaleString('ar-SA') + '\n';
1979
+ text += '═══════════════════════════════════════════\n\n';
1980
+
1981
+ msgs.forEach(m => {
1982
+ const role = m.classList.contains('user') ? 'المستخدم' : 'المساعد';
1983
+ const bubble = m.querySelector('.msg-bubble');
1984
+ if (!bubble) return;
1985
+ const clone = bubble.cloneNode(true);
1986
+ clone.querySelectorAll('.msg-images,.web-results,.img-results').forEach(el => el.remove());
1987
+ text += `[${role}]\n${clone.textContent.trim()}\n\n${'─'.repeat(43)}\n\n`;
1988
+ });
1989
+
1990
+ const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
1991
+ const url = URL.createObjectURL(blob);
1992
+ const a = document.createElement('a');
1993
+ a.href = url;
1994
+ a.download = `diagnostic_${new Date().toISOString().slice(0,10)}.txt`;
1995
+ a.click();
1996
+ URL.revokeObjectURL(url);
1997
+ closeSidebar();
1998
+ showToast('تم تصدير سجل التشخيص', 'success');
1999
+ }
2000
+
2001
+ /* ── IMAGE MODAL ── */
2002
+ function openModal(url) {
2003
+ const modal = document.getElementById('imgModal');
2004
+ const img = document.getElementById('modalImg');
2005
+ img.src = '';
2006
+ img.removeAttribute('data-retried');
2007
+ img.onerror = function() {
2008
+ if (!this.dataset.retried) {
2009
+ this.dataset.retried = 'true';
2010
+ if (!url.startsWith('/api/proxy-image')) {
2011
+ this.src = '/api/proxy-image?url=' + encodeURIComponent(url);
2012
+ }
2013
+ }
2014
+ };
2015
+ img.src = url;
2016
+ modal.classList.add('active');
2017
+ }
2018
+
2019
+ function closeModal() {
2020
+ document.getElementById('imgModal').classList.remove('active');
2021
+ }
2022
+
2023
+ document.addEventListener('keydown', e => {
2024
+ if (e.key === 'Escape') closeModal();
2025
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') { e.preventDefault(); msgInput.focus(); }
2026
+ if ((e.ctrlKey || e.metaKey) && e.key === 'l') { e.preventDefault(); clearChat(); }
2027
+ });
2028
+
2029
+ /* ── SPEECH RECOGNITION ── */
2030
+ function initSpeech() {
2031
+ if (!('webkitSpeechRecognition' in window || 'SpeechRecognition' in window)) return;
2032
+ const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
2033
+ const rec = new SR();
2034
+ rec.continuous = false;
2035
+ rec.interimResults = false;
2036
+ rec.lang = 'ar-SA';
2037
+ let listening = false;
2038
+
2039
+ const micBtn = document.getElementById('micBtn');
2040
+
2041
+ micBtn.onclick = (e) => {
2042
+ e.preventDefault();
2043
+ if (listening) { rec.stop(); return; }
2044
+ rec.start();
2045
+ listening = true;
2046
+ micBtn.classList.add('active');
2047
+ micBtn.innerHTML = '<i data-lucide="mic-off"></i>';
2048
+ ri();
2049
+ showToast('جاري الاستماع...', 'success');
2050
+ };
2051
+
2052
+ rec.onresult = e => {
2053
+ msgInput.value = e.results[0][0].transcript;
2054
+ msgInput.dispatchEvent(new Event('input'));
2055
+ showToast('تم استقبال الإدخال الصوتي', 'success');
2056
+ };
2057
+
2058
+ rec.onerror = () => showToast('فشل الإدخال الصوتي', 'error');
2059
+
2060
+ rec.onend = () => {
2061
+ listening = false;
2062
+ micBtn.classList.remove('active');
2063
+ micBtn.innerHTML = '<i data-lucide="mic"></i>';
2064
+ ri();
2065
+ };
2066
+ }
2067
+
2068
+ /* ── HEALTH CHECK ── */
2069
+ async function checkHealth() {
2070
+ try {
2071
+ const r = await fetch('/api/health');
2072
+ const d = await r.json();
2073
+ console.log('%c[DIAG] API OK', 'color:#22c55e;font-weight:bold;', d);
2074
+ } catch(e) {
2075
+ const dot = document.getElementById('sDot');
2076
+ const label = document.getElementById('sLabel');
2077
+ const pill = document.getElementById('statusPill');
2078
+ if (dot) dot.style.background = 'var(--red)';
2079
+ if (label) label.textContent = 'OFFLINE';
2080
+ if (pill) {
2081
+ pill.style.background = 'rgba(239,68,68,0.08)';
2082
+ pill.style.borderColor = 'rgba(239,68,68,0.18)';
2083
+ pill.style.color = 'var(--red)';
2084
+ }
2085
+ }
2086
+ }
2087
+
2088
+ /* ── ONLINE / OFFLINE ── */
2089
+ window.addEventListener('online', () => {
2090
+ showToast('تم استعادة الاتصال', 'success');
2091
+ const dot = document.getElementById('sDot');
2092
+ const label = document.getElementById('sLabel');
2093
+ const pill = document.getElementById('statusPill');
2094
+ if (dot) dot.style.background = 'var(--green)';
2095
+ if (label) label.textContent = 'ONLINE';
2096
+ if (pill) {
2097
+ pill.style.background = 'rgba(34,197,94,0.08)';
2098
+ pill.style.borderColor = 'rgba(34,197,94,0.18)';
2099
+ pill.style.color = 'var(--green)';
2100
+ }
2101
+ });
2102
+
2103
+ window.addEventListener('offline', () => {
2104
+ showToast('لا يوجد اتصال بالإنترنت', 'error');
2105
+ const dot = document.getElementById('sDot');
2106
+ const label = document.getElementById('sLabel');
2107
+ const pill = document.getElementById('statusPill');
2108
+ if (dot) dot.style.background = 'var(--red)';
2109
+ if (label) label.textContent = 'OFFLINE';
2110
+ if (pill) {
2111
+ pill.style.background = 'rgba(239,68,68,0.08)';
2112
+ pill.style.borderColor = 'rgba(239,68,68,0.18)';
2113
+ pill.style.color = 'var(--red)';
2114
+ }
2115
+ });
2116
+
2117
+ /* ── PASTE IMAGE ── */
2118
+ msgInput.addEventListener('paste', e => {
2119
+ for (let i = 0; i < e.clipboardData.items.length; i++) {
2120
+ if (e.clipboardData.items[i].type.indexOf('image') !== -1) {
2121
+ e.preventDefault();
2122
+ const blob = e.clipboardData.items[i].getAsFile();
2123
+ const file = new File([blob], `pasted-${Date.now()}.png`, { type: blob.type });
2124
+ selectedImages = [file];
2125
+ renderImgPreviews([file]);
2126
+ uploadAllImages();
2127
+ showToast('تم لصق الصورة - جاري الرفع...', 'success');
2128
+ break;
2129
+ }
2130
+ }
2131
+ });
2132
+
2133
+ /* ── AUTO SCROLL ── */
2134
+ new MutationObserver(() => {
2135
+ const c = document.getElementById('messages');
2136
+ c.scrollTop = c.scrollHeight;
2137
+ }).observe(document.getElementById('messages'), { childList: true, subtree: true });
2138
+
2139
+ /* ── FOCUS ON VISIBILITY ── */
2140
+ document.addEventListener('visibilitychange', () => {
2141
+ if (!document.hidden) msgInput.focus();
2142
+ });
2143
+ </script>
2144
+ </body>
2145
+ </html>