Restrictated commited on
Commit
5f680c0
·
verified ·
1 Parent(s): bd6be38

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +397 -15
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Cubic — AGI, AI, and Cybersecurity Research</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600;8..60,700&display=swap" rel="stylesheet">
@@ -168,7 +168,7 @@
168
  margin-bottom:5px;
169
  letter-spacing:-0.01em;
170
  }
171
- .metric span{font-size:10.5px;color:var(--muted);letter-spacing:0.04em;text-transform:uppercase;font-family:'IBM Plex Mono',monospace;}
172
 
173
  section{margin-bottom:64px;}
174
  h2{
@@ -184,7 +184,7 @@
184
  }
185
  h2::after{content:"";flex:1;height:1px;background:var(--line-bright);}
186
 
187
- .focus-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
188
  .card{
189
  position:relative;
190
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
@@ -293,6 +293,69 @@
293
  .project-meta span{color:var(--muted);}
294
  .project-card p.hint{font-size:12px;color:var(--muted);margin:0;font-family:'IBM Plex Mono',monospace;}
295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  .team-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
297
  .team-card{text-align:center;}
298
  .avatar{
@@ -423,26 +486,21 @@
423
  </nav>
424
  </header>
425
 
426
- <div class="eyebrow">AGI · AI · Cybersecurity Research</div>
427
  <h1>Accessible AI systems that run on consumer hardware.</h1>
428
  <p class="tagline">CPU-first training, open-source research, and practical deployment — no data center required.</p>
429
 
430
  <div class="metrics">
431
- <div class="metric"><b><span class="pin1" title="pin 1"></span>27</b><span>Models</span></div>
432
- <div class="metric"><b>1</b><span>Research paper</span></div>
433
- <div class="metric"><b>3</b><span>Team members</span></div>
434
- <div class="metric"><b>$0.004</b><span>Min training cost</span></div>
435
- <div class="metric"><b>100%</b><span>Open source</span></div>
436
  </div>
437
 
438
  <section>
439
  <h2>Research Focus</h2>
440
  <div class="focus-grid" id="focus-grid">
441
- <button type="button" class="card focus-card" data-filter="agi">
442
- <b>AGI Research</b>
443
- <p>Advancing towards artificial general intelligence.</p>
444
- <em>Filter projects →</em>
445
- </button>
446
  <button type="button" class="card focus-card" data-filter="cpu-first code">
447
  <b>AI Development</b>
448
  <p>CPU-first architectures and open-source models.</p>
@@ -477,6 +535,148 @@
477
  <div class="empty-state" id="empty-state">No projects match that search. Press Esc to reset.</div>
478
  </section>
479
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
480
  <section>
481
  <h2>Team</h2>
482
  <div class="team-grid">
@@ -492,7 +692,7 @@
492
  <li>Dark/Light theme toggle</li>
493
  <li>Category filtering on projects</li>
494
  <li>Search across projects</li>
495
- <li>Layered 3D sun backdrop</li>
496
  <li>Keyboard shortcuts (/ to search, Esc to close)</li>
497
  <li>Print-friendly CSS</li>
498
  <li>Responsive design</li>
@@ -639,6 +839,188 @@
639
  });
640
  });
641
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  /* ---------- cursor glow (perf-guarded, respects reduced motion) ---------- */
643
  var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
644
  if (!reduceMotion) {
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Cubic — AI and Cybersecurity Research</title>
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600;700&family=Source+Serif+4:opsz,wght@8..60,400;8..60,500;8..60,600;8..60,700&display=swap" rel="stylesheet">
 
168
  margin-bottom:5px;
169
  letter-spacing:-0.01em;
170
  }
171
+ .metric > span{font-size:11px;color:var(--muted);letter-spacing:0.04em;text-transform:uppercase;font-family:'IBM Plex Mono',monospace;}
172
 
173
  section{margin-bottom:64px;}
174
  h2{
 
184
  }
