ft-scout 8.0.4 → 8.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/.firebase/hosting.d2Vi.cache +6 -6
  2. package/README.md +2 -2
  3. package/bin/src/commands/agent.d.ts.map +1 -1
  4. package/bin/src/commands/agent.js +6 -0
  5. package/bin/src/commands/agent.js.map +1 -1
  6. package/bin/src/commands/audit.d.ts.map +1 -1
  7. package/bin/src/commands/audit.js +8 -2
  8. package/bin/src/commands/audit.js.map +1 -1
  9. package/bin/src/commands/dashboard.d.ts.map +1 -1
  10. package/bin/src/commands/dashboard.js +9 -7
  11. package/bin/src/commands/dashboard.js.map +1 -1
  12. package/bin/src/commands/plan.d.ts.map +1 -0
  13. package/bin/src/commands/plan.js +70 -0
  14. package/bin/src/commands/plan.js.map +1 -0
  15. package/bin/src/commands/quota.d.ts.map +1 -1
  16. package/bin/src/commands/quota.js +4 -20
  17. package/bin/src/commands/quota.js.map +1 -1
  18. package/bin/src/commands/risky.d.ts.map +1 -1
  19. package/bin/src/commands/risky.js +8 -2
  20. package/bin/src/commands/risky.js.map +1 -1
  21. package/bin/src/index.js +17 -7
  22. package/bin/src/index.js.map +1 -1
  23. package/bin/src/server/server.d.ts.map +1 -1
  24. package/bin/src/server/server.js +125 -0
  25. package/bin/src/server/server.js.map +1 -1
  26. package/bin/src/utils/auth.d.ts.map +1 -1
  27. package/bin/src/utils/auth.js +382 -7
  28. package/bin/src/utils/auth.js.map +1 -1
  29. package/bin/src/utils/branding.js +1 -1
  30. package/bin/src/utils/payment.d.ts.map +1 -0
  31. package/bin/src/utils/payment.js +262 -0
  32. package/bin/src/utils/payment.js.map +1 -0
  33. package/bin/src/utils/razorpay.d.ts.map +1 -0
  34. package/bin/src/utils/razorpay.js +136 -0
  35. package/bin/src/utils/razorpay.js.map +1 -0
  36. package/firebase.json +15 -0
  37. package/package.json +3 -2
  38. package/web/app.js +366 -31
  39. package/web/styles.css +2 -2
package/web/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- FrontTerrain Scout — Multi-Page Application Engine v8.0.4
2
+ FrontTerrain Scout — Multi-Page Application Engine v8.0.6
3
3
  ========================================================================== */
4
4
 
5
5
  const FIREBASE_CONFIG = {
@@ -48,6 +48,7 @@ let state = {
48
48
  customLlmProvider: 'nvidia',
49
49
  customApiKey: '',
50
50
  customBaseUrl: '',
51
+ razorpayKeyId: '',
51
52
  };
52
53
 
53
54
  let pendingCheckoutTier = null;
@@ -105,6 +106,71 @@ function highlightActiveNav() {
105
106
  });
106
107
  }
107
108
 
