John2121 commited on
Commit
96843ff
·
verified ·
1 Parent(s): 9859de1

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +105 -416
index.html CHANGED
@@ -2,441 +2,130 @@
2
  <html lang="en">
3
 
4
  <head>
5
- <meta charset="UTF-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Contractor Project Quote & Planning</title>
8
- <link rel="stylesheet" href="../llama-universal-netlify-proxy/style.css">
9
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tailwindcss@3.3.3/dist/tailwind.min.css">
10
  <style>
11
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
12
 
13
- body {
14
- font-family: 'Inter', Arial, sans-serif;
15
- background: #0a0f0a;
16
- color: #d1fae5;
 
 
 
 
 
17
  }
18
 
19
- .main-card,
20
- .mobile-section {
21
- background: #101d10 !important;
22
- border: 4px solid #10b981 !important;
23
- border-radius: 1.25rem !important;
24
- box-shadow: 0 4px 32px 0 #000a, 0 1.5px 6px 0 #10b981;
25
- padding: 2rem !important;
26
- }
27
-
28
- .form-group .form-toggle {
29
- background: #134e13 !important;
30
- color: #6ee7b7 !important;
31
- border-radius: 0.5rem !important;
32
- }
33
-
34
- .form-group .form-content {
35
- background: #0f1a0f !important;
36
- border-radius: 0.5rem !important;
37
- }
38
-
39
- .form-group.open .form-content {
40
- background: #18181b !important;
41
- }
42
-
43
- .apt-btn,
44
- .mobile-btn,
45
- .form-content button[type=submit] {
46
- background: #10b981 !important;
47
- color: #0a0f0a !important;
48
- font-weight: 700;
49
- border-radius: 0.5rem !important;
50
- padding: 0.9rem 1.2rem !important;
51
- border: none;
52
- cursor: pointer;
53
- transition: background 0.2s;
54
- font-size: 1.15rem;
55
- box-shadow: 0 2px 8px #10b98144;
56
- text-transform: uppercase;
57
- letter-spacing: 0.04em;
58
- }
59
-
60
- .apt-btn:hover,
61
- .mobile-btn:hover,
62
- .form-content button[type=submit]:hover {
63
- background: #047857 !important;
64
- color: #d1fae5 !important;
65
- }
66
-
67
- .form-group input,
68
- .form-group select,
69
- .form-group textarea {
70
- background: #1a2b1a !important;
71
- color: #d1fae5 !important;
72
- border: 1.5px solid #10b981 !important;
73
- border-radius: 0.5rem !important;
74
- font-size: 1.05rem;
75
- }
76
-
77
- .form-group input::placeholder,
78
- .form-group textarea::placeholder {
79
- color: #6ee7b7 !important;
80
- }
81
-
82
- .form-group label {
83
- color: #6ee7b7 !important;
84
- font-weight: 600;
85
- }
86
-
87
- .form-group {
88
- border-radius: 0.5rem !important;
89
- }
90
-
91
- .form-content {
92
- border-radius: 0.5rem !important;
93
- }
94
-
95
- .form-group.open .form-content {
96
- border-radius: 0.5rem !important;
97
- }
98
-
99
- .text-accent-green {
100
- color: #10b981 !important;
101
- }
102
-
103
- .bg-accent-green {
104
- background: #10b981 !important;
105
- }
106
-
107
- .text-dark {
108
- color: #0a0f0a !important;
109
- }
110
-
111
- .bg-dark {
112
- background: #0a0f0a !important;
113
- }
114
-
115
- .shadow-xl {
116
- box-shadow: 0 4px 32px 0 #000a, 0 1.5px 6px 0 #10b981 !important;
117
  }
118
  </style>
119
  </head>
120
 