185
  h2::after{content:"";flex:1;height:1px;background:var(--line-bright);}
186
 
187
+ .focus-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px;}
188
  .card{
189
  position:relative;
190
  background:linear-gradient(180deg, var(--panel-2), var(--panel));
 
293
  .project-meta span{color:var(--muted);}
294
  .project-card p.hint{font-size:12px;color:var(--muted);margin:0;font-family:'IBM Plex Mono',monospace;}
295
 
296
+ .calc-panel{padding:0;overflow:hidden;}
297
+ .calc-tabs{
298
+ display:flex;
299
+ flex-wrap:wrap;
300
+ border-bottom:1px solid var(--line);
301
+ background:var(--panel);
302
+ }
303
+ .calc-tab{
304
+ background:none;
305
+ border:none;
306
+ border-right:1px solid var(--line);
307
+ color:var(--muted);
308
+ font-family:'IBM Plex Mono',monospace;
309
+ font-size:11.5px;
310
+ letter-spacing:0.02em;
311
+ padding:12px 14px;
312
+ cursor:pointer;
313
+ transition:color 0.12s ease, background 0.12s ease;
314
+ }
315
+ .calc-tab:hover{color:var(--silk);}
316
+ .calc-tab.active{color:var(--silk);background:var(--panel-2);box-shadow:inset 0 -2px 0 var(--accent);}
317
+ .calc-tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px;}
318
+
319
+ .calc-body{padding:20px;}
320
+ .calc-pane{display:none;}
321
+ .calc-pane.active{display:block;}
322
+ .calc-desc{font-size:13px;color:var(--muted);margin:0 0 16px;line-height:1.55;}
323
+ .calc-row{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:11px;}
324
+ .calc-row label{font-family:'IBM Plex Mono',monospace;font-size:12px;color:var(--text);}
325
+ .calc-row input,.calc-row select{
326
+ width:180px;
327
+ background:var(--panel);
328
+ border:1px solid var(--line);
329
+ color:var(--text);
330
+ border-radius:6px;
331
+ padding:8px 10px;
332
+ font-size:13px;
333
+ font-family:'IBM Plex Mono',monospace;
334
+ outline:none;
335
+ transition:border-color 0.15s ease;
336
+ }
337
+ .calc-row input:focus,.calc-row select:focus{border-color:var(--line-bright);}
338
+ .calc-result{
339
+ margin-top:16px;
340
+ padding:13px 14px;
341
+ background:var(--panel);
342
+ border:1px solid var(--line-bright);
343
+ border-radius:6px;
344
+ font-size:13.5px;
345
+ color:var(--muted);
346
+ font-family:'IBM Plex Mono',monospace;
347
+ }
348
+ .calc-result b{color:var(--silk);font-size:15px;}
349
+ .calc-compare{display:flex;flex-direction:column;gap:6px;}
350
+ .calc-compare .cc-row{display:flex;justify-content:space-between;}
351
+ .calc-compare .cc-row span:first-child{color:var(--text);}
352
+ .calc-compare .cc-row span:last-child{color:var(--silk);font-weight:600;}
353
+
354
+ @media (max-width:720px){
355
+ .calc-row{flex-direction:column;align-items:flex-start;}
356
+ .calc-row input,.calc-row select{width:100%;}
357
+ }
358
+
359
  .team-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;}
360
  .team-card{text-align:center;}