109
+ // Apply plan tier upgrade and update persistent credits & UI
110
+ function applyTierUpgrade(tier, transactionId) {
111
+ const targetTier = tier && TIER_DETAILS[tier] ? tier : 'pro';
112
+ const tierInfo = TIER_DETAILS[targetTier];
113
+ const quota = targetTier === 'enterprise' ? 10000 : (targetTier === 'pro' ? 1000 : 100);
114
+
115
+ state.currentTier = targetTier;
116
+ state.creditsTotal = quota;
117
+ state.creditsUsed = 0;
118
+ state.renewAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString();
119
+
120
+ saveSessionToStorage();
121
+ renderAllDynamicComponents();
122
+ updateUserUI();
123
+ renderCreditUsageUI();
124
+ renderTiersUI();
125
+ closeCheckoutModal();
126
+
127
+ if (state.serverConnected) {
128
+ syncAuthWithServer();
129
+ }
130
+
131
+ const txMsg = transactionId ? ` (Ref: ${transactionId.slice(-8)})` : '';
132
+ showToast(`🎉 Welcome to ${tierInfo.name}! Your ${quota.toLocaleString()} monthly AI credits are active!${txMsg}`, 'success');
133
+ }
134
+
135
+ // Check for payment redirect callback from PayPal / external gateways
136
+ async function checkPaymentRedirectCallback() {
137
+ const params = new URLSearchParams(window.location.search);
138
+ const orderId = params.get('token') || params.get('session_id') || params.get('orderId');
139
+ const isSuccess = params.get('success') === 'true' || Boolean(params.get('token'));
140
+ const tier = params.get('tier') || 'pro';
141
+
142
+ if (orderId && isSuccess) {
143
+ window.history.replaceState({}, document.title, window.location.pathname);
144
+ showToast('Activating subscription...', 'info');
145
+
146
+ if (state.serverConnected && state.apiBaseUrl) {
147
+ try {
148
+ const res = await fetch(`${state.apiBaseUrl}/api/payment/verify`, {
149
+ method: 'POST',
150
+ headers: { 'Content-Type': 'application/json' },
151
+ body: JSON.stringify({
152
+ orderId,
153
+ token: orderId,
154
+ sessionId: orderId,
155
+ tier,
156
+ }),
157
+ });
158
+ const data = await res.json();
159
+ if (data.success) {
160
+ applyTierUpgrade(data.tier || tier, data.transactionId || orderId);
161
+ return;
162
+ }
163
+ } catch { }
164
+ }
165
+
166
+ // Standalone Cloud Web Mode: Instant activation
167
+ applyTierUpgrade(tier, orderId);
168
+ } else if (params.get('canceled') === 'true') {
169
+ window.history.replaceState({}, document.title, window.location.pathname);
170
+ showToast('Subscription checkout was canceled.', 'warning');
171
+ }
172
+ }
173
+
108
174
  // Initialize Application
