protocol-jarvis commited on
Commit
f785ea6
·
verified ·
1 Parent(s): fbc5f09

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +838 -399
index.html CHANGED
@@ -1,4 +1,5 @@
1
  <!DOCTYPE html>
 
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
@@ -7,406 +8,844 @@
7
  <style>
8
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&family=Space+Grotesk:wght@400;600&display=swap');
9
 
10
- :root {
11
- --accent: #ff8c00;
12
- --accent-dim: rgba(255,140,0,0.15);
13
- --accent-glow: rgba(255,140,0,0.5);
14
- --bg: #09090e;
15
- --surface: rgba(255,255,255,0.03);
16
- --border: rgba(255,140,0,0.12);
17
- --text: #d8d8d8;
18
- --muted: rgba(255,255,255,0.25);
19
- }
20
-
21
- * { margin: 0; padding: 0; box-sizing: border-box; }
22
-
23
- body {
24
- background: var(--bg);
25
- font-family: 'JetBrains Mono', monospace;
26
- color: var(--text);
27
- min-height: 100vh;
28
- overflow-x: hidden;
29
- }
30
-
31
- .grid-bg {
32
- position: fixed;
33
- inset: 0;
34
- background-image:
35
- linear-gradient(rgba(255,140,0,0.025) 1px, transparent 1px),
36
- linear-gradient(90deg, rgba(255,140,0,0.025) 1px, transparent 1px);
37
- background-size: 48px 48px;
38
- pointer-events: none;
39
- z-index: 0;
40
- }
41
- .orb {
42
- position: fixed;
43
- border-radius: 50%;
44
- pointer-events: none;
45
- z-index: 0;
46
- background: radial-gradient(circle, rgba(255,140,0,0.07) 0%, transparent 70%);
47
- }
48
- .orb1 { top: 10%; left: 5%; width: 500px; height: 500px; }
49
- .orb2 { bottom: 10%; right: 5%; width: 400px; height: 400px; }
50
-
51
- .shell {
52
- position: relative;
53
- z-index: 1;
54
- display: flex;
55
- flex-direction: column;
56
- height: 100dvh;
57
- max-width: 920px;
58
- margin: 0 auto;
59
- padding: 0 16px;
60
- }
61
-
62
- .header {
63
- display: flex;
64
- align-items: center;
65
- justify-content: space-between;
66
- padding: 14px 0 12px;
67
- border-bottom: 1px solid var(--border);
68
- flex-shrink: 0;
69
- }
70
- .header-left { display: flex; align-items: center; gap: 12px; }
71
- .logo-icon {
72
- font-size: 26px;
73
- color: var(--accent);
74
- animation: logoPulse 3s ease-in-out infinite;
75
- line-height: 1;
76
- }
77
- @keyframes logoPulse {
78
- 0%,100% { filter: drop-shadow(0 0 6px var(--accent-glow)); }
79
- 50% { filter: drop-shadow(0 0 18px var(--accent-glow)); }
80
- }
81
- .header h1 {
82
- font-size: 17px;
83
- font-weight: 800;
84
- color: var(--accent);
85
- letter-spacing: 6px;
86
- }
87
- .header-right {
88
- display: flex;
89
- align-items: center;
90
- gap: 8px;
91
- }
92
- .stat-pill {
93
- background: var(--accent-dim);
94
- border: 1px solid var(--border);
95
- border-radius: 20px;
96
- color: rgba(255,140,0,0.7);
97
- font-size: 10px;
98
- letter-spacing: 1px;
99
- padding: 4px 10px;
100
- }
101
-
102
- button {
103
- font-family: 'JetBrains Mono', monospace;
104
- cursor: pointer;
105
- border: none;
106
- border-radius: 3px;
107
- transition: all 0.18s;
108
- }
109
- .btn-primary {
110
- background: var(--accent);
111
- color: #0a0a0f;
112
- font-weight: 800;
113
- font-size: 11px;
114
- letter-spacing: 2px;
115
- padding: 10px 22px;
116
- }
117
- .btn-primary:hover:not(:disabled) {
118
- background: #ffaa33;
119
- box-shadow: 0 0 18px var(--accent-glow);
120
- transform: translateY(-1px);
121
- }
122
- .btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
123
-
124
- .btn-ghost {
125
- background: var(--surface);
126
- border: 1px solid rgba(255,255,255,0.07);
127
- color: var(--muted);
128
- font-size: 10px;
129
- letter-spacing: 1.5px;
130
- padding: 8px 14px;
131
- }
132
- .btn-ghost:hover { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.6); }
133
-
134
- .btn-copy {
135
- background: rgba(255,140,0,0.08);
136
- border: 1px solid rgba(255,140,0,0.2);
137
- color: rgba(255,140,0,0.7);
138
- font-size: 9px;
139
- letter-spacing: 1px;
140
- padding: 4px 10px;
141
- }
142
- .btn-copy:hover { background: rgba(255,140,0,0.18); color: var(--accent); }
143
- .btn-copy.copied {
144
- background: rgba(80,220,100,0.1);
145
- border-color: rgba(80,220,100,0.3);
146
- color: rgba(80,220,100,0.9);
147
- }
148
-
149
- .lang-bar {
150
- flex-shrink: 0;
151
- padding: 10px 0;
152
- border-bottom: 1px solid rgba(255,140,0,0.07);
153
- overflow-x: auto;
154
- scrollbar-width: none;
155
- }
156
- .lang-bar::-webkit-scrollbar { display: none; }
157
- .lang-inner {
158
- display: flex;
159
- gap: 6px;
160
- white-space: nowrap;
161
- }
162
- .lang-btn {
163
- background: var(--surface);
164
- border: 1px solid rgba(255,255,255,0.06);
165
- color: rgba(255,255,255,0.35);
166
- font-size: 10px;
167
- font-family: 'JetBrains Mono', monospace;
168
- letter-spacing: 0.5px;
169
- padding: 5px 13px;
170
- border-radius: 2px;
171
- transition: all 0.15s;
172
- }
173
- .lang-btn:hover { color: rgba(255,255,255,0.6); background: rgba(255,255,255,0.06); }
174
- .lang-btn.active {
175
- background: var(--accent-dim);
176
- border-color: rgba(255,140,0,0.35);
177
- color: var(--accent);
178
- font-weight: 700;
179
- }
180
-
181
- .examples {
182
- flex-shrink: 0;
183
- padding: 10px 0;
184
- overflow-x: auto;
185
- scrollbar-width: none;
186
- }
187
- .examples::-webkit-scrollbar { display: none; }
188
- .examples-inner {
189
- display: flex;
190
- gap: 7px;
191
- white-space: nowrap;
192
- }
193
- .ex-btn {
194
- background: rgba(255,140,0,0.04);
195
- border: 1px solid rgba(255,140,0,0.13);
196
- color: rgba(255,140,0,0.55);
197
- font-size: 10px;
198
- font-family: 'JetBrains Mono', monospace;
199
- padding: 6px 13px;
200
- border-radius: 2px;
201
- transition: all 0.15s;
202
- }
203
- .ex-btn:hover { background: rgba(255,140,0,0.1); color: var(--accent); }
204
-
205
- .chat-area {
206
- flex: 1;
207
- overflow-y: auto;
208
- padding: 16px 4px;
209
- scroll-behavior: smooth;
210
- }
211
- .empty-state {
212
- display: flex;
213
- flex-direction: column;
214
- align-items: center;
215
- justify-content: center;
216
- height: 100%;
217
- gap: 12px;
218
- color: rgba(255,255,255,0.1);
219
- font-size: 12px;
220
- letter-spacing: 2px;
221
- text-align: center;
222
- }
223
- .empty-state .big { font-size: 40px; opacity: 0.15; }
224
-
225
- .message {
226
- display: flex;
227
- gap: 10px;
228
- margin-bottom: 16px;
229
- animation: msgIn 0.28s ease forwards;
230
- }
231
- @keyframes msgIn {
232
- from { opacity: 0; transform: translateY(12px); }
233
- to { opacity: 1; transform: translateY(0); }
234
- }
235
- .message.user { flex-direction: row-reverse; }
236
-
237
- .avatar {
238
- flex-shrink: 0;
239
- width: 30px;
240
- height: 30px;
241
- border-radius: 3px;
242
- display: flex;
243
- align-items: center;
244
- justify-content: center;
245
- font-size: 13px;
246
- font-weight: 700;
247
- }
248
- .message.user .avatar {
249
- background: rgba(255,140,0,0.12);
250
- border: 1px solid rgba(255,140,0,0.25);
251
- color: var(--accent);
252
- }
253
- .message.assistant .avatar {
254
- background: rgba(255,255,255,0.04);
255
- border: 1px solid rgba(255,255,255,0.08);
256
- color: rgba(255,255,255,0.4);
257
- }
258
-
259
- .bubble {
260
- max-width: 82%;
261
- padding: 12px 14px;
262
- border-radius: 4px;
263
- font-size: 13px;
264
- line-height: 1.65;
265
- word-break: break-word;
266
- }
267
- .message.user .bubble {
268
- background: rgba(255,140,0,0.08);
269
- border: 1px solid rgba(255,140,0,0.18);
270
- color: #ffb347;
271
- border-radius: 6px 2px 6px 6px;
272
- }
273
- .message.assistant .bubble {
274
- background: rgba(255,255,255,0.025);
275
- border: 1px solid rgba(255,255,255,0.06);
276
- color: #ccc;
277
- border-radius: 2px 6px 6px 6px;
278
- }
279
-
280
- .code-block {
281
- margin: 12px 0;
282
- border-radius: 4px;
283
- overflow: hidden;
284
- border: 1px solid rgba(255,140,0,0.14);
285
- }
286
- .code-header {
287
- display: flex;
288
- justify-content: space-between;
289
- align-items: center;
290
- background: rgba(0,0,0,0.5);
291
- padding: 7px 12px;
292
- border-bottom: 1px solid rgba(255,255,255,0.05);
293
- }
294
- .code-lang {
295
- font-size: 10px;
296
- font-weight: 700;
297
- color: var(--accent);
298
- letter-spacing: 2px;
299
- text-transform: uppercase;
300
- }
301
- .code-block pre {
302
- background: rgba(0,0,0,0.55);
303
- padding: 14px 16px;
304
- overflow-x: auto;
305
- margin: 0;
306
- }
307
- .code-block pre code {
308
- color: #d4d4d4;
309
- font-size: 12.5px;
310
- line-height: 1.65;
311
- }
312
-
313
- .typing-wrap { display: flex; align-items: center; gap: 5px; padding: 4px 0; }
314
- .dot { width: 6px; height: 6px; background: var(--accent); border-radius: 50%; animation: dotPop 1.1s ease-in-out infinite; }
315
- .dot:nth-child(2) { animation-delay: 0.18s; }
316
- .dot:nth-child(3) { animation-delay: 0.36s; }
317
- @keyframes dotPop { 0%,100% { opacity: 0.2; transform: scale(0.75); } 50% { opacity: 1; transform: scale(1.25); } }
318
-
319
- .input-area { flex-shrink: 0; padding: 12px 0 16px; border-top: 1px solid rgba(255,140,0,0.09); }
320
- textarea {
321
- width: 100%; padding: 12px 14px; background: rgba(255,255,255,0.035); border: 1px solid rgba(255,140,0,0.15);
322
- border-radius: 4px; color: #e0e0e0; font-family: 'JetBrains Mono', monospace; font-size: 13px;
323
- line-height: 1.6; resize: none; outline: none; min-height: 60px; max-height: 160px;
324
- }
325
-
326
- .gate { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 100dvh; z-index: 1; }
327
- .gate-card { background: rgba(255,140,0,0.03); border: 1px solid var(--border); border-radius: 6px; padding: 40px; max-width: 380px; width: 90%; text-align: center; backdrop-filter: blur(12px); }
328
- .gate-title { font-size: 26px; font-weight: 800; color: var(--accent); letter-spacing: 8px; margin-bottom: 28px; }
329
- .gate-input { width: 100%; padding: 13px 16px; background: rgba(0,0,0,0.55); border: 1px solid rgba(255,140,0,0.2); border-radius: 3px; color: var(--accent); font-family: 'JetBrains Mono', monospace; letter-spacing: 4px; text-align: center; outline: none; margin-bottom: 18px; }
330
- .gate-error { color: #ff6b6b; font-size: 11px; min-height: 16px; margin-top: 10px; }
331
-
332
- @media (max-width: 600px) { .header h1 { font-size: 13px; letter-spacing: 4px; } .bubble { max-width: 92%; } }
333
- </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334
  </head>
335
  <body>
336
- <div class="grid-bg"></div>
337
- <div class="orb orb1"></div>
338
- <div class="orb orb2"></div>
339
- <div id="root"></div>
340
-
341
- <script>
342
- const PASSWORD = "CodeForge#2026";
343
- const LANGUAGES = [
344
- { id: "auto", label: "✦ Auto" },
345
- { id: "javascript", label: "JS" },
346
- { id: "python", label: "Python" },
347
- { id: "html", label: "HTML/CSS" }
348
- ];
349
-
350
- const state = { unlocked: false, messages: [], lang: "auto", count: 0, loading: false };
351
-
352
- function escHtml(s) { return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); }
353
-
354
- function renderMarkdown(text) {
355
- let result = text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
356
- return `<div class="code-block"><div class="code-header"><span class="code-lang">${lang || 'code'}</span><button class="btn-copy" onclick="copyCode(this)">⎘ COPY</button></div><pre><code>${escHtml(code.trim())}</code></pre></div>`;
357
- });
358
- return result.replace(/\n/g, "<br>");
359
- }
360
-
361
- window.copyCode = async (btn) => {
362
- const code = btn.closest(".code-block").querySelector("code").innerText;
363
- await navigator.clipboard.writeText(code);
364
- btn.textContent = "✓ COPIED";
365
- setTimeout(() => btn.textContent = "⎘ COPY", 2000);
366
- };
367
-
368
- function render() {
369
- const root = document.getElementById("root");
370
- if (!state.unlocked) {
371
- root.innerHTML = `<div class="gate"><div class="gate-card"><div class="gate-title">CODEFORGE</div><input class="gate-input" id="pwInput" type="password" placeholder="••••" onkeydown="if(event.key==='Enter') tryUnlock()"><button class="btn-primary" style="width:100%" onclick="tryUnlock()">UNLOCK →</button><div class="gate-error" id="gateErr"></div></div></div>`;
372
- return;
373
- }
374
-
375
- let msgHtml = state.messages.map(m => `
376
- <div class="message ${m.role === 'user' ? 'user' : 'assistant'}">
377
- <div class="avatar">${m.role === 'user' ? 'U' : '⟨/⟩'}</div>
378
- <div class="bubble">${m.role === 'user' ? escHtml(m.content) : renderMarkdown(m.content)}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
379
  </div>
380
- `).join("");
381
-
382
- root.innerHTML = `
383
- <div class="shell">
384
- <div class="header"><h1>CODEFORGE</h1><button class="btn-ghost" onclick="state.unlocked=false;render()">🔒 Lock</button></div>
385
- <div class="chat-area" id="chatArea">${msgHtml || '<div class="empty-state">Ready for input</div>'}</div>
386
- <div class="input-area">
387
- <textarea id="userInput" placeholder="Describe code..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMessage();}"></textarea>
388
- <button class="btn-primary" style="width:100%;margin-top:10px" onclick="sendMessage()">GENERATE</button>
389
- </div>
390
- </div>`;
391
- const chat = document.getElementById("chatArea");
392
- if(chat) chat.scrollTop = chat.scrollHeight;
393
- }
394
-
395
- window.tryUnlock = () => {
396
- if (document.getElementById("pwInput").value === PASSWORD) { state.unlocked = true; render(); }
397
- else { document.getElementById("gateErr").textContent = "Invalid Code"; }
398
- };
399
-
400
- window.sendMessage = () => {
401
- const inp = document.getElementById("userInput");
402
- if (!inp.value.trim()) return;
403
- state.messages.push({ role: "user", content: inp.value });
404
- inp.value = "";
405
- render();
406
- // Mock response logic here
407
- };
408
-
409
- render();
410
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  </body>
412
- </html>
 