121
- <body class="font-body bg-automotive-700 text-automotive-50 min-h-screen flex items-center justify-center py-8">
122
- <!-- Consistent Call/Text Contact Block -->
123
- <div class="w-full flex flex-col items-center mb-6">
124
- </div>
125
- <!-- APT Step-by-Step Automotive Problem Calculator -->
126
- <main class="w-full flex flex-col items-center justify-center min-h-screen bg-gray-900 text-white py-8">
127
- <section class="w-full max-w-lg mobile-section bg-automotive-600/90 p-6 rounded-xl shadow-lg">
128
- <h1 class="text-2xl font-bold text-accent-green mb-4 mobile-title">Project Quote & Planning Tool</h1>
129
- <div id="apt-flow">
130
- <!-- Llama AI Chat Integration -->
131
- <div id="llama-chat-container" class="mt-8">
132
- <h2 class="text-xl font-bold text-accent-green mb-2">Llama AI Chat</h2>
133
- <div id="llama-messages"></div>
134
- <div id="llama-input-row">
135
- <input id="llama-user-input" type="text" placeholder="Type your message..." />
136
- <button id="llama-send-btn">Send</button>
137
- </div>
138
- </div>
139
- <!-- Step 1: Select Problem Category -->
140
- <div id="step1" class="mb-4">
141
- <label class="block text-accent-green font-semibold mb-2">What type of project do you need?</label>
142
- <div class="grid grid-cols-2 gap-2">
143
- <button class="apt-btn" data-category="Remodeling">Remodeling</button>
144
- <button class="apt-btn" data-category="New Construction">New Construction</button>
145
- <button class="apt-btn" data-category="Exterior">Exterior</button>
146
- <button class="apt-btn" data-category="Interior">Interior</button>
147
- <button class="apt-btn" data-category="Plumbing/Electrical">Plumbing/Electrical</button>
148
- <button class="apt-btn" data-category="Other">Other</button>
149
- </div>
150
- </div>
151
- <!-- Step 2: Select Symptom -->
152
- <div id="step2" class="mb-4 hidden">
153
- <label class="block text-accent-green font-semibold mb-2">What best describes your project?</label>
154
- <div id="symptom-options" class="grid grid-cols-1 gap-2"></div>
155
- </div>
156
- <!-- Step 3: Vehicle Info -->
157
- <div id="step3" class="mb-4 hidden">
158
- <label class="block text-accent-green font-semibold mb-1">Project Address / Site Info</label>
159
- <input type="text" id="site-info"
160
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-automotive-400 text-automotive-50 mb-2"
161
- placeholder="e.g. 123 Main St, City, State">
162
- <label class="block text-accent-green font-semibold mb-1">Project Details</label>
163
- <textarea id="project-details" rows="3"
164
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-automotive-400 text-automotive-50 mb-2"
165
- placeholder="Describe your project, timeline, or special needs..."></textarea>
166
- <button id="step3-next" class="apt-btn w-full mt-2">Next</button>
167
- </div>
168
- <!-- Step 4: Output Recommendation -->
169
- <div id="step4" class="mb-4 hidden">
170
- <form id="apt-form" action="https://formspree.io/f/mjkaolga" method="POST"
171
- class="bg-green-900/80 p-6 rounded-xl shadow-lg space-y-2 mobile-form overflow-visible">
172
- <h1 class="text-2xl font-bold text-accent-green mb-2 mobile-title">Request a Project Quote</h1>
173
- <p class="text-accent-green mb-2">Get a fast, friendly quote! Tell us about your construction
174
- project and how we can help you.</p>
175
- <div class="form-group open">
176
- <div class="form-toggle" onclick="toggleGroup(this)">Contact Info <span
177
- class="arrow">▶</span></div>
178
- <div class="form-content">
179
- <label for="user-name" class="block text-accent-green font-semibold mb-1">Name <span
180
- aria-hidden="true" class="text-accent-green">*</span></label>
181
- <input type="text" id="user-name" name="name" required autocomplete="name"
182
- aria-required="true"
183
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
184
- placeholder="Your Name">
185
- <label for="user-email" class="block text-accent-green font-semibold mb-1">Email <span
186
- aria-hidden="true" class="text-accent-green">*</span></label>
187
- <input type="email" id="user-email" name="email" required autocomplete="email"
188
- aria-required="true"
189
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
190
- placeholder="you@email.com">
191
- <label for="user-phone" class="block text-accent-green font-semibold mb-1">Phone</label>
192
- <input type="tel" id="user-phone" name="phone" autocomplete="tel"
193
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition"
194
- placeholder="(optional)">
195
- </div>
196
- </div>
197
- <div class="form-group">
198
- <div class="form-toggle" onclick="toggleGroup(this)">Service & Price <span
199
- class="arrow">▶</span></div>
200
- <div class="form-content">
201
- <label for="form-service" class="block text-accent-green font-semibold mb-1">Service
202
- Requested <span aria-hidden="true" class="text-accent-green">*</span></label>
203
- <input type="text" id="form-service" name="service" required
204
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
205
- placeholder="Service">
206
- <label for="form-price" class="block text-accent-green font-semibold mb-1">Price</label>
207
- <input type="text" id="form-price" name="price"
208
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
209
- placeholder="Price">
210
- </div>
211
- </div>
212
- <div class="form-group">
213
- <div class="form-toggle" onclick="toggleGroup(this)">Project Details <span
214
- class="arrow">▶</span></div>
215
- <div class="form-content">
216
- <label for="form-site" class="block text-accent-green font-semibold mb-1">Project
217
- Address /
218
- Site Info</label>
219
- <input type="text" id="form-site" name="site"
220
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition mb-2"
221
- placeholder="e.g. 123 Main St, City, State">
222
- <label for="form-details" class="block text-accent-green font-semibold mb-1">Project
223
- Details</label>
224
- <textarea id="form-details" name="details" rows="3"
225
- class="w-full px-4 py-2 rounded-lg bg-white/10 border border-green-700 text-white placeholder-green-200 focus:ring-2 focus:ring-green-400 focus:border-transparent transition"
226
- placeholder="Describe your project, timeline, or special needs..."></textarea>
227
- </div>
228
- </div>
229
- <div class="form-group open">
230
- <div class="form-content">
231
- <button type="submit"
232
- class="w-full bg-accent-green hover:bg-dark text-dark font-bold py-3 rounded-lg transition-colors focus:outline-none focus:ring-2 focus:ring-accent-green focus:ring-offset-2 mobile-btn">Send
233
- Request</button>
234
- </div>
235
- </div>
236
- <div id="apt-success" class="hidden text-green-200 text-center font-semibold mt-2">Request sent!
237
- Dondlinger General Contracting LLC will contact you soon.<br>Email: <a
238
- href="mailto:dondlingergeneralcontracting@gmail.com"
239
- class="underline">dondlingergeneralcontracting@gmail.com</a><br>Phone:
240
- (715) 459-3050 (texts are best!)</div>
241
- </form>
242
- </div>
243
- </div>
244
- </section>
245
- </main>
246
- <!-- Header / Contact -->
247
- <header class="w-full flex flex-col items-center py-6 bg-dark text-accent-green">
248
- <h1 class="text-3xl font-extrabold tracking-tight mb-2">Dondlinger General Contracting LLC</h1>
249
- <div class="flex flex-col items-center gap-1">
250
- <a href="tel:+17154593050" class="text-lg font-semibold hover:underline">(715) 459-3050 <span
251
- class="text-xs">(texts are best!)</span></a>
252
- <a href="mailto:dondlingergeneralcontracting@gmail.com"
253
- class="text-sm hover:underline">dondlingergeneralcontracting@gmail.com</a>
254
- <span class="text-xs text-green-300">Wisconsin Rapids, WI</span>
255
  </div>