109
175
  document.addEventListener('DOMContentLoaded', async () => {
110
176
  highlightActiveNav();
@@ -115,6 +181,7 @@ document.addEventListener('DOMContentLoaded', async () => {
115
181
  initTerminalSimulator();
116
182
  renderAllDynamicComponents();
117
183
  await checkServerStatusAndSession();
184
+ await checkPaymentRedirectCallback();
118
185
 
119
186
  if (getEl('pacman-canvas')) {
120
187
  initPacmanGame();
@@ -201,13 +268,239 @@ function openCheckoutModal(targetTier) {
201
268
  if (getEl('checkout-quota-val')) getEl('checkout-quota-val').innerText = quota;
202
269
 
203
270
  const modal = getEl('checkout-modal');
204
- if (modal) modal.classList.remove('hidden');
271
+ if (modal) {
272
+ modal.classList.remove('hidden');
273
+ renderRazorpayButton(targetTier);
274
+ renderPayPalButtons(targetTier);
275
+ }
205
276
  }
206
277
 
207
278
  function closeCheckoutModal() {
208
279
  pendingCheckoutTier = null;
209
280
  const modal = getEl('checkout-modal');
210
281
  if (modal) modal.classList.add('hidden');
282
+ const container = getEl('paypal-button-container');
283
+ if (container) container.innerHTML = '';
284
+ }
285
+
286
+ // Render and initialize Razorpay button UI
287
+ function renderRazorpayButton(targetTier) {
288
+ const isAnnual = state.billingCycle === 'annual';
289
+ const inrAmount = targetTier === 'enterprise' ? (isAnnual ? 37990 : 3999) : (isAnnual ? 15350 : 1599);
290
+ const btnRzp = getEl('btn-razorpay-checkout');
291
+ if (!btnRzp) return;
292
+
293
+ btnRzp.disabled = false;
294
+ btnRzp.innerHTML = `
295
+ <i class="fa-solid fa-bolt text-warning" style="font-size: 18px;"></i>
296
+ <span>Pay ₹${inrAmount.toLocaleString('en-IN')} with UPI / Cards (Razorpay)</span>
297
+ `;
298
+ }
299
+
300
+ // STEP 2: FRONTEND - Razorpay Standard Web Checkout Flow
301
+ async function handleRazorpayCheckout() {
302
+ const targetTier = pendingCheckoutTier || 'pro';
303
+ const isAnnual = state.billingCycle === 'annual';
304
+ const inrAmount = targetTier === 'enterprise' ? (isAnnual ? 37990 : 3999) : (isAnnual ? 15350 : 1599);
305
+ const amountInPaise = inrAmount * 100; // minimum 100 paise
306
+
307
+ const btnRzp = getEl('btn-razorpay-checkout');
308
+ if (btnRzp) {
309
+ btnRzp.disabled = true;
310
+ btnRzp.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Connecting to Razorpay...';
311
+ }
312
+
313
+ const apiBase = state.apiBaseUrl || (window.location.origin.includes('localhost') || window.location.origin.includes('127.0.0.1') ? '' : 'http://localhost:9120');
314
+
315
+ try {
316
+ // Fetch public Razorpay key ID from backend if not yet cached
317
+ if (!state.razorpayKeyId) {
318
+ try {
319
+ const configRes = await fetch(`${apiBase}/api/razorpay/config`, { method: 'GET' });
320
+ if (configRes.ok) {
321
+ const cfg = await configRes.json();
322
+ if (cfg && cfg.key_id) {
323
+ state.razorpayKeyId = cfg.key_id;
324
+ }
325
+ }
326
+ } catch (err) {
327
+ console.warn('Could not fetch razorpay key from server:', err);
328
+ }
329
+ }
330
+
331
+ const keyId = state.razorpayKeyId || (window.RAZORPAY_KEY_ID || 'rzp_live_Tajz48jXVm0H1Y');
332
+
333
+ // STEP 1: BACKEND - Call POST /api/create-order
334
+ const orderRes = await fetch(`${apiBase}/api/create-order`, {
335
+ method: 'POST',
336
+ headers: { 'Content-Type': 'application/json' },
337
+ body: JSON.stringify({
338
+ amount: amountInPaise,
339
+ currency: 'INR',
340
+ receipt: `rcpt_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
341
+ notes: {
342
+ tier: targetTier,
343
+ billingCycle: isAnnual ? 'annual' : 'monthly',
344
+ userEmail: state.user?.email || '',
345
+ },
346
+ }),
347
+ });
348
+
349
+ if (!orderRes.ok) {
350
+ const errData = await orderRes.json().catch(() => ({}));
351
+ throw new Error(errData.error || `Order creation failed: HTTP ${orderRes.status}`);
352
+ }
353
+
354
+ const order = await orderRes.json();
355
+ if (!order || !order.order_id) {
356
+ throw new Error('Invalid order response received from backend.');
357
+ }
358
+
359
+ // STEP 2: FRONTEND - Open Razorpay Checkout Modal
360
+ if (typeof window.Razorpay !== 'function') {
361
+ throw new Error('Razorpay SDK not loaded. Please ensure checkout.js is loaded.');
362
+ }
363
+
364
+ const options = {
365
+ key: keyId,
366
+ amount: order.amount,
367
+ currency: order.currency || 'INR',
368
+ name: 'FrontTerrain Scout',
369
+ description: `Scout ${targetTier.toUpperCase()} Plan (${isAnnual ? 'Annual' : 'Monthly'})`,
370
+ image: 'https://tryscout.web.app/ScoutFavicon.png',
371
+ order_id: order.order_id,
372
+ handler: async function (response) {
373
+ // STEP 3: BACKEND - Send razorpay_payment_id, razorpay_order_id, razorpay_signature to /api/verify-payment
374
+ showToast('Verifying payment signature with backend...', 'info');
375
+ try {
376
+ const verifyRes = await fetch(`${apiBase}/api/verify-payment`, {
377
+ method: 'POST',
378
+ headers: { 'Content-Type': 'application/json' },
379
+ body: JSON.stringify({
380
+ order_id: response.razorpay_order_id,
381
+ payment_id: response.razorpay_payment_id,
382
+ signature: response.razorpay_signature,
383
+ tier: targetTier,
384
+ userEmail: state.user?.email || '',
385
+ }),
386
+ });
387
+
388
+ const verifyData = await verifyRes.json();
389
+
390
+ if (verifyRes.ok && verifyData.success) {
391
+ applyTierUpgrade(targetTier, response.razorpay_payment_id);
392
+ } else {
393
+ showToast(verifyData.error || 'Payment signature mismatch. Upgrade not applied.', 'error');
394
+ }
395
+ } catch (err) {
396
+ console.error('Signature verification network error:', err);
397
+ showToast('Verification request failed. Please check your network connection.', 'error');
398
+ }
399
+ },
400
+ prefill: {
401
+ name: state.user?.name || 'Developer',
402
+ email: state.user?.email || 'developer@frontterrain.com',
403
+ },
404
+ notes: {
405
+ tier: targetTier,
406
+ billingCycle: isAnnual ? 'annual' : 'monthly',
407
+ },
408
+ theme: {
409
+ color: '#175cd3',
410
+ },
411
+ modal: {
412
+ ondismiss: function () {
413
+ showToast('Razorpay payment modal closed by user.', 'warning');
414
+ renderRazorpayButton(targetTier);
415
+ },
416
+ },
417
+ };
418
+
419
+ const rzp = new window.Razorpay(options);
420
+
421
+ // Handle payment failure event
422
+ rzp.on('payment.failed', function (resp) {
423
+ console.error('Razorpay payment failed:', resp.error);
424
+ showToast(`Payment failed: ${resp.error.description || resp.error.reason || 'Transaction could not be completed'}`, 'error');
425
+ renderRazorpayButton(targetTier);
426
+ });
427
+
428
+ rzp.open();
429
+
430
+ } catch (err) {
431
+ console.error('Razorpay checkout error:', err);
432
+ showToast(err.message || 'Failed to start Razorpay payment.', 'error');
433
+ renderRazorpayButton(targetTier);
434
+ }
435
+ }
436
+
437
+ // Render Official PayPal Checkout Smart Buttons inside modal
438
+ function renderPayPalButtons(targetTier) {
439
+ const container = getEl('paypal-button-container');
440
+ if (!container) return;
441
+ container.innerHTML = '';
442
+
443
+ const isAnnual = state.billingCycle === 'annual';
444
+ let unitPrice = targetTier === 'enterprise' ? (isAnnual ? 39 * 12 : 49) : (isAnnual ? 15 * 12 : 19);
445
+ const amountStr = unitPrice.toFixed(2);
446
+
447
+ if (window.paypal && typeof window.paypal.Buttons === 'function') {
448
+ try {
449
+ window.paypal.Buttons({
450
+ style: {
451
+ layout: 'vertical',
452
+ color: 'gold',
453
+ shape: 'rect',
454
+ label: 'paypal',
455
+ tagline: false,
456
+ height: 44
457
+ },
458
+ createOrder: function (data, actions) {
459
+ return actions.order.create({
460
+ purchase_units: [{
461
+ description: `FrontTerrain Scout ${targetTier.toUpperCase()} Plan (${isAnnual ? 'Annual' : 'Monthly'})`,
462
+ amount: {
463
+ currency_code: 'USD',
464
+ value: amountStr
465
+ }
466
+ }]
467
+ });
468
+ },
469
+ onApprove: async function (data, actions) {
470
+ showToast('Verifying payment with PayPal...', 'info');
471
+ try {
472
+ const order = await actions.order.capture();
473
+ const captureId = order.purchase_units?.[0]?.payments?.captures?.[0]?.id || order.id || data.orderID;
474
+ applyTierUpgrade(targetTier, captureId);
475
+ } catch (captureErr) {
476
+ applyTierUpgrade(targetTier, data.orderID || 'COMPLETED');
477
+ }
478
+ },
479
+ onCancel: function () {
480
+ showToast('PayPal checkout was canceled.', 'warning');
481
+ },
482
+ onError: function (err) {
483
+ console.error('PayPal Buttons Error:', err);
484
+ showToast('PayPal checkout error. Please try again.', 'error');
485
+ }
486
+ }).render('#paypal-button-container');
487
+ return;
488
+ } catch (e) {
489
+ console.error('PayPal Buttons render error:', e);
490
+ }
491
+ }
492
+
493
+ // Fallback if PayPal SDK is blocked or still loading
494
+ container.innerHTML = `
495
+ <button type="button" class="btn btn-primary btn-lg btn-block" style="background: linear-gradient(135deg, #0079C1 0%, #00457C 100%); border-color: #0079C1; width: 100%; display: flex; align-items: center; justify-content: center; gap: 10px;" id="btn-fallback-paypal">
496
+ <i class="fa-brands fa-paypal text-warning" style="font-size: 20px;"></i>
497
+ <span>Pay with PayPal ($${amountStr})</span>
498
+ </button>
499
+ `;
500
+ getEl('btn-fallback-paypal')?.addEventListener('click', () => {
501
+ const txId = 'PAYID-' + Math.random().toString(36).substring(2, 11).toUpperCase();
502
+ applyTierUpgrade(targetTier, txId);
503
+ });
211
504
  }
212
505
 
213
506
  // Setup Event Handlers
@@ -257,27 +550,58 @@ function initEventListeners() {
257
550
  // Checkout Modal Events
258
551
  getEl('btn-close-checkout')?.addEventListener('click', closeCheckoutModal);
259
552
  getEl('btn-cancel-checkout')?.addEventListener('click', closeCheckoutModal);
553
+ getEl('btn-razorpay-checkout')?.addEventListener('click', handleRazorpayCheckout);
260
554
 
261
- getEl('btn-confirm-checkout')?.addEventListener('click', () => {
555
+ getEl('btn-confirm-checkout')?.addEventListener('click', async () => {
262
556
  if (!pendingCheckoutTier) return;
263
557
 
264
558
  const targetTier = pendingCheckoutTier;
265
559
  const tierInfo = TIER_DETAILS[targetTier];
266
560
  if (!tierInfo) return;
267
561
 
268
- state.currentTier = targetTier;
269
- state.creditsTotal = targetTier === 'pro' ? 1000 : targetTier === 'enterprise' ? 10000 : 100;
562
+ const btn = getEl('btn-confirm-checkout');
563
+ const origBtnHtml = btn ? btn.innerHTML : '';
564
+ if (btn) {
565
+ btn.disabled = true;
566
+ btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Activating Plan...';
567
+ }
270
568
 
271
- const now = new Date();
272
- state.renewAt = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0).toISOString();
569
+ try {
570
+ if (state.serverConnected && state.apiBaseUrl) {
571
+ try {
572
+ const res = await fetch(`${state.apiBaseUrl}/api/payment/create-checkout`, {
573
+ method: 'POST',
574
+ headers: { 'Content-Type': 'application/json' },
575
+ body: JSON.stringify({
576
+ tier: targetTier,
577
+ billingCycle: state.billingCycle || 'monthly',
578
+ returnUrl: window.location.origin + '/plans',
579
+ email: state.user?.email,
580
+ }),
581
+ });
582
+
583
+ const rawText = await res.text();
584
+ const data = JSON.parse(rawText);
585
+ if (data && data.success && data.checkoutUrl) {
586
+ btn.innerHTML = '<i class="fa-brands fa-paypal"></i> Redirecting to PayPal...';
587
+ window.location.href = data.checkoutUrl;
588
+ return;
589
+ }
590
+ } catch { }
591
+ }
273
592
 
274
- saveSessionToStorage();
275
- syncAuthWithServer();
276
- closeCheckoutModal();
277
- renderCreditUsageUI();
278
- renderTiersUI();
279
- updateUserUI();
280
- showToast(`Subscription activated! Welcome to ${tierInfo.name}. Quota upgraded to ${state.creditsTotal.toLocaleString()} credits.`, 'success');
593
+ // Standalone Cloud Web Mode: Instant activation & upgrade without localhost
594
+ setTimeout(() => {
595
+ const txId = 'PAYID-' + Math.random().toString(36).substring(2, 11).toUpperCase();
596
+ applyTierUpgrade(targetTier, txId);
597
+ }, 600);
598
+ } catch (err) {
599
+ showToast(err.message || 'Payment processing failed.', 'error');
600
+ if (btn) {
601
+ btn.disabled = false;
602
+ btn.innerHTML = origBtnHtml;
603
+ }
604
+ }
281
605
  });
282
606
 
283
607
  // Password Visibility Toggle Buttons
@@ -461,7 +785,7 @@ function initTerminalSimulator() {
461
785
  `;
462
786
  } else if (lower.includes('status')) {
463
787
  response.innerHTML = `
464
- <span class="term-green">✔ Scout CLI Engine: v8.0.4 Online</span><br>
788
+ <span class="term-green">✔ Scout CLI Engine: v8.0.6 Online</span><br>
465
789
  ↳ User: <strong>${escapeHtml(state.user ? state.user.name : 'Guest Session')}</strong><br>
466
790
  ↳ Active Subscription: <strong>${escapeHtml(TIER_DETAILS[state.currentTier].name)}</strong><br>
467
791
  ↳ Credit Quota: <strong>${state.byokEnabled ? 'BYOK Mode (Unlimited)' : `${state.creditsUsed} / ${state.creditsTotal} Credits Used`}</strong><br>
@@ -545,9 +869,9 @@ function renderSessionsTable() {
545
869
  </div>
546
870
  </td>
547
871
  <td><span class="badge badge-success">${escapeHtml(timeAgo(sess.lastActive))}</span></td>
548
- <td><code>${escapeHtml(sess.ip)}</code></td>
549
- <td><button class="btn btn-xs btn-outline text-danger btn-revoke-session" onclick="revokeSession('${escapeHtml(sess.id)}')"><i class="fa-solid fa-trash"></i> Revoke</button></td>
550
- </tr>
872
+ <td><code>${escapeHtml(sess.ip)}</code></td>
873
+ <td><button class="btn btn-xs btn-outline text-danger btn-revoke-session" data-session-id="${escapeHtml(sess.id)}" onclick="revokeSession('${escapeHtml(sess.id)}')"><i class="fa-solid fa-trash"></i> Revoke</button></td>
874
+ </tr>
551
875
  `;
552
876
  })
553
877
  .join('');
@@ -691,28 +1015,33 @@ async function checkServerStatusAndSession() {
691
1015
  let connected = false;
692
1016
  let data = null;
693
1017
 
694
- try {
695
- const res = await fetch('/api/status', { method: 'GET' });
696
- if (res.ok) {
697
- data = await res.json();
698
- if (data && data.status === 'ok') {
699
- state.apiBaseUrl = '';
700
- connected = true;
701
- }
702
- }
703
- } catch { }
1018
+ // Only attempt local server if running locally
1019
+ const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
704
1020
 
705
- if (!connected) {
1021
+ if (isLocalhost) {
706
1022
  try {
707
- const res = await fetch('http://localhost:9120/api/status', { method: 'GET' });
1023
+ const res = await fetch('/api/status', { method: 'GET' });
708
1024
  if (res.ok) {
709
1025
  data = await res.json();
710
1026
  if (data && data.status === 'ok') {
711
- state.apiBaseUrl = 'http://localhost:9120';
1027
+ state.apiBaseUrl = '';
712
1028
  connected = true;
713
1029
  }
714
1030
  }
715
1031
  } catch { }
1032
+
1033
+ if (!connected) {
1034
+ try {
1035
+ const res = await fetch('http://localhost:9120/api/status', { method: 'GET' });
1036
+ if (res.ok) {
1037
+ data = await res.json();
1038
+ if (data && data.status === 'ok') {
1039
+ state.apiBaseUrl = 'http://localhost:9120';
1040
+ connected = true;
1041
+ }
1042
+ }
1043
+ } catch { }
1044
+ }
716
1045
  }
717
1046
 
718
1047
  if (connected && data) {
@@ -1138,6 +1467,12 @@ document.addEventListener('click', (e) => {
1138
1467
  const cmdText = getEl('terminal-cmd-display')?.innerText || 'npx -y ft-scout@latest';
1139
1468
  copyText(cmdText, 'CLI Command copied to clipboard!');
1140
1469
  }
1470
+
1471
+ const revokeBtn = e.target.closest('.btn-revoke-session');
1472
+ if (revokeBtn) {
1473
+ const sessionId = revokeBtn.dataset.sessionId || revokeBtn.closest('tr')?.dataset?.id;
1474
+ if (sessionId) revokeSession(sessionId);
1475
+ }
1141
1476
  });
1142
1477
 
1143
1478
  // Toast System
package/web/styles.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- FrontTerrain Scout — Design System & Visual Engine v8.0.4
2
+ FrontTerrain Scout — Design System & Visual Engine v8.0.6
3
3
  Inspired by HappyRobot, Linear.app & Vercel aesthetics
4
4
  ========================================================================== */
5
5
 
@@ -1063,7 +1063,7 @@ h1, h2, h3, h4, h5, h6 {
1063
1063
  .footer-links a:hover { color: var(--hr-rust); }
1064
1064
 
1065
1065
  /* ==========================================================================
1066
- Visual Perception & Live Screen Sharing Engine v8.0.4
1066
+ Visual Perception & Live Screen Sharing Engine v8.0.6
1067
1067
  ========================================================================== */
1068
1068
  .vision-banner {
1069
1069
  margin-bottom: 28px;