361
  .avatar{
 
486
  </nav>
487
  </header>
488
 
489
+ <div class="eyebrow">AI · Cybersecurity Research</div>
490
  <h1>Accessible AI systems that run on consumer hardware.</h1>
491
  <p class="tagline">CPU-first training, open-source research, and practical deployment — no data center required.</p>
492
 
493
  <div class="metrics">
494
+ <div class="metric"><b><span class="pin1" title="pin 1"></span><span class="count-num" data-target="27" data-suffix="">0</span></b><span>Models</span></div>
495
+ <div class="metric"><b><span class="count-num" data-target="1" data-suffix="">0</span></b><span>Research paper</span></div>
496
+ <div class="metric"><b><span class="count-num" data-target="3" data-suffix="">0</span></b><span>Team members</span></div>
497
+ <div class="metric"><b>$<span class="count-num" data-target="0.004" data-decimals="3" data-suffix="">0</span></b><span>Min training cost</span></div>
498
+ <div class="metric"><b><span class="count-num" data-target="100" data-suffix="%">0</span></b><span>Open source</span></div>
499
  </div>
500
 
501
  <section>
502
  <h2>Research Focus</h2>
503
  <div class="focus-grid" id="focus-grid">
 
 
 
 
 
504
  <button type="button" class="card focus-card" data-filter="cpu-first code">
505
  <b>AI Development</b>
506
  <p>CPU-first architectures and open-source models.</p>
 
535
  <div class="empty-state" id="empty-state">No projects match that search. Press Esc to reset.</div>
536
  </section>
537
 
538
+ <section>
539
+ <h2>Interactive Tools</h2>
540
+ <div class="calc-panel card">
541
+ <div class="calc-tabs" id="calc-tabs" role="tablist" aria-label="Calculator selector">
542
+ <button type="button" class="calc-tab active" data-calc="training-cost" role="tab" aria-selected="true">Training Cost</button>
543
+ <button type="button" class="calc-tab" data-calc="inference-speed" role="tab" aria-selected="false">Inference Speed</button>
544
+ <button type="button" class="calc-tab" data-calc="gguf-size" role="tab" aria-selected="false">GGUF Size</button>
545
+ <button type="button" class="calc-tab" data-calc="context-length" role="tab" aria-selected="false">Context Length</button>
546
+ <button type="button" class="calc-tab" data-calc="memory-usage" role="tab" aria-selected="false">Memory Usage</button>
547
+ <button type="button" class="calc-tab" data-calc="training-time" role="tab" aria-selected="false">Training Time</button>
548
+ <button type="button" class="calc-tab" data-calc="model-compare" role="tab" aria-selected="false">Model Compare</button>
549
+ </div>
550
+
551
+ <div class="calc-body">
552
+
553
+ <div class="calc-pane active" data-pane="training-cost">
554
+ <p class="calc-desc">Extrapolates from AXL's measured $0.004 per-model cost on consumer CPU.</p>
555
+ <div class="calc-row">
556
+ <label for="tc-models">Number of models</label>
557
+ <input type="number" id="tc-models" min="1" step="1" value="27">
558
+ </div>
559
+ <div class="calc-result" id="tc-result">Total: <b>$0.108</b></div>
560
+ </div>
561
+
562
+ <div class="calc-pane" data-pane="inference-speed">
563
+ <p class="calc-desc">Estimated CPU tokens/sec by parameter count (byte-level transformer, single-thread baseline).</p>
564
+ <div class="calc-row">
565
+ <label for="is-params">Model size (params)</label>
566
+ <select id="is-params">
567
+ <option value="566000">566K (smallest AXL)</option>
568
+ <option value="8000000">8M</option>
569
+ <option value="60000000">60M</option>
570
+ <option value="150000000">150M</option>
571
+ <option value="318000000" selected>318M (largest AXL)</option>
572
+ </select>
573
+ </div>
574
+ <div class="calc-row">
575
+ <label for="is-threads">CPU threads</label>
576
+ <input type="number" id="is-threads" min="1" max="64" step="1" value="4">
577
+ </div>
578
+ <div class="calc-result" id="is-result">Estimated: <b>— tok/s</b></div>
579
+ </div>
580
+
581
+ <div class="calc-pane" data-pane="gguf-size">
582
+ <p class="calc-desc">File size from parameter count and quantization level.</p>
583
+ <div class="calc-row">
584
+ <label for="gg-params">Parameters</label>
585
+ <input type="number" id="gg-params" min="1" step="1" value="318000000">
586
+ </div>
587
+ <div class="calc-row">
588
+ <label for="gg-quant">Quantization</label>
589
+ <select id="gg-quant">
590
+ <option value="4">Q4 (4-bit)</option>
591
+ <option value="5">Q5 (5-bit)</option>
592
+ <option value="8" selected>Q8 (8-bit)</option>
593
+ <option value="16">F16 (16-bit)</option>
594
+ <option value="32">F32 (32-bit)</option>
595
+ </select>
596
+ </div>
597
+ <div class="calc-result" id="gg-result">File size: <b>—</b></div>
598
+ </div>
599
+
600
+ <div class="calc-pane" data-pane="context-length">
601
+ <p class="calc-desc">KV-cache memory required for a given context window.</p>
602
+ <div class="calc-row">
603
+ <label for="cl-tokens">Context length (tokens)</label>
604
+ <input type="number" id="cl-tokens" min="1" step="1" value="4096">
605
+ </div>
606
+ <div class="calc-row">
607
+ <label for="cl-layers">Layers</label>
608
+ <input type="number" id="cl-layers" min="1" step="1" value="12">
609
+ </div>
610
+ <div class="calc-row">
611
+ <label for="cl-dims">Hidden dim</label>
612
+ <input type="number" id="cl-dims" min="1" step="1" value="768">
613
+ </div>
614
+ <div class="calc-result" id="cl-result">KV-cache: <b>—</b></div>
615
+ </div>
616
+
617
+ <div class="calc-pane" data-pane="memory-usage">
618
+ <p class="calc-desc">Total estimated RAM: weights at your chosen quantization plus KV-cache overhead.</p>
619
+ <div class="calc-row">
620
+ <label for="mu-params">Parameters</label>
621
+ <input type="number" id="mu-params" min="1" step="1" value="318000000">
622
+ </div>
623
+ <div class="calc-row">
624
+ <label for="mu-quant">Quantization</label>
625
+ <select id="mu-quant">
626
+ <option value="4">Q4 (4-bit)</option>
627
+ <option value="8" selected>Q8 (8-bit)</option>
628
+ <option value="16">F16 (16-bit)</option>
629
+ </select>
630
+ </div>
631
+ <div class="calc-row">
632
+ <label for="mu-context">Context (tokens)</label>
633
+ <input type="number" id="mu-context" min="0" step="1" value="4096">
634
+ </div>
635
+ <div class="calc-result" id="mu-result">Estimated RAM: <b>—</b></div>
636
+ </div>
637
+
638
+ <div class="calc-pane" data-pane="training-time">
639
+ <p class="calc-desc">Backs out training time from the $0.004/model anchor at a given effective hourly CPU rate.</p>
640
+ <div class="calc-row">
641
+ <label for="tt-rate">CPU cost ($ / hour)</label>
642
+ <input type="number" id="tt-rate" min="0.001" step="0.001" value="0.05">
643
+ </div>
644
+ <div class="calc-row">
645
+ <label for="tt-cost">Cost per model ($)</label>
646
+ <input type="number" id="tt-cost" min="0.0001" step="0.001" value="0.004">
647
+ </div>
648
+ <div class="calc-result" id="tt-result">Estimated time: <b>—</b></div>
649
+ </div>
650
+
651
+ <div class="calc-pane" data-pane="model-compare">
652
+ <p class="calc-desc">Side-by-side comparison of two AXL model sizes.</p>
653
+ <div class="calc-row">
654
+ <label for="mc-a">Model A (params)</label>
655
+ <select id="mc-a">
656
+ <option value="566000" selected>566K</option>
657
+ <option value="8000000">8M</option>
658
+ <option value="60000000">60M</option>
659
+ <option value="150000000">150M</option>
660
+ <option value="318000000">318M</option>
661
+ </select>
662
+ </div>
663
+ <div class="calc-row">
664
+ <label for="mc-b">Model B (params)</label>
665
+ <select id="mc-b">
666
+ <option value="566000">566K</option>
667
+ <option value="8000000">8M</option>
668
+ <option value="60000000">60M</option>
669
+ <option value="150000000">150M</option>
670
+ <option value="318000000" selected>318M</option>
671
+ </select>
672
+ </div>
673
+ <div class="calc-result calc-compare" id="mc-result"></div>
674
+ </div>
675
+
676
+ </div>
677
+ </div>
678
+ </section>
679
+
680
  <section>
681
  <h2>Team</h2>
682
  <div class="team-grid">
 
692
  <li>Dark/Light theme toggle</li>
693
  <li>Category filtering on projects</li>
694
  <li>Search across projects</li>
695
+ <li>Animated counters on metrics</li>
696
  <li>Keyboard shortcuts (/ to search, Esc to close)</li>
697
  <li>Print-friendly CSS</li>
698
  <li>Responsive design</li>
 
839
  });