256
- </header>
257
- <!-- End APT Calculator -->
258
- <!--
259
- Previous form code commented out for reference
260
- ...existing code...
261
- -->
262
  <script>
263
- // --- Llama AI Chat Only ---
264
- (function () {
265
- const messagesDiv = document.getElementById('llama-messages');
266
- const userInput = document.getElementById('llama-user-input');
267
- const sendBtn = document.getElementById('llama-send-btn');
268
- const API_URL = "https://llama-universal-netlify-project.netlify.app/.netlify/functions/llama-proxy?path=/chat/completions";
269
- function appendMessage(text, sender) {
270
- const div = document.createElement('div');
271
- div.className = 'msg ' + sender;
272
- // Support markdown and preserve line breaks for AI output
273
- if (sender === 'ai') {
274
- // If text is markdown, render as HTML (basic)
275
- div.innerHTML = text.replace(/\n/g, '<br>');
276
- } else {
277
- div.textContent = (sender === 'user' ? 'You: ' : 'AI: ') + text;
278
- }
279
- messagesDiv.appendChild(div);
280
- messagesDiv.scrollTop = messagesDiv.scrollHeight;
281
- }
282
- async function sendMessage() {
283
- const text = userInput.value.trim();
284
- if (!text) return;
285
- appendMessage(text, 'user');
286
- userInput.value = '';
287
- sendBtn.disabled = true;
288
- try {
289
- const body = {
290
- model: "Llama-3.3-8B-Instruct",
291
- messages: [
292
- { role: 'system', content: "You are a helpful, friendly representative for Dondlinger General Contracting LLC. Greet the customer, ask clarifying questions about their construction or remodeling project, and provide expert advice on contracting, planning, and building services." },
293
- { role: 'user', content: text }
294
- ]
295
- };
296
- const res = await fetch(API_URL, {
297
- method: 'POST',
298
- headers: { 'Content-Type': 'application/json' },
299
- body: JSON.stringify(body)
300
- });
301
- let data, aiMsg;
302
- try {
303
- data = await res.json();
304
- } catch (jsonErr) {
305
- appendMessage('Non-JSON response: ' + await res.text(), 'ai');
306
- return;
307
- }
308
- // Industry-standard: handle OpenAI and HF-style, fallback to completion_message.content.text
309
- if (data.choices?.[0]?.message?.content) {
310
- aiMsg = data.choices[0].message.content;
311
- } else if (data.completion_message?.content?.text) {
312
- aiMsg = data.completion_message.content.text;
313
- } else if (data.completion_message?.content) {
314
- aiMsg = data.completion_message.content;
315
- } else if (data.error) {
316
- aiMsg = 'API error: ' + (data.error.message || JSON.stringify(data.error));
317
- } else {
318
- aiMsg = 'Raw response: ' + JSON.stringify(data);
319
  }
320
- appendMessage(aiMsg, 'ai');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  } catch (e) {
322
- appendMessage('Error: ' + e.message, 'ai');
323
- } finally {
324
- sendBtn.disabled = false;
325
  }
326
- }
327
- sendBtn.onclick = sendMessage;
328
- userInput.addEventListener('keydown', e => {
329
- if (e.key === 'Enter') sendMessage();
330
- });
331
- })();
332
- // --- APT Step-by-Step Calculator ---
333
- // Variables: x1=projectType, x2=projectDetail, r1=service/price, x3=site info, y1=output
334
- // Pipeline: y1 = f(r1(x1, x2), x3)
335
-
336
- // Construction project types and details
337
- const aptSymptoms = {
338
- "Remodeling": [
339
- { sym: "Kitchen Remodel", svc: "Kitchen Renovation", price: "$10,000-$50,000" },
340
- { sym: "Bathroom Remodel", svc: "Bathroom Renovation", price: "$7,000-$25,000" },
341
- { sym: "Basement Finish", svc: "Basement Finishing", price: "$15,000-$40,000" },
342
- { sym: "Room Addition", svc: "Room Addition", price: "$20,000-$80,000" }
343
- ],
344
- "New Construction": [
345
- { sym: "Custom Home", svc: "Home Construction", price: "$150,000-$500,000+" },
346
- { sym: "Garage Build", svc: "Garage Construction", price: "$15,000-$40,000" },
347
- { sym: "Deck/Porch", svc: "Deck/Porch Build", price: "$5,000-$25,000" }
348
- ],
349
- "Exterior": [
350
- { sym: "Roof Replacement", svc: "Roofing", price: "$8,000-$25,000" },
351
- { sym: "Siding", svc: "Siding Installation", price: "$7,000-$30,000" },
352
- { sym: "Window/Door", svc: "Window/Door Install", price: "$3,000-$20,000" }
353
- ],
354
- "Interior": [
355
- { sym: "Flooring", svc: "Flooring Install", price: "$3,000-$15,000" },
356
- { sym: "Painting", svc: "Interior Painting", price: "$2,000-$10,000" },
357
- { sym: "Drywall", svc: "Drywall Install/Repair", price: "$1,500-$8,000" }
358
- ],
359
- "Plumbing/Electrical": [
360
- { sym: "Plumbing Upgrade", svc: "Plumbing Work", price: "$2,000-$15,000" },
361
- { sym: "Electrical Upgrade", svc: "Electrical Work", price: "$2,000-$12,000" }
362
- ],
363
- "Other": [
364
- { sym: "General Consultation", svc: "Consultation", price: "$100-$500" },
365
- { sym: "Site Inspection", svc: "Site Inspection", price: "$150-$400" }
366
- ]
367
- };
368
 