1
  <!DOCTYPE html>
2
+
3
  <html lang="en">
4
  <head>
5
  <meta charset="UTF-8">
 
8
  <style>
9
  @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700;800&family=Space+Grotesk:wght@400;600&display=swap');
10
 
11
+ ```
12
+ :root {
13
+ --accent: #ff8c00;
14
+ --accent-dim: rgba(255,140,0,0.15);
15
+ --accent-glow: rgba(255,140,0,0.5);
16
+ --bg: #09090e;
17
+ --surface: rgba(255,255,255,0.03);
18
+ --border: rgba(255,140,0,0.12);
19
+ --text: #d8d8d8;
20
+ --muted: rgba(255,255,255,0.25);
21
+ }
22
+
23
+ * { margin: 0; padding: 0; box-sizing: border-box; }
24
+
25
+ body {
26
+ background: var(--bg);
27
+ font-family: 'JetBrains Mono', monospace;
28
+ color: var(--text);
29
+ min-height: 100vh;
30
+ overflow-x: hidden;
31
+ }
32
+
33
+ /* Grid + orbs */
34
+ .grid-bg {
35
+ position: fixed;
36
+ inset: 0;
37
+ background-image:
38
+ linear-gradient(rgba(255,140,0,0.025) 1px, transparent 1px),
39
+ linear-gradient(90deg, rgba(255,140,0,0.025) 1px, transparent 1px);
40
+ background-size: 48px 48px;
41
+ pointer-events: none;
42
+ z-index: 0;
43
+ }
44
+ .orb {
45
+ position: fixed;
46
+ border-radius: 50%;
47
+ pointer-events: none;
48
+ z-index: 0;
49
+ background: radial-gradient(circle, rgba(255,140,0,0.07) 0%, transparent 70%);
50
+ }
51
+ .orb1 { top: 10%; left: 5%; width: 500px; height: 500px; }
52
+ .orb2 { bottom: 10%; right: 5%; width: 400px; height: 400px; }
53
+
54
+ /* Layout */
55
+ .shell {
56
+ position: relative;
57
+ z-index: 1;
58
+ display: flex;
59
+ flex-direction: column;
60
+ height: 100dvh;
61
+ max-width: 920px;
62
+ margin: 0 auto;
63
+ padding: 0 16px;
64
+ }
65
+
66
+ /* ── Header ── */
67
+ .header {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: space-between;
71
+ padding: 14px 0 12px;
72
+ border-bottom: 1px solid var(--border);
73
+ flex-shrink: 0;
74
+ }
75
+ .header-left { display: flex; align-items: center; gap: 12px; }
76
+ .logo-icon {
77
+ font-size: 26px;
78
+ color: var(--accent);
79
+ animation: logoPulse 3s ease-in-out infinite;
80
+ line-height: 1;
81
+ }
82
+ @keyframes logoPulse {
83
+ 0%,100% { filter: drop-shadow(0 0 6px var(--accent-glow)); }
84
+ 50% { filter: drop-shadow(0 0 18px var(--accent-glow)); }
85
+ }
86
+ .header h1 {
87
+ font-size: 17px;
88
+ font-weight: 800;
89
+ color: var(--accent);
90
+ letter-spacing: 6px;
91
+ }
92
+ .header-right {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 8px;
96
+ }
97
+ .stat-pill {
98
+ background: var(--accent-dim);
99
+ border: 1px solid var(--border);
100
+ border-radius: 20px;
101
+ color: rgba(255,140,0,0.7);
102
+ font-size: 10px;
103
+ letter-spacing: 1px;
104
+ padding: 4px 10px;
105
+ }
106
+
107
+ /* ── Buttons ── */
108
+ button {
109
+ font-family: 'JetBrains Mono', monospace;
110
+ cursor: pointer;
111
+ border: none;
112
+ border-radius: 3px;
113
+ transition: all 0.18s;
114
+ }
115
+ .btn-primary {
116
+ background: var(--accent);
117
+ color: #0a0a0f;
118
+ font-weight: 800;
119
+ font-size: 11px;
120
+ letter-spacing: 2px;
121
+ padding: 10px 22px;
122
+ }
123
+ .btn-primary:hover:not(:disabled) {
124
+ background: #ffaa33;
125
+ box-shadow: 0 0 18px var(--accent-glow);
126
+ transform: translateY(-1px);
127
+ }
128
+ .btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
129
+
130
+ .btn-ghost {
131
+ background: var(--surface);
132
+ border: 1px solid rgba(255,255,255,0.07);
133
+ color: var(--muted);
134
+ font-size: 10px;
135
+ letter-spacing: 1.5px;
136
+ padding: 8px 14px;
137
+ }
138
+ .btn-ghost:hover { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.6); }
139
+
140
+ .btn-copy {
141
+ background: rgba(255,140,0,0.08);
142
+ border: 1px solid rgba(255,140,0,0.2);
143
+ color: rgba(255,140,0,0.7);
144
+ font-size: 9px;
145
+ letter-spacing: 1px;
146
+ padding: 4px 10px;
147
+ }
148
+ .btn-copy:hover { background: rgba(255,140,0,0.18); color: var(--accent); }
149
+ .btn-copy.copied {
150
+ background: rgba(80,220,100,0.1);
151
+ border-color: rgba(80,220,100,0.3);
152
+ color: rgba(80,220,100,0.9);
153
+ }
154
+
155
+ /* ── Language bar ── */
156
+ .lang-bar {
157
+ flex-shrink: 0;
158
+ padding: 10px 0;
159
+ border-bottom: 1px solid rgba(255,140,0,0.07);
160
+ overflow-x: auto;
161
+ -ms-overflow-style: none;
162
+ scrollbar-width: none;
163
+ }
164
+ .lang-bar::-webkit-scrollbar { display: none; }
165
+ .lang-inner {
166
+ display: flex;
167
+ gap: 6px;
168
+ white-space: nowrap;
169
+ }
170
+ .lang-btn {
171
+ background: var(--surface);
172
+ border: 1px solid rgba(255,255,255,0.06);
173
+ color: rgba(255,255,255,0.35);
174
+ font-size: 10px;
175
+ font-family: 'JetBrains Mono', monospace;
176
+ letter-spacing: 0.5px;
177
+ padding: 5px 13px;
178
+ border-radius: 2px;
179
+ transition: all 0.15s;
180
+ }
181
+ .lang-btn:hover { color: rgba(255,255,255,0.6); background: rgba(255,255,255,0.06); }
182
+ .lang-btn.active {
183
+ background: var(--accent-dim);
184
+ border-color: rgba(255,140,0,0.35);
185
+ color: var(--accent);
186
+ font-weight: 700;
187
+ }
188
+
189
+ /* ── Examples ── */
190
+ .examples {
191
+ flex-shrink: 0;
192
+ padding: 10px 0;
193
+ overflow-x: auto;
194
+ -ms-overflow-style: none;
195
+ scrollbar-width: none;
196
+ }
197
+ .examples::-webkit-scrollbar { display: none; }
198
+ .examples-inner {
199
+ display: flex;
200
+ gap: 7px;
201
+ white-space: nowrap;
202
+ }
203
+ .ex-btn {
204
+ background: rgba(255,140,0,0.04);
205
+ border: 1px solid rgba(255,140,0,0.13);
206
+ color: rgba(255,140,0,0.55);
207
+ font-size: 10px;
208
+ font-family: 'JetBrains Mono', monospace;
209
+ padding: 6px 13px;
210
+ border-radius: 2px;
211
+ transition: all 0.15s;
212
+ }
213
+ .ex-btn:hover { background: rgba(255,140,0,0.1); color: var(--accent); }
214
+
215
+ /* ── Chat ── */
216
+ .chat-area {
217
+ flex: 1;
218
+ overflow-y: auto;
219
+ padding: 16px 4px;
220
+ scroll-behavior: smooth;
221
+ }
222
+ .chat-area::-webkit-scrollbar { width: 4px; }
223
+ .chat-area::-webkit-scrollbar-thumb { background: rgba(255,140,0,0.25); border-radius: 4px; }
224
+ .chat-area::-webkit-scrollbar-track { background: transparent; }
225
+
226
+ .empty-state {
227
+ display: flex;
228
+ flex-direction: column;
229
+ align-items: center;
230
+ justify-content: center;
231
+ height: 100%;
232
+ gap: 12px;
233
+ color: rgba(255,255,255,0.1);
234
+ font-size: 12px;
235
+ letter-spacing: 2px;
236
+ text-align: center;
237
+ }
238
+ .empty-state .big { font-size: 40px; opacity: 0.15; }
239
+
240
+ .message {
241
+ display: flex;
242
+ gap: 10px;
243
+ margin-bottom: 16px;
244
+ animation: msgIn 0.28s ease forwards;
245
+ }
246
+ @keyframes msgIn {
247
+ from { opacity: 0; transform: translateY(12px); }
248
+ to { opacity: 1; transform: translateY(0); }
249
+ }
250
+ .message.user { flex-direction: row-reverse; }
251
+
252
+ .avatar {
253
+ flex-shrink: 0;
254
+ width: 30px;
255
+ height: 30px;
256
+ border-radius: 3px;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ font-size: 13px;
261
+ font-weight: 700;
262
+ }
263
+ .message.user .avatar {
264
+ background: rgba(255,140,0,0.12);
265
+ border: 1px solid rgba(255,140,0,0.25);
266
+ color: var(--accent);
267
+ }
268
+ .message.assistant .avatar {
269
+ background: rgba(255,255,255,0.04);
270
+ border: 1px solid rgba(255,255,255,0.08);
271
+ color: rgba(255,255,255,0.4);
272
+ }
273
+
274
+ .bubble {
275
+ max-width: 82%;
276
+ padding: 12px 14px;
277
+ border-radius: 4px;
278
+ font-size: 13px;
279
+ line-height: 1.65;
280
+ word-break: break-word;
281
+ }
282
+ .message.user .bubble {
283
+ background: rgba(255,140,0,0.08);
284
+ border: 1px solid rgba(255,140,0,0.18);
285
+ color: #ffb347;
286
+ border-radius: 6px 2px 6px 6px;
287
+ }
288
+ .message.assistant .bubble {
289
+ background: rgba(255,255,255,0.025);
290
+ border: 1px solid rgba(255,255,255,0.06);
291
+ color: #ccc;
292
+ border-radius: 2px 6px 6px 6px;
293
+ }
294
+
295
+ /* Inline markdown */
296
+ .bubble strong { color: #ffcc80; font-weight: 700; }
297
+ .bubble em { color: #aad4f5; font-style: italic; }
298
+ .bubble code {
299
+ background: rgba(0,0,0,0.5);
300
+ border: 1px solid rgba(255,255,255,0.08);
301
+ border-radius: 2px;
302
+ padding: 1px 5px;
303
+ font-size: 12px;
304
+ color: #ffb347;
305
+ font-family: 'JetBrains Mono', monospace;
306
+ }
307
+
308
+ /* Code blocks */
309
+ .code-block {
310
+ margin: 12px 0;
311
+ border-radius: 4px;
312
+ overflow: hidden;
313
+ border: 1px solid rgba(255,140,0,0.14);
314
+ }
315
+ .code-header {
316
+ display: flex;
317
+ justify-content: space-between;
318
+ align-items: center;
319
+ background: rgba(0,0,0,0.5);
320
+ padding: 7px 12px;
321
+ border-bottom: 1px solid rgba(255,255,255,0.05);
322
+ }
323
+ .code-lang {
324
+ font-size: 10px;
325
+ font-weight: 700;
326
+ color: var(--accent);
327
+ letter-spacing: 2px;
328
+ text-transform: uppercase;
329
+ }
330
+ .code-block pre {
331
+ background: rgba(0,0,0,0.55);
332
+ padding: 14px 16px;
333
+ overflow-x: auto;
334
+ margin: 0;
335
+ }
336
+ .code-block pre code {
337
+ background: none;
338
+ border: none;
339
+ padding: 0;
340
+ color: #d4d4d4;
341
+ font-size: 12.5px;
342
+ line-height: 1.65;
343
+ }
344
+
345
+ /* Typing indicator */
346
+ .typing-wrap {
347
+ display: flex;
348
+ align-items: center;
349
+ gap: 5px;
350
+ padding: 4px 0;
351
+ }
352
+ .dot {
353
+ width: 6px; height: 6px;
354
+ background: var(--accent);
355
+ border-radius: 50%;
356
+ animation: dotPop 1.1s ease-in-out infinite;
357
+ }
358
+ .dot:nth-child(2) { animation-delay: 0.18s; }
359
+ .dot:nth-child(3) { animation-delay: 0.36s; }
360
+ @keyframes dotPop {
361
+ 0%,100% { opacity: 0.2; transform: scale(0.75); }
362
+ 50% { opacity: 1; transform: scale(1.25); }
363
+ }
364
+ .typing-label {
365
+ font-size: 10px;
366
+ color: rgba(255,140,0,0.45);
367
+ letter-spacing: 1px;
368
+ margin-left: 4px;
369
+ }
370
+
371
+ /* ── Input area ── */
372
+ .input-area {
373
+ flex-shrink: 0;
374
+ padding: 12px 0 16px;
375
+ border-top: 1px solid rgba(255,140,0,0.09);
376
+ }
377
+ textarea {
378
+ width: 100%;
379
+ padding: 12px 14px;
380
+ background: rgba(255,255,255,0.035);
381
+ border: 1px solid rgba(255,140,0,0.15);
382
+ border-radius: 4px;
383
+ color: #e0e0e0;
384
+ font-family: 'JetBrains Mono', monospace;
385
+ font-size: 13px;
386
+ line-height: 1.6;
387
+ resize: none;
388
+ outline: none;
389
+ transition: border-color 0.2s, box-shadow 0.2s;
390
+ min-height: 60px;
391
+ max-height: 160px;
392
+ overflow-y: auto;
393
+ }
394
+ textarea:focus {
395
+ border-color: rgba(255,140,0,0.4);
396
+ box-shadow: 0 0 0 3px rgba(255,140,0,0.04);
397
+ }
398
+ .btn-row {
399
+ display: flex;
400
+ gap: 8px;
401
+ margin-top: 9px;
402
+ align-items: center;
403
+ }
404
+ .btn-row .btn-primary { flex: 1; }
405
+ .hint {
406
+ font-size: 9px;
407
+ color: rgba(255,255,255,0.13);
408
+ letter-spacing: 1px;
409
+ text-align: right;
410
+ margin-top: 5px;
411
+ }
412
+
413
+ /* ── Password Gate ── */
414
+ .gate {
415
+ display: flex;
416
+ flex-direction: column;
417
+ align-items: center;
418
+ justify-content: center;
419
+ min-height: 100dvh;
420
+ gap: 0;
421
+ position: relative;
422
+ z-index: 1;
423
+ }
424
+ .gate-card {
425
+ background: rgba(255,140,0,0.03);
426
+ border: 1px solid var(--border);
427
+ border-radius: 6px;
428
+ padding: 40px;
429
+ max-width: 380px;
430
+ width: 90%;
431
+ text-align: center;
432
+ backdrop-filter: blur(12px);
433
+ }
434
+ .gate-icon {
435
+ font-size: 52px;
436
+ color: var(--accent);
437
+ animation: logoPulse 3s ease-in-out infinite;
438
+ margin-bottom: 12px;
439
+ display: block;
440
+ }
441
+ .gate-title {
442
+ font-size: 26px;
443
+ font-weight: 800;
444
+ color: var(--accent);
445
+ letter-spacing: 8px;
446
+ margin-bottom: 4px;
447
+ }
448
+ .gate-sub {
449
+ font-size: 10px;
450
+ color: rgba(255,140,0,0.35);
451
+ letter-spacing: 4px;
452
+ margin-bottom: 28px;
453
+ }
454
+ .gate-label {
455
+ font-size: 10px;
456
+ color: rgba(255,140,0,0.4);
457
+ letter-spacing: 3px;
458
+ margin-bottom: 10px;
459
+ }
460
+ .gate-input {
461
+ width: 100%;
462
+ padding: 13px 16px;
463
+ background: rgba(0,0,0,0.55);
464
+ border: 1px solid rgba(255,140,0,0.2);
465
+ border-radius: 3px;
466
+ color: var(--accent);
467
+ font-family: 'JetBrains Mono', monospace;
468
+ font-size: 14px;
469
+ letter-spacing: 4px;
470
+ text-align: center;
471
+ outline: none;
472
+ transition: all 0.2s;
473
+ margin-bottom: 18px;
474
+ }
475
+ .gate-input:focus {
476
+ border-color: rgba(255,140,0,0.55);
477
+ box-shadow: 0 0 14px rgba(255,140,0,0.1);
478
+ }
479
+ .gate-error {
480
+ color: #ff6b6b;
481
+ font-size: 11px;
482
+ letter-spacing: 1px;
483
+ margin-top: 10px;
484
+ min-height: 16px;
485
+ }
486
+
487
+ /* Scrollbar */
488
+ ::-webkit-scrollbar { width: 4px; height: 4px; }
489
+ ::-webkit-scrollbar-track { background: transparent; }
490
+ ::-webkit-scrollbar-thumb { background: rgba(255,140,0,0.2); border-radius: 4px; }
491
+
492
+ @media (max-width: 600px) {
493
+ .header h1 { font-size: 13px; letter-spacing: 4px; }
494
+ .bubble { max-width: 92%; font-size: 12px; }
495
+ .btn-primary { font-size: 10px; padding: 9px 14px; }
496
+ }
497
+ </style>
498
+ ```
499
+
500
  </head>