840
  });
841
 
842
+ /* ---------- calculator tabs ---------- */
843
+ var calcTabs = Array.prototype.slice.call(document.querySelectorAll('.calc-tab'));
844
+ var calcPanes = Array.prototype.slice.call(document.querySelectorAll('.calc-pane'));
845
+
846
+ calcTabs.forEach(function(tab){
847
+ tab.addEventListener('click', function(){
848
+ var target = tab.dataset.calc;
849
+ calcTabs.forEach(function(t){
850
+ t.classList.toggle('active', t === tab);
851
+ t.setAttribute('aria-selected', t === tab ? 'true' : 'false');
852
+ });
853
+ calcPanes.forEach(function(p){
854
+ p.classList.toggle('active', p.dataset.pane === target);
855
+ });
856
+ });
857
+ });
858
+
859
+ function fmtBytes(bytes){
860
+ if (bytes < 1024) return bytes.toFixed(0) + ' B';
861
+ var units = ['KB','MB','GB','TB'];
862
+ var val = bytes;
863
+ var i = -1;
864
+ do { val /= 1024; i++; } while (val >= 1024 && i < units.length - 1);
865
+ return val.toFixed(2) + ' ' + units[i];
866
+ }
867
+
868
+ function fmtParams(n){
869
+ if (n >= 1e9) return (n / 1e9).toFixed(2) + 'B';
870
+ if (n >= 1e6) return (n / 1e6).toFixed(1) + 'M';
871
+ if (n >= 1e3) return (n / 1e3).toFixed(0) + 'K';
872
+ return String(n);
873
+ }
874
+
875
+ /* Training Cost — extrapolates from the $0.004/model measured anchor */
876
+ var tcModels = document.getElementById('tc-models');
877
+ var tcResult = document.getElementById('tc-result');
878
+ function calcTrainingCost(){
879
+ var n = parseFloat(tcModels.value) || 0;
880
+ var total = n * 0.004;
881
+ tcResult.innerHTML = 'Total: <b>$' + total.toFixed(3) + '</b>';
882
+ }
883
+ tcModels.addEventListener('input', calcTrainingCost);
884
+
885
+ /* Inference Speed — heuristic tok/s from param count + thread count on CPU */
886
+ var isParams = document.getElementById('is-params');
887
+ var isThreads = document.getElementById('is-threads');
888
+ var isResult = document.getElementById('is-result');
889
+ function calcInferenceSpeed(){
890
+ var params = parseFloat(isParams.value) || 1;
891
+ var threads = Math.max(1, parseFloat(isThreads.value) || 1);
892
+ /* baseline: ~40 tok/s per thread for a 1M-param byte-level model,
893
+ scaling down roughly with sqrt of param count growth */
894
+ var base = 40 / Math.sqrt(params / 1e6);
895
+ var estimate = base * Math.pow(threads, 0.7);
896
+ isResult.innerHTML = 'Estimated: <b>' + estimate.toFixed(1) + ' tok/s</b>';
897
+ }
898
+ isParams.addEventListener('change', calcInferenceSpeed);
899
+ isThreads.addEventListener('input', calcInferenceSpeed);
900
+
901
+ /* GGUF File Size — params × bytes-per-param at chosen quantization + ~5% metadata overhead */
902
+ var ggParams = document.getElementById('gg-params');
903
+ var ggQuant = document.getElementById('gg-quant');
904
+ var ggResult = document.getElementById('gg-result');
905
+ function calcGGUFSize(){
906
+ var params = parseFloat(ggParams.value) || 0;
907
+ var bitsPerParam = parseFloat(ggQuant.value) || 8;
908
+ var bytes = params * (bitsPerParam / 8) * 1.05;
909
+ ggResult.innerHTML = 'File size: <b>' + fmtBytes(bytes) + '</b>';
910
+ }
911
+ ggParams.addEventListener('input', calcGGUFSize);
912
+ ggQuant.addEventListener('change', calcGGUFSize);
913
+
914
+ /* Context Length — KV-cache = 2 (K+V) × layers × context × hidden_dim × 2 bytes (fp16) */
915
+ var clTokens = document.getElementById('cl-tokens');
916
+ var clLayers = document.getElementById('cl-layers');
917
+ var clDims = document.getElementById('cl-dims');
918
+ var clResult = document.getElementById('cl-result');
919
+ function calcContextLength(){
920
+ var tokens = parseFloat(clTokens.value) || 0;
921
+ var layers = parseFloat(clLayers.value) || 0;
922
+ var dims = parseFloat(clDims.value) || 0;
923
+ var bytes = 2 * layers * tokens * dims * 2;
924
+ clResult.innerHTML = 'KV-cache: <b>' + fmtBytes(bytes) + '</b>';
925
+ }
926
+ [clTokens, clLayers, clDims].forEach(function(el){ el.addEventListener('input', calcContextLength); });
927
+
928
+ /* Memory Usage — weights at quantization + KV-cache overhead (assumes 12 layers / 768 dim default arch) */
929
+ var muParams = document.getElementById('mu-params');
930
+ var muQuant = document.getElementById('mu-quant');
931
+ var muContext = document.getElementById('mu-context');
932
+ var muResult = document.getElementById('mu-result');
933
+ function calcMemoryUsage(){
934
+ var params = parseFloat(muParams.value) || 0;
935
+ var bitsPerParam = parseFloat(muQuant.value) || 8;
936
+ var context = parseFloat(muContext.value) || 0;
937
+ var weightBytes = params * (bitsPerParam / 8);
938
+ var kvBytes = 2 * 12 * context * 768 * 2; /* approximate default arch */
939
+ var total = weightBytes + kvBytes;
940
+ muResult.innerHTML = 'Estimated RAM: <b>' + fmtBytes(total) + '</b>';
941
+ }
942
+ [muParams, muQuant, muContext].forEach(function(el){ el.addEventListener('input', calcMemoryUsage); el.addEventListener('change', calcMemoryUsage); });
943
+
944
+ /* Training Time — time = cost / hourly rate */
945
+ var ttRate = document.getElementById('tt-rate');
946
+ var ttCost = document.getElementById('tt-cost');
947
+ var ttResult = document.getElementById('tt-result');
948
+ function calcTrainingTime(){
949
+ var rate = parseFloat(ttRate.value) || 0.0001;
950
+ var cost = parseFloat(ttCost.value) || 0;
951
+ var hours = cost / rate;
952
+ var minutes = hours * 60;
953
+ var label = minutes < 60 ? minutes.toFixed(1) + ' min' : hours.toFixed(2) + ' hr';
954
+ ttResult.innerHTML = 'Estimated time: <b>' + label + '</b>';
955
+ }
956
+ [ttRate, ttCost].forEach(function(el){ el.addEventListener('input', calcTrainingTime); });
957
+
958
+ /* Model Comparison — side by side on params, est. GGUF (Q8) size, and est. inference speed */
959
+ var mcA = document.getElementById('mc-a');
960
+ var mcB = document.getElementById('mc-b');
961
+ var mcResult = document.getElementById('mc-result');
962
+ function speedFor(params){ return 40 / Math.sqrt(params / 1e6) * Math.pow(4, 0.7); }
963
+ function calcModelCompare(){
964
+ var a = parseFloat(mcA.value) || 1;
965
+ var b = parseFloat(mcB.value) || 1;
966
+ var sizeA = a * 1 * 1.05, sizeB = b * 1 * 1.05; /* Q8 */
967
+ var speedA = speedFor(a), speedB = speedFor(b);
968
+ mcResult.innerHTML =
969
+ '<div class="cc-row"><span>Parameters</span><span>' + fmtParams(a) + ' vs ' + fmtParams(b) + '</span></div>' +
970
+ '<div class="cc-row"><span>Est. GGUF size (Q8)</span><span>' + fmtBytes(sizeA) + ' vs ' + fmtBytes(sizeB) + '</span></div>' +
971
+ '<div class="cc-row"><span>Est. inference (4 threads)</span><span>' + speedA.toFixed(1) + ' vs ' + speedB.toFixed(1) + ' tok/s</span></div>' +
972
+ '<div class="cc-row"><span>Training cost</span><span>$0.004 vs $0.004</span></div>';
973
+ }
974
+ [mcA, mcB].forEach(function(el){ el.addEventListener('change', calcModelCompare); });
975
+
976
+ /* run all calculators once on load so panes aren't showing placeholder dashes */
977
+ calcTrainingCost();
978
+ calcInferenceSpeed();
979
+ calcGGUFSize();
980
+ calcContextLength();
981
+ calcMemoryUsage();
982
+ calcTrainingTime();
983
+ calcModelCompare();
984
+
985
+ /* ---------- animated metric counters ---------- */
986
+ function animateCounters(){
987
+ var nums = Array.prototype.slice.call(document.querySelectorAll('.count-num'));
988
+ var duration = 900;
989
+
990
+ nums.forEach(function(el){
991
+ var target = parseFloat(el.dataset.target);
992
+ var decimals = el.dataset.decimals ? parseInt(el.dataset.decimals, 10) : 0;
993
+ var suffix = el.dataset.suffix || '';
994
+ if (isNaN(target)) return;
995
+
996
+ var start = null;
997
+ function step(ts){
998
+ if (start === null) start = ts;
999
+ var progress = Math.min((ts - start) / duration, 1);
1000
+ var eased = 1 - Math.pow(1 - progress, 3);
1001
+ var value = target * eased;
1002
+ el.textContent = value.toFixed(decimals) + suffix;
1003
+ if (progress < 1) {
1004
+ requestAnimationFrame(step);
1005
+ } else {
1006
+ el.textContent = target.toFixed(decimals) + suffix;
1007
+ }
1008
+ }
1009
+ requestAnimationFrame(step);
1010
+ });
1011
+ }
1012
+
1013
+ var reduceMotionCounters = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
1014
+ if (reduceMotionCounters) {
1015
+ document.querySelectorAll('.count-num').forEach(function(el){
1016
+ var target = parseFloat(el.dataset.target);
1017
+ var decimals = el.dataset.decimals ? parseInt(el.dataset.decimals, 10) : 0;
1018
+ el.textContent = target.toFixed(decimals) + (el.dataset.suffix || '');
1019
+ });
1020
+ } else {
1021
+ animateCounters();
1022
+ }
1023
+
1024
  /* ---------- cursor glow (perf-guarded, respects reduced motion) ---------- */
1025
  var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
1026
  if (!reduceMotion) {