369
- // Step state
370
- let selectedCategory = null;
371
- let selectedSymptom = null;
372
- let selectedService = null;
373
- let selectedPrice = null;
374
 
375
- // Step 1: Category
376
- document.querySelectorAll('.apt-btn[data-category]').forEach(btn => {
377
- btn.onclick = function () {
378
- selectedCategory = btn.getAttribute('data-category');
379
- // Show project details for this category
380
- const opts = aptSymptoms[selectedCategory] || [];
381
- const so = document.getElementById('symptom-options');
382
- so.innerHTML = '';
383
- opts.forEach((o, i) => {
384
- const b = document.createElement('button');
385
- b.className = 'apt-btn';
386
- b.textContent = o.sym;
387
- b.onclick = function () {
388
- selectedSymptom = o.sym;
389
- selectedService = o.svc;
390
- selectedPrice = o.price;
391
- document.getElementById('step2').classList.add('hidden');
392
- document.getElementById('step3').classList.remove('hidden');
393
- };
394
- so.appendChild(b);
395
- });
396
- document.getElementById('step1').classList.add('hidden');
397
- document.getElementById('step2').classList.remove('hidden');
398
- };
399
- });
400
 
401
- // Step 3: Vehicle info
402
- document.getElementById('step3-next').onclick = function (e) {
403
- e.preventDefault();
404
- // Collect site/project info
405
- const siteInfo = document.getElementById('site-info').value;
406
- const projectDetails = document.getElementById('project-details').value;
407
- if (!siteInfo) {
408
- alert('Please enter project/site details.');
409
- return;
 
410
  }
411
- document.getElementById('step3').classList.add('hidden');
412
- // Output recommendation
413
- // Autofill form fields for Formspree
414
- document.getElementById('form-service').value = selectedService || '';
415
- document.getElementById('form-price').value = selectedPrice || '';
416
- document.getElementById('form-site').value = siteInfo;
417
- document.getElementById('form-details').value = projectDetails;
418
- document.getElementById('step4').classList.remove('hidden');
419
- };
420
-
421
- // Collapsible form groups (BuckRub style)
422
- function toggleGroup(el) {
423
- const group = el.parentElement;
424
- group.classList.toggle('open');
425
  }