501
  <body>
502
+ <div class="grid-bg"></div>
503
+ <div class="orb orb1"></div>
504
+ <div class="orb orb2"></div>
505
+
506
+ <div id="root"></div>
507
+
508
+ <script>
509
+ // ─────────────────────────────────────────────
510
+ // CONFIG
511
+ // ─────────────────────────────────────────────
512
+ const PASSWORD = "CodeForge#2026";
513
+ const MODEL = "claude-sonnet-4-20250514";
514
+ const API_URL = "https://api.anthropic.com/v1/messages";
515
+
516
+ const SYSTEM_PROMPT = `You are CodeForge, an elite AI code generator. When a user asks for code:
517
+ 1. Briefly explain what you're building (1-2 sentences, plain text).
518
+ 2. Output the complete, working code inside a fenced code block with the correct language tag.
519
+ 3. After the block, add one short tip or note if relevant.
520
+ Keep explanations concise. Prioritise clean, well-commented, production-ready code.
521
+ If the user is following up or asking for modifications, continue naturally.`;
522
+
523
+ const LANGUAGES = [
524
+ { id:"auto", label:"✦ Auto" },
525
+ { id:"javascript", label:"JS" },
526
+ { id:"typescript", label:"TS" },
527
+ { id:"python", label:"Python" },
528
+ { id:"html", label:"HTML/CSS" },
529
+ { id:"react", label:"⚛ React" },
530
+ { id:"sql", label:"SQL" },
531
+ { id:"bash", label:"Bash" },
532
+ { id:"rust", label:"🦀 Rust" },
533
+ { id:"go", label:"Go" },
534
+ { id:"java", label:"Java" },
535
+ { id:"cpp", label:"C++" },
536
+ ];
537
+
538
+ const EXAMPLES = [
539
+ "Build a REST API with JWT auth",
540
+ "Responsive navbar with hamburger",
541
+ "Binary search algorithm",
542
+ "SQL user analytics query",
543
+ "Python web scraper",
544
+ "React counter with history",
545
+ "Debounce utility function",
546
+ "CSS glassmorphism card",
547
+ ];
548
+
549
+ // ─────────────────────────────────────────────
550
+ // STATE
551
+ // ─────────────────────────────────────────────
552
+ const state = {
553
+ unlocked: false,
554
+ messages: [], // {role, content}
555
+ lang: "auto",
556
+ count: 0,
557
+ loading: false,
558
+ };
559
+
560
+ // ─────────────────────────────────────────────
561
+ // API CALL
562
+ // ─────────────────────────────────────────────
563
+ async function callClaude(userPrompt) {
564
+ const langNote = state.lang !== "auto"
565
+ ? ` [Preferred language: ${state.lang}]`
566
+ : "";
567
+
568
+ // Build history + current message
569
+ const history = state.messages.map(m => ({ role: m.role, content: m.content }));
570
+ history.push({ role: "user", content: userPrompt + langNote });
571
+
572
+ const res = await fetch(API_URL, {
573
+ method: "POST",
574
+ headers: { "Content-Type": "application/json" },
575
+ body: JSON.stringify({
576
+ model: MODEL,
577
+ max_tokens: 2048,
578
+ system: SYSTEM_PROMPT,
579
+ messages: history,
580
+ }),
581
+ });
582
+
583
+ if (!res.ok) {
584
+ const err = await res.json().catch(() => ({}));
585
+ throw new Error(err?.error?.message || `HTTP ${res.status}`);
586
+ }
587
+
588
+ const data = await res.json();
589
+ return data.content?.find(b => b.type === "text")?.text ?? "No response.";
590
+ }
591
+
592
+ // ─────────────────────────────────────────────
593
+ // MARKDOWN → HTML (minimal, safe)
594
+ // ─────────────────────────────────────────────
595
+ function escHtml(s) {
596
+ return s.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;");
597
+ }
598
+
599
+ function renderMarkdown(text) {
600
+ // Extract fenced code blocks first (protect from inline processing)
601
+ const codeBlocks = [];
602
+ const placeholder = "\x00CB\x00";
603
+
604
+ let result = text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
605
+ const id = codeBlocks.length;
606
+ const langLabel = lang || "code";
607
+ const safeCode = escHtml(code.trim());
608
+ codeBlocks.push(
609
+ `<div class="code-block">` +
610
+ `<div class="code-header"><span class="code-lang">${langLabel}</span>` +
611
+ `<button class="btn-copy" onclick="copyCode(this)">⎘ COPY</button></div>` +
612
+ `<pre><code>${safeCode}</code></pre></div>`
613
+
614
+ ```
615
+ );
616
+ return placeholder + id + placeholder;
617
+ });
618
+
619
+ // Inline code (before escaping remaining text)
620
+ result = result.replace(/`([^`]+)`/g, (_, c) => `<code>${escHtml(c)}</code>`);
621
+
622
+ // Bold & italic on remaining text segments
623
+ // Process text nodes between placeholders
624
+ const parts = result.split(new RegExp(placeholder.replace(/\x00/g,"\\x00") + "(\\d+)" + placeholder.replace(/\x00/g,"\\x00")));
625
+ let final = "";
626
+ for (let i = 0; i < parts.length; i++) {
627
+ if (i % 2 === 0) {
628
+ // Text segment — apply inline formatting
629
+ let seg = escHtml(parts[i]);
630
+ seg = seg.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>");
631
+ seg = seg.replace(/\*(.+?)\*/g, "<em>$1</em>");
632
+ seg = seg.replace(/\n/g, "<br>");
633
+ final += seg;
634
+ } else {
635
+ // Code block index
636
+ final += codeBlocks[parseInt(parts[i])];
637
+ }
638
+ }
639
+ return final;
640
+ ```
641
+
642
+ }
643
+
644
+ // ─────────────────────────────────────────────
645
+ // COPY CODE
646
+ // ─────────────────────────────────────────────
647
+ window.copyCode = async (btn) => {
648
+ const pre = btn.closest(”.code-block”).querySelector(“pre code”);
649
+ try {
650
+ await navigator.clipboard.writeText(pre.innerText);
651
+ btn.textContent = “✓ COPIED”;
652
+ btn.classList.add(“copied”);
653
+ setTimeout(() => { btn.textContent = “⎘ COPY”; btn.classList.remove(“copied”); }, 1800);
654
+ } catch {
655
+ btn.textContent = “ERR”;
656
+ }
657
+ };
658
+
659
+ // ─────────────────────────────────────────────
660
+ // RENDER
661
+ // ─────────────────────────────────────────────
662
+ const root = document.getElementById(“root”);
663
+
664
+ function renderGate() {
665
+ root.innerHTML = ` <div class="gate"> <div class="gate-card"> <span class="gate-icon">⟨/⟩</span> <div class="gate-title">CODEFORGE</div> <div class="gate-sub">AI · CODE · GENERATOR</div> <div class="gate-label">🔐 ENTER ACCESS CODE</div> <input class="gate-input" id="pwInput" type="password" placeholder="••••••••••••••" autofocus onkeydown="if(event.key==='Enter') tryUnlock()"> <button class="btn-primary" style="width:100%" onclick="tryUnlock()">UNLOCK ACCESS →</button> <div class="gate-error" id="gateErr"></div> </div> </div>`;
666
+ document.getElementById(“pwInput”).focus();
667
+ }
668
+
669
+ function renderApp() {
670
+ // Build messages
671
+ let msgHtml = “”;
672
+ for (const m of state.messages) {
673
+ const isUser = m.role === “user”;
674
+ msgHtml += ` <div class="message ${isUser ? "user" : "assistant"}"> <div class="avatar">${isUser ? "U" : "⟨/⟩"}</div> <div class="bubble">${isUser ? escHtml(m.content).replace(/\n/g,"<br>") : renderMarkdown(m.content)}</div> </div>`;
675
+ }
676
+
677
+ ```
678
+ if (state.loading) {
679
+ msgHtml += `
680
+ <div class="message assistant">
681
+ <div class="avatar">⟨/⟩</div>
682
+ <div class="bubble">
683
+ <div class="typing-wrap">
684
+ <div class="dot"></div><div class="dot"></div><div class="dot"></div>
685
+ <span class="typing-label">generating...</span>
686
  </div>
687
+ </div>
688
+ </div>`;
689
+ }
690
+
691
+ const langBtns = LANGUAGES.map(l =>
692
+ `<button class="lang-btn ${state.lang === l.id ? "active" : ""}" onclick="setLang('${l.id}')">${l.label}</button>`
693
+ ).join("");
694
+
695
+ const exBtns = EXAMPLES.map(e =>
696
+ `<button class="ex-btn" onclick="useExample(this)">▸ ${escHtml(e)}</button>`
697
+ ).join("");
698
+
699
+ root.innerHTML = `
700
+ <div class="shell">
701
+ <div class="header">
702
+ <div class="header-left">
703
+ <span class="logo-icon">⟨/⟩</span>
704
+ <h1>CODEFORGE</h1>
705
+ </div>
706
+ <div class="header-right">
707
+ <span class="stat-pill">⚡ ${state.count} prompts</span>
708
+ <button class="btn-ghost" onclick="lockApp()">🔒 Lock</button>
709
+ <button class="btn-ghost" onclick="clearChat()">🗑 Clear</button>
710
+ </div>
711
+ </div>
712
+
713
+ <div class="lang-bar">
714
+ <div class="lang-inner">${langBtns}</div>
715
+ </div>
716
+
717
+ <div class="examples">
718
+ <div class="examples-inner">${exBtns}</div>
719
+ </div>
720
+
721
+ <div class="chat-area" id="chatArea">
722
+ ${msgHtml || `<div class="empty-state"><div class="big">⟨/⟩</div><div>Describe the code you need to get started</div></div>`}
723
+ </div>
724
+
725
+ <div class="input-area">
726
+ <textarea id="userInput" rows="2"
727
+ placeholder="Describe what you want to build…"
728
+ onkeydown="handleKey(event)"></textarea>
729
+ <div class="btn-row">
730
+ <button class="btn-primary" id="sendBtn" onclick="sendMessage()" ${state.loading ? "disabled" : ""}>▶ GENERATE</button>
731
+ </div>
732
+ <div class="hint">↵ SEND · ⇧↵ NEW LINE</div>
733
+ </div>
734
+ </div>`;
735
+
736
+ // Scroll chat to bottom
737
+ const chat = document.getElementById("chatArea");
738
+ if (chat) chat.scrollTop = chat.scrollHeight;
739
+
740
+ // Restore focus
741
+ const inp = document.getElementById("userInput");
742
+ if (inp) inp.focus();
743
+ ```
744
+
745
+ }
746
+
747
+ function render() {
748
+ state.unlocked ? renderApp() : renderGate();
749
+ }
750
+
751
+ // ─────────────────────────────────────────────
752
+ // EVENT HANDLERS (on window so inline onclick works)
753
+ // ─────────────────────────────────────────────
754
+ window.tryUnlock = () => {
755
+ const v = document.getElementById(“pwInput”).value;
756
+ if (v === PASSWORD) {
757
+ state.unlocked = true;
758
+ render();
759
+ } else {
760
+ const err = document.getElementById(“gateErr”);
761
+ err.textContent = “❌ Invalid access code. Try again.”;
762
+ const inp = document.getElementById(“pwInput”);
763
+ inp.value = “”;
764
+ inp.focus();
765
+ // Shake animation
766
+ inp.style.animation = “none”;
767
+ inp.style.borderColor = “rgba(255,80,80,0.6)”;
768
+ setTimeout(() => { inp.style.borderColor = “”; }, 800);
769
+ }
770
+ };
771
+
772
+ window.setLang = (id) => {
773
+ state.lang = id;
774
+ // Update buttons without full re-render
775
+ document.querySelectorAll(”.lang-btn”).forEach(b => {
776
+ const label = LANGUAGES.find(l => l.id === id)?.label;
777
+ b.classList.toggle(“active”, b.textContent.trim() === label ||
778
+ (id === “auto” && b.textContent.trim() === “✦ Auto”));
779
+ });
780
+ // Re-render just lang bar cleanly
781
+ document.querySelectorAll(”.lang-btn”).forEach(b => b.classList.remove(“active”));
782
+ document.querySelectorAll(”.lang-btn”).forEach(b => {
783
+ const match = LANGUAGES.find(l => l.id === id);
784
+ if (b.textContent.trim() === match?.label) b.classList.add(“active”);
785
+ });
786
+ };
787
+
788
+ window.useExample = (btn) => {
789
+ const text = btn.textContent.replace(/^▸\s*/, “”);
790
+ const inp = document.getElementById(“userInput”);
791
+ if (inp) { inp.value = text; inp.focus(); }
792
+ };
793
+
794
+ window.handleKey = (e) => {
795
+ if (e.key === “Enter” && !e.shiftKey) { e.preventDefault(); sendMessage(); }
796
+ };
797
+
798
+ window.sendMessage = async () => {
799
+ const inp = document.getElementById(“userInput”);
800
+ const text = (inp?.value || “”).trim();
801
+ if (!text || state.loading) return;
802
+ inp.value = “”;
803
+
804
+ ```
805
+ state.messages.push({ role: "user", content: text });
806
+ state.count++;
807
+ state.loading = true;
808
+ render();
809
+
810
+ try {
811
+ const reply = await callClaude(text);
812
+ state.messages.push({ role: "assistant", content: reply });
813
+ } catch (err) {
814
+ state.messages.push({
815
+ role: "assistant",
816
+ content: `⚠️ **Error**: ${err.message}\n\nPlease try again.`
817
+ });
818
+ } finally {
819
+ state.loading = false;
820
+ render();
821
+ }
822
+ ```
823
+
824
+ };
825
+
826
+ window.clearChat = () => {
827
+ if (state.messages.length === 0) return;
828
+ if (confirm(“Clear all messages?”)) {
829
+ state.messages = [];
830
+ state.count = 0;
831
+ render();
832
+ }
833
+ };
834
+
835
+ window.lockApp = () => {
836
+ if (confirm(“Lock CodeForge? You’ll need the password again.”)) {
837
+ state.unlocked = false;
838
+ state.messages = [];
839
+ state.count = 0;
840
+ render();
841
+ }
842
+ };
843
+
844
+ // ─────────────────────────────────────────────
845
+ // BOOT
846
+ // ─────────────────────────────────────────────
847
+ render();
848
+ </script>
849
+
850
  </body>
851
+ </html>