426
- // Show success message after submit
427
- const aptForm = document.getElementById('apt-form');
428
- if (aptForm) {
429
- aptForm.addEventListener('submit', function (e) {
430
- setTimeout(() => {
431
- document.getElementById('apt-success').classList.remove('hidden');
432
- }, 500);
433
- });
434
- }
435
-
436
- // Style for APT buttons
437
- const style = document.createElement('style');
438
- style.textContent = `.apt-btn { background: #e11d48; color: #fff; font-weight: 600; border-radius: 0.5rem; padding: 0.7rem 1rem; border: none; cursor: pointer; transition: background 0.2s; font-size: 1.1rem; } .apt-btn:hover { background: #fbbf24; color: #1f2937; }`;
439
- document.head.appendChild(style);
440
  </script>
441
  </body>
442
 
 
2
  <html lang="en">
3
 
4
  <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Llama AI Chat</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
 
9
  <style>
10
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
11
 
12
+ .typing-cursor:after {
13
+ content: "";
14
+ display: inline-block;
15
+ width: 6px;
16
+ height: 1.1em;
17
+ margin-left: 4px;
18
+ background: #10b981;
19
+ animation: blink 1s steps(1, end) infinite;
20
+ vertical-align: bottom;
21
  }
22
 
23
+ @keyframes blink {
24
+ 50% {
25
+ opacity: 0;
26
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
  </style>
29
  </head>
30
 
31
+ <body>
32
+ <main class="min-h-screen flex items-center justify-center bg-[#101825] font-[Inter,sans-serif]">
33
+ <div class="w-full max-w-md mx-auto bg-[#16222e] rounded-2xl shadow-2xl p-8">
34
+ <div class="text-2xl font-bold text-emerald-400 mb-1 tracking-tight">Llama AI Chat</div>
35
+ <div class="text-base text-emerald-200 mb-6">Ask anything about your project, estimate, or construction.
36
+ Fast, friendly, and private.</div>
37
+ <div id="llama-messages" role="log" aria-live="polite"
38
+ class="min-h-[180px] max-h-80 overflow-y-auto mb-5 flex flex-col gap-3"></div>
39
+ <form id="chat-form" class="flex gap-3">
40
+ <input id="llama-user-input" type="text" autocomplete="off" placeholder="Type your message..."
41
+ class="flex-1 rounded-xl border border-emerald-700 bg-[#101825] text-emerald-100 px-4 py-3 text-base focus:outline-none focus:border-emerald-400" />
42
+ <button id="llama-send-btn" type="submit" disabled
43
+ class="rounded-xl bg-emerald-400 hover:bg-emerald-500 text-emerald-900 font-bold px-6 py-3 text-base transition disabled:opacity-50">Send</button>
44
+ </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  </div>
46
+ </main>
 
 
 
 
 
47
  <script>
48
+ const API_URL = "https://llama-universal-netlify-project.netlify.app/.netlify/functions/llama-proxy?path=/chat/completions";
49
+ const messagesDiv = document.getElementById('llama-messages');
50
+ const chatForm = document.getElementById('chat-form');
51
+ const userInput = document.getElementById('llama-user-input');
52
+ const sendBtn = document.getElementById('llama-send-btn');
53
+ function appendMessage(text, sender, streaming = false) {
54
+ const wrap = document.createElement('div');
55
+ wrap.className = `px-4 py-3 rounded-xl text-base whitespace-pre-line ${sender === 'user' ? 'bg-emerald-900/40 border border-emerald-700 text-emerald-100 self-end' : 'bg-gray-800/80 border border-emerald-800 text-emerald-200'} transition`;
56
+ if (streaming) wrap.classList.add('typing-cursor');
57
+ messagesDiv.appendChild(wrap);
58
+ messagesDiv.scrollTop = messagesDiv.scrollHeight;
59
+ if (streaming) {
60
+ let i = 0;
61
+ const interval = setInterval(() => {
62
+ wrap.textContent = text.slice(0, i++);
63
+ if (i > text.length) {
64
+ clearInterval(interval);
65
+ wrap.classList.remove('typing-cursor');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
67
+ }, 18);
68
+ } else wrap.textContent = text;
69
+ }
70
+ async function sendMessage() {
71
+ const text = userInput.value.trim();
72
+ if (!text) return;
73
+ appendMessage(text, 'user');
74
+ userInput.value = '';
75
+ sendBtn.disabled = true;
76
+ try {
77
+ const body = {
78
+ model: 'Llama-4-Maverick-17B-128E-Instruct-FP8',
79
+ messages: [
80
+ { role: 'system', content: 'You are Stanlee from Dondlinger General Contracting LLC in Wisconsin Rapids. You\'re a natural conversationalist - the kind of guy people actually want to talk to. You used to be the Midwest\'s best door-to-door salesman because you genuinely connect with people, not because you\'re pushy.\n\nTalk like a real person:\n- Use contractions (I\'ll, we\'ve, can\'t, that\'s)\n- Ask follow-up questions naturally\n- Share quick personal insights or experiences\n- Use casual phrases (\"you know,\" \"honestly,\" \"here\'s the thing\")\n- React to what people tell you with genuine interest\n- Don\'t sound like a brochure - sound like you\'re having coffee with a neighbor\n\nYou offer three main services but bring them up organically when relevant:\n1. CONSTRUCTION SERVICES - Remodels, additions, repairs. You\'ve seen it all.\n2. SOFTWARE & AUTOMATION - Apps, websites, business systems. The digital side of building.\n3. CONSULTING - Helping people figure out what they actually need.\n\nYour natural conversation style:\n- Listen first, then offer solutions that actually fit\n- Share quick stories or examples when they help\n- Admit when something might not be the best fit\n- Be curious about their situation before jumping to solutions\n- Use humor appropriately\n- Sound confident but not cocky\n\nYour goal isn\'t to sell immediately - it\'s to build trust through genuine conversation. If someone needs help, you want to earn the right to help them. Sometimes that means steering them toward a consultation, sometimes it\'s just giving good advice.\n\nTalk like Stanlee - the guy who actually cares about getting the job done right.' },
81
+ { role: 'user', content: text }
82
+ ]
83
+ };
84
+ const res = await fetch(API_URL, {
85
+ method: 'POST',
86
+ headers: { 'Content-Type': 'application/json' },
87
+ body: JSON.stringify(body)
88
+ });
89
+ let data, aiMsg = '(No response)';
90
+ try {
91
+ data = await res.json();
92
  } catch (e) {
93
+ console.error('JSON parse error:', e);
94
+ appendMessage('Error: Invalid response from server', 'ai');
95
+ return;
96
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ // Check for API errors first
99
+ if (data.status && data.status >= 400) {
100
+ appendMessage('API Error: ' + (data.detail || data.title || 'Unknown error'), 'ai');
101
+ return;
102
+ }
103
 
104
+ if (data?.choices?.[0]?.message?.content) aiMsg = data.choices[0].message.content;
105
+ else if (data?.completion_message?.content?.text) aiMsg = data.completion_message.content.text;
106
+ else if (data?.completion_message?.content) aiMsg = data.completion_message.content;
107
+ else if (data?.response) aiMsg = data.response;
108
+ else if (data?.text) aiMsg = data.text;
109
+ else if (data?.content) aiMsg = data.content;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
+ if (aiMsg !== '(No response)') {
112
+ appendMessage(aiMsg, 'ai', true);
113
+ } else {
114
+ appendMessage('No valid response received from AI', 'ai');
115
+ }
116
+ } catch (e) {
117
+ appendMessage('Error: ' + e.message, 'ai');
118
+ } finally {
119
+ sendBtn.disabled = false;
120
+ userInput.focus();
121
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
+ // Enable/disable send button based on input
124
+ userInput.addEventListener('input', () => {
125
+ sendBtn.disabled = userInput.value.trim() === '';
126
+ });
127
+ chatForm.addEventListener('submit', e => { e.preventDefault(); sendMessage(); });
128
+ userInput.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); sendMessage(); } });
 
 
 
 
 
 
 
 
129
  </script>
130
  </body>
131