Aashish34 commited on
Commit
6536b45
·
1 Parent(s): b241b7e

update maths file

Browse files
math-ds-complete/app.js CHANGED
@@ -18,14 +18,28 @@ let animationFrames = {};
18
 
19
  // ===== INITIALIZATION =====
20
  document.addEventListener('DOMContentLoaded', () => {
 
21
  initNavigation();
22
  initSubjectTabs();
23
  initInteractiveElements();
24
  setupScrollObserver();
25
  initializeAllVisualizations();
26
-
 
 
 
 
 
 
27
  // Show initial subject
28
  switchSubject('statistics');
 
 
 
 
 
 
 
29
  });
30
 
31
  // ===== SUBJECT SWITCHING =====
@@ -41,7 +55,7 @@ function initSubjectTabs() {
41
 
42
  function switchSubject(subject) {
43
  currentSubject = subject;
44
-
45
  // Update active tab
46
  document.querySelectorAll('.subject-tab').forEach(tab => {
47
  tab.classList.remove('active');
@@ -49,7 +63,7 @@ function switchSubject(subject) {
49
  tab.classList.add('active');
50
  }
51
  });
52
-
53
  // Update sidebar title
54
  const titles = {
55
  'statistics': 'Statistics Content',
@@ -62,7 +76,7 @@ function switchSubject(subject) {
62
  if (sidebarTitle) {
63
  sidebarTitle.textContent = titles[subject];
64
  }
65
-
66
  // Show/hide sidebar modules
67
  document.querySelectorAll('.module').forEach(module => {
68
  const moduleSubject = module.dataset.subject;
@@ -73,13 +87,13 @@ function switchSubject(subject) {
73
  module.style.display = subject === 'statistics' ? 'block' : 'none';
74
  }
75
  });
76
-
77
  // Show/hide topic sections
78
  document.querySelectorAll('.topic-section').forEach(section => {
79
  const sectionSubject = section.dataset.subject || 'statistics';
80
  section.style.display = sectionSubject === subject ? 'block' : 'none';
81
  });
82
-
83
  // Scroll to first topic of subject
84
  const firstTopic = document.querySelector(`.topic-section[data-subject="${subject}"], .topic-section:not([data-subject])`);
85
  if (firstTopic && subject !== 'statistics') {
@@ -101,7 +115,7 @@ function initNavigation() {
101
  // Mobile menu toggle
102
  const mobileMenuBtn = document.getElementById('mobileMenuBtn');
103
  const sidebar = document.getElementById('sidebar');
104
-
105
  if (mobileMenuBtn) {
106
  mobileMenuBtn.addEventListener('click', () => {
107
  sidebar.classList.toggle('active');
@@ -114,17 +128,17 @@ function initNavigation() {
114
  link.addEventListener('click', (e) => {
115
  e.preventDefault();
116
  const topicId = link.getAttribute('data-topic');
117
-
118
  // Handle both 'topic-X' and 'ml-topic-X' formats
119
  let target = document.getElementById(topicId);
120
  if (!target && !topicId.includes('-')) {
121
  target = document.getElementById(`topic-${topicId}`);
122
  }
123
-
124
  if (target) {
125
  target.scrollIntoView({ behavior: 'smooth', block: 'start' });
126
  updateActiveLink(topicId);
127
-
128
  // Close mobile menu if open
129
  if (window.innerWidth <= 1024) {
130
  sidebar.classList.remove('active');
@@ -230,14 +244,14 @@ function calculateMedian(data) {
230
  function calculateMode(data) {
231
  const frequency = {};
232
  let maxFreq = 0;
233
-
234
  data.forEach(val => {
235
  frequency[val] = (frequency[val] || 0) + 1;
236
  maxFreq = Math.max(maxFreq, frequency[val]);
237
  });
238
-
239
  if (maxFreq === 1) return 'None';
240
-
241
  const modes = Object.keys(frequency).filter(key => frequency[key] === maxFreq);
242
  return modes.join(', ');
243
  }
@@ -257,17 +271,17 @@ function calculateQuartiles(data) {
257
  const sorted = [...data].sort((a, b) => a - b);
258
  const q2 = calculateMedian(sorted);
259
  const midIndex = Math.floor(sorted.length / 2);
260
-
261
  const lowerHalf = sorted.length % 2 === 0
262
  ? sorted.slice(0, midIndex)
263
  : sorted.slice(0, midIndex);
264
  const upperHalf = sorted.length % 2 === 0
265
  ? sorted.slice(midIndex)
266
  : sorted.slice(midIndex + 1);
267
-
268
  const q1 = calculateMedian(lowerHalf);
269
  const q3 = calculateMedian(upperHalf);
270
-
271
  return { q1, q2, q3 };
272
  }
273
 
@@ -276,7 +290,7 @@ function calculateIQR(data) {
276
  const iqr = q3 - q1;
277
  const lowerFence = q1 - 1.5 * iqr;
278
  const upperFence = q3 + 1.5 * iqr;
279
-
280
  return { q1, q3, iqr, lowerFence, upperFence };
281
  }
282
 
@@ -289,11 +303,11 @@ function calculateCovariance(x, y) {
289
  const meanX = calculateMean(x);
290
  const meanY = calculateMean(y);
291
  let sum = 0;
292
-
293
  for (let i = 0; i < x.length; i++) {
294
  sum += (x[i] - meanX) * (y[i] - meanY);
295
  }
296
-
297
  return sum / (x.length - 1);
298
  }
299
 
@@ -310,12 +324,12 @@ function calculateCorrelation(x, y) {
310
  function initPopulationSampleViz() {
311
  const canvas = document.getElementById('populationSampleCanvas');
312
  if (!canvas) return;
313
-
314
  const ctx = canvas.getContext('2d');
315
  let population = [];
316
  let sample = [];
317
  let sampleSize = 30;
318
-
319
  // Initialize population
320
  for (let i = 0; i < 200; i++) {
321
  population.push({
@@ -324,57 +338,57 @@ function initPopulationSampleViz() {
324
  inSample: false
325
  });
326
  }
327
-
328
  function draw() {
329
  clearCanvas(ctx, canvas);
330
-
331
  // Draw title
332
  drawText(ctx, 'Population (All dots) vs Sample (Highlighted)', canvas.width / 2, 30, 16, COLORS.cyan);
333
-
334
  // Draw population
335
  population.forEach(point => {
336
  const color = point.inSample ? COLORS.orange : COLORS.primary;
337
  const radius = point.inSample ? 6 : 4;
338
  drawCircle(ctx, point.x, point.y, radius, color);
339
  });
340
-
341
  // Draw statistics
342
  const popCount = population.length;
343
  const sampleCount = population.filter(p => p.inSample).length;
344
  drawText(ctx, `Population Size: N = ${popCount}`, 150, canvas.height - 20, 14, COLORS.text, 'center');
345
  drawText(ctx, `Sample Size: n = ${sampleCount}`, canvas.width - 150, canvas.height - 20, 14, COLORS.orange, 'center');
346
  }
347
-
348
  function takeSample() {
349
  // Reset all
350
  population.forEach(p => p.inSample = false);
351
-
352
  // Randomly select sample
353
  const shuffled = [...population].sort(() => Math.random() - 0.5);
354
  for (let i = 0; i < Math.min(sampleSize, population.length); i++) {
355
  shuffled[i].inSample = true;
356
  }
357
-
358
  draw();
359
  }
360
-
361
  // Event listeners
362
  const sampleBtn = document.getElementById('sampleBtn');
363
  const resetBtn = document.getElementById('resetPopBtn');
364
  const sizeSlider = document.getElementById('sampleSizeSlider');
365
  const sizeLabel = document.getElementById('sampleSizeLabel');
366
-
367
  if (sampleBtn) {
368
  sampleBtn.addEventListener('click', takeSample);
369
  }
370
-
371
  if (resetBtn) {
372
  resetBtn.addEventListener('click', () => {
373
  population.forEach(p => p.inSample = false);
374
  draw();
375
  });
376
  }
377
-
378
  if (sizeSlider) {
379
  sizeSlider.addEventListener('input', (e) => {
380
  sampleSize = parseInt(e.target.value);
@@ -383,7 +397,7 @@ function initPopulationSampleViz() {
383
  }
384
  });
385
  }
386
-
387
  draw();
388
  }
389
 
@@ -391,73 +405,73 @@ function initPopulationSampleViz() {
391
  function initCentralTendencyViz() {
392
  const canvas = document.getElementById('centralTendencyCanvas');
393
  if (!canvas) return;
394
-
395
  const ctx = canvas.getContext('2d');
396
  let data = [10, 20, 30, 40, 50];
397
-
398
  function parseInput(input) {
399
  return input.split(',').map(s => parseFloat(s.trim())).filter(n => !isNaN(n));
400
  }
401
-
402
  function draw() {
403
  clearCanvas(ctx, canvas);
404
-
405
  if (data.length === 0) {
406
  drawText(ctx, 'Please enter valid numbers', canvas.width / 2, canvas.height / 2, 16, COLORS.orange);
407
  return;
408
  }
409
-
410
  const sorted = [...data].sort((a, b) => a - b);
411
  const min = Math.min(...sorted);
412
  const max = Math.max(...sorted);
413
  const range = max - min || 1;
414
  const padding = 80;
415
  const width = canvas.width - 2 * padding;
416
-
417
  // Calculate statistics
418
  const mean = calculateMean(data);
419
  const median = calculateMedian(data);
420
  const mode = calculateMode(data);
421
-
422
  // Update results display
423
  document.getElementById('meanResult').textContent = mean.toFixed(2);
424
  document.getElementById('medianResult').textContent = median.toFixed(2);
425
  document.getElementById('modeResult').textContent = mode;
426
-
427
  // Draw axis
428
  const axisY = canvas.height / 2;
429
  drawLine(ctx, padding, axisY, canvas.width - padding, axisY, COLORS.text, 2);
430
-
431
  // Draw data points
432
  sorted.forEach((val, idx) => {
433
  const x = padding + ((val - min) / range) * width;
434
  drawCircle(ctx, x, axisY, 8, COLORS.primary);
435
  drawText(ctx, val.toString(), x, axisY + 30, 12, COLORS.text);
436
  });
437
-
438
  // Draw mean
439
  const meanX = padding + ((mean - min) / range) * width;
440
  drawLine(ctx, meanX, axisY - 60, meanX, axisY + 60, COLORS.cyan, 3);
441
  drawText(ctx, `Mean: ${mean.toFixed(2)}`, meanX, axisY - 70, 14, COLORS.cyan);
442
-
443
  // Draw median
444
  const medianX = padding + ((median - min) / range) * width;
445
  drawLine(ctx, medianX, axisY - 50, medianX, axisY + 50, COLORS.orange, 2);
446
  drawText(ctx, `Median: ${median.toFixed(2)}`, medianX, axisY - 55, 12, COLORS.orange);
447
  }
448
-
449
  // Event listeners
450
  const input = document.getElementById('centralTendencyInput');
451
  const calcBtn = document.getElementById('calculateCentralBtn');
452
  const randomBtn = document.getElementById('randomDataBtn');
453
-
454
  if (calcBtn && input) {
455
  calcBtn.addEventListener('click', () => {
456
  data = parseInput(input.value);
457
  draw();
458
  });
459
  }
460
-
461
  if (randomBtn && input) {
462
  randomBtn.addEventListener('click', () => {
463
  data = Array.from({ length: 10 }, () => Math.floor(Math.random() * 100));
@@ -465,7 +479,7 @@ function initCentralTendencyViz() {
465
  draw();
466
  });
467
  }
468
-
469
  if (input) {
470
  input.addEventListener('keypress', (e) => {
471
  if (e.key === 'Enter') {
@@ -474,7 +488,7 @@ function initCentralTendencyViz() {
474
  }
475
  });
476
  }
477
-
478
  draw();
479
  }
480
 
@@ -483,19 +497,19 @@ function initializeAllVisualizations() {
483
  // Statistics visualizations
484
  initPopulationSampleViz();
485
  initCentralTendencyViz();
486
-
487
  // Linear Algebra visualizations
488
  initVectorCanvas();
489
  initSpanCanvas();
490
  initTransformationGrid();
491
  initEigenvectorCanvas();
492
-
493
  // Calculus visualizations
494
  initCircleAreaCanvas();
495
  initDerivativeCanvas();
496
  initRiemannSumCanvas();
497
  initTaylorSeriesCanvas();
498
-
499
  // Data Science visualizations
500
  initSimpleRegressionCanvas();
501
  initLogisticRegressionCanvas();
@@ -503,7 +517,7 @@ function initializeAllVisualizations() {
503
  initPCACanvas();
504
  initGradientDescentCanvas();
505
  initLossLandscapeCanvas();
506
-
507
  // Machine Learning visualizations
508
  initMLLinearRegressionCanvas();
509
  initMLKMeansCanvas();
@@ -518,32 +532,32 @@ function initializeAllVisualizations() {
518
  function initMLLinearRegressionCanvas() {
519
  const canvas = document.getElementById('canvas-ml-1');
520
  if (!canvas) return;
521
-
522
  const ctx = canvas.getContext('2d');
523
  let showLine = false;
524
-
525
  // House price data from worked example
526
  const data = [
527
- {x: 1000, y: 150},
528
- {x: 1500, y: 200},
529
- {x: 2000, y: 250},
530
- {x: 3000, y: 350}
531
  ];
532
-
533
  function draw() {
534
  clearCanvas(ctx, canvas);
535
-
536
  const padding = 80;
537
  const width = canvas.width - 2 * padding;
538
  const height = canvas.height - 2 * padding;
539
-
540
  const maxX = 3500;
541
  const maxY = 400;
542
-
543
  // Draw axes
544
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
545
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
546
-
547
  // Draw grid
548
  for (let i = 0; i <= 7; i++) {
549
  const x = padding + (i / 7) * width;
@@ -551,14 +565,14 @@ function initMLLinearRegressionCanvas() {
551
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
552
  drawText(ctx, xVal, x, canvas.height - padding + 20, 10, COLORS.textSecondary);
553
  }
554
-
555
  for (let i = 0; i <= 8; i++) {
556
  const y = canvas.height - padding - (i / 8) * height;
557
  const yVal = (i * 50).toString();
558
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
559
  drawText(ctx, yVal, padding - 15, y + 4, 10, COLORS.textSecondary, 'right');
560
  }
561
-
562
  // Draw labels
563
  drawText(ctx, 'Size (sq ft)', canvas.width / 2, canvas.height - 10, 12, COLORS.cyan);
564
  ctx.save();
@@ -566,7 +580,7 @@ function initMLLinearRegressionCanvas() {
566
  ctx.rotate(-Math.PI / 2);
567
  drawText(ctx, 'Price ($1000s)', 0, 0, 12, COLORS.cyan);
568
  ctx.restore();
569
-
570
  // Draw data points
571
  data.forEach(point => {
572
  const px = padding + (point.x / maxX) * width;
@@ -574,29 +588,29 @@ function initMLLinearRegressionCanvas() {
574
  drawCircle(ctx, px, py, 8, COLORS.cyan);
575
  drawText(ctx, `${point.y}k`, px + 15, py - 10, 10, COLORS.cyan, 'left');
576
  });
577
-
578
  // Draw regression line if enabled
579
  if (showLine) {
580
  // From worked example: y = 50 + 0.1x
581
  const slope = 0.1;
582
  const intercept = 50;
583
-
584
  const x1 = 0;
585
  const y1 = intercept;
586
  const x2 = maxX;
587
  const y2 = intercept + slope * x2;
588
-
589
  const px1 = padding + (x1 / maxX) * width;
590
  const py1 = canvas.height - padding - (y1 / maxY) * height;
591
  const px2 = padding + (x2 / maxX) * width;
592
  const py2 = canvas.height - padding - (y2 / maxY) * height;
593
-
594
  drawLine(ctx, px1, py1, px2, py2, COLORS.orange, 3);
595
-
596
  // Show equation
597
  drawText(ctx, 'y = 50 + 0.10x', canvas.width / 2, 30, 16, COLORS.orange);
598
  drawText(ctx, 'R² = 1.00 (Perfect Fit!)', canvas.width / 2, 50, 14, COLORS.green);
599
-
600
  // Highlight prediction point (2500, 300)
601
  const predX = 2500;
602
  const predY = 50 + 0.1 * predX;
@@ -606,62 +620,62 @@ function initMLLinearRegressionCanvas() {
606
  drawText(ctx, '2500 sq ft → $300k', ppx - 80, ppy - 15, 12, COLORS.green, 'left');
607
  }
608
  }
609
-
610
  const fitBtn = document.getElementById('btn-ml-1-fit');
611
  const resetBtn = document.getElementById('btn-ml-1-reset');
612
-
613
  if (fitBtn) {
614
  fitBtn.addEventListener('click', () => {
615
  showLine = true;
616
  draw();
617
  });
618
  }
619
-
620
  if (resetBtn) {
621
  resetBtn.addEventListener('click', () => {
622
  showLine = false;
623
  draw();
624
  });
625
  }
626
-
627
  draw();
628
  }
629
 
630
  function initMLKMeansCanvas() {
631
  const canvas = document.getElementById('canvas-ml-15');
632
  if (!canvas) return;
633
-
634
  const ctx = canvas.getContext('2d');
635
  let clustered = false;
636
-
637
  // Customer data from worked example
638
  const customers = [
639
- {name: 'A', age: 25, income: 40, cluster: null},
640
- {name: 'B', age: 30, income: 50, cluster: null},
641
- {name: 'C', age: 28, income: 45, cluster: null},
642
- {name: 'D', age: 55, income: 80, cluster: null},
643
- {name: 'E', age: 60, income: 90, cluster: null},
644
- {name: 'F', age: 52, income: 75, cluster: null}
645
  ];
646
-
647
  let centroids = [
648
- {age: 25, income: 40, color: COLORS.cyan},
649
- {age: 60, income: 90, color: COLORS.orange}
650
  ];
651
-
652
  function assignClusters() {
653
  customers.forEach(customer => {
654
  // Calculate distance to each centroid
655
  const d1 = Math.sqrt(Math.pow(customer.age - centroids[0].age, 2) + Math.pow(customer.income - centroids[0].income, 2));
656
  const d2 = Math.sqrt(Math.pow(customer.age - centroids[1].age, 2) + Math.pow(customer.income - centroids[1].income, 2));
657
-
658
  customer.cluster = d1 < d2 ? 0 : 1;
659
  });
660
-
661
  // Update centroids
662
  const cluster0 = customers.filter(c => c.cluster === 0);
663
  const cluster1 = customers.filter(c => c.cluster === 1);
664
-
665
  if (cluster0.length > 0) {
666
  centroids[0].age = cluster0.reduce((s, c) => s + c.age, 0) / cluster0.length;
667
  centroids[0].income = cluster0.reduce((s, c) => s + c.income, 0) / cluster0.length;
@@ -671,34 +685,34 @@ function initMLKMeansCanvas() {
671
  centroids[1].income = cluster1.reduce((s, c) => s + c.income, 0) / cluster1.length;
672
  }
673
  }
674
-
675
  function draw() {
676
  clearCanvas(ctx, canvas);
677
-
678
  const padding = 80;
679
  const width = canvas.width - 2 * padding;
680
  const height = canvas.height - 2 * padding;
681
-
682
  const minAge = 20, maxAge = 70;
683
  const minIncome = 30, maxIncome = 100;
684
-
685
  // Draw axes
686
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
687
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
688
-
689
  // Draw grid
690
  for (let age = 20; age <= 70; age += 10) {
691
  const x = padding + ((age - minAge) / (maxAge - minAge)) * width;
692
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
693
  drawText(ctx, age.toString(), x, canvas.height - padding + 20, 10, COLORS.textSecondary);
694
  }
695
-
696
  for (let income = 30; income <= 100; income += 10) {
697
  const y = canvas.height - padding - ((income - minIncome) / (maxIncome - minIncome)) * height;
698
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
699
  drawText(ctx, `$${income}k`, padding - 40, y + 4, 10, COLORS.textSecondary, 'right');
700
  }
701
-
702
  // Draw labels
703
  drawText(ctx, 'Age', canvas.width / 2, canvas.height - 10, 12, COLORS.cyan);
704
  ctx.save();
@@ -706,23 +720,23 @@ function initMLKMeansCanvas() {
706
  ctx.rotate(-Math.PI / 2);
707
  drawText(ctx, 'Income ($k)', 0, 0, 12, COLORS.cyan);
708
  ctx.restore();
709
-
710
  // Draw customers
711
  customers.forEach(customer => {
712
  const px = padding + ((customer.age - minAge) / (maxAge - minAge)) * width;
713
  const py = canvas.height - padding - ((customer.income - minIncome) / (maxIncome - minIncome)) * height;
714
-
715
  const color = clustered ? (customer.cluster === 0 ? COLORS.cyan : COLORS.orange) : COLORS.primary;
716
  drawCircle(ctx, px, py, 10, color);
717
  drawText(ctx, customer.name, px, py - 15, 12, COLORS.text);
718
  });
719
-
720
  // Draw centroids if clustered
721
  if (clustered) {
722
  centroids.forEach((centroid, i) => {
723
  const cx = padding + ((centroid.age - minAge) / (maxAge - minAge)) * width;
724
  const cy = canvas.height - padding - ((centroid.income - minIncome) / (maxIncome - minIncome)) * height;
725
-
726
  // Draw X marker for centroid
727
  ctx.strokeStyle = centroid.color;
728
  ctx.lineWidth = 4;
@@ -732,19 +746,19 @@ function initMLKMeansCanvas() {
732
  ctx.moveTo(cx + 12, cy - 12);
733
  ctx.lineTo(cx - 12, cy + 12);
734
  ctx.stroke();
735
-
736
- drawText(ctx, `C${i+1} [${centroid.age.toFixed(1)}, ${centroid.income.toFixed(1)}]`,
737
- cx + 20, cy, 11, centroid.color, 'left');
738
  });
739
-
740
  drawText(ctx, 'Cluster 1 (Young, Lower Income)', 150, 30, 12, COLORS.cyan);
741
  drawText(ctx, 'Cluster 2 (Mature, Higher Income)', 150, 50, 12, COLORS.orange);
742
  }
743
  }
744
-
745
  const clusterBtn = document.getElementById('btn-ml-15-cluster');
746
  const resetBtn = document.getElementById('btn-ml-15-reset');
747
-
748
  if (clusterBtn) {
749
  clusterBtn.addEventListener('click', () => {
750
  clustered = true;
@@ -752,19 +766,19 @@ function initMLKMeansCanvas() {
752
  draw();
753
  });
754
  }
755
-
756
  if (resetBtn) {
757
  resetBtn.addEventListener('click', () => {
758
  clustered = false;
759
  customers.forEach(c => c.cluster = null);
760
  centroids = [
761
- {age: 25, income: 40, color: COLORS.cyan},
762
- {age: 60, income: 90, color: COLORS.orange}
763
  ];
764
  draw();
765
  });
766
  }
767
-
768
  draw();
769
  }
770
 
@@ -773,21 +787,21 @@ function initMLKMeansCanvas() {
773
  function initVectorCanvas() {
774
  const canvas = document.getElementById('canvas-42');
775
  if (!canvas) return;
776
-
777
  const ctx = canvas.getContext('2d');
778
  let vx = 3, vy = 2;
779
-
780
  function draw() {
781
  clearCanvas(ctx, canvas);
782
-
783
  const centerX = canvas.width / 2;
784
  const centerY = canvas.height / 2;
785
  const scale = 40;
786
-
787
  // Draw axes
788
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
789
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
790
-
791
  // Draw grid
792
  ctx.strokeStyle = '#333';
793
  ctx.lineWidth = 0.5;
@@ -797,14 +811,14 @@ function initVectorCanvas() {
797
  drawLine(ctx, 0, centerY + i * scale, canvas.width, centerY + i * scale, '#333', 0.5);
798
  }
799
  }
800
-
801
  // Draw vector
802
  const endX = centerX + vx * scale;
803
  const endY = centerY - vy * scale;
804
-
805
  // Arrow shaft
806
  drawLine(ctx, centerX, centerY, endX, endY, COLORS.cyan, 3);
807
-
808
  // Arrow head
809
  const angle = Math.atan2(vy, vx);
810
  const arrowSize = 15;
@@ -815,20 +829,20 @@ function initVectorCanvas() {
815
  ctx.closePath();
816
  ctx.fillStyle = COLORS.cyan;
817
  ctx.fill();
818
-
819
  // Draw vector label
820
  drawText(ctx, `v = [${vx.toFixed(1)}, ${vy.toFixed(1)}]`, endX + 20, endY - 10, 14, COLORS.cyan, 'left');
821
-
822
  // Draw magnitude
823
  const magnitude = Math.sqrt(vx * vx + vy * vy);
824
  drawText(ctx, `|v| = ${magnitude.toFixed(2)}`, canvas.width / 2, 30, 14, COLORS.text);
825
  }
826
-
827
  const sliderX = document.getElementById('slider42x');
828
  const sliderY = document.getElementById('slider42y');
829
  const labelX = document.getElementById('vec42x');
830
  const labelY = document.getElementById('vec42y');
831
-
832
  if (sliderX) {
833
  sliderX.addEventListener('input', (e) => {
834
  vx = parseFloat(e.target.value);
@@ -836,7 +850,7 @@ function initVectorCanvas() {
836
  draw();
837
  });
838
  }
839
-
840
  if (sliderY) {
841
  sliderY.addEventListener('input', (e) => {
842
  vy = parseFloat(e.target.value);
@@ -844,7 +858,7 @@ function initVectorCanvas() {
844
  draw();
845
  });
846
  }
847
-
848
  const resetBtn = document.getElementById('btn42reset');
849
  if (resetBtn) {
850
  resetBtn.addEventListener('click', () => {
@@ -856,37 +870,37 @@ function initVectorCanvas() {
856
  draw();
857
  });
858
  }
859
-
860
  draw();
861
  }
862
 
863
  function initSpanCanvas() {
864
  const canvas = document.getElementById('canvas-43');
865
  if (!canvas) return;
866
-
867
  const ctx = canvas.getContext('2d');
868
  let animating = false;
869
  let t = 0;
870
-
871
  function draw() {
872
  clearCanvas(ctx, canvas);
873
-
874
  const centerX = canvas.width / 2;
875
  const centerY = canvas.height / 2;
876
  const scale = 50;
877
-
878
  // Draw axes
879
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
880
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
881
-
882
  // Basis vectors
883
  const v1 = { x: 2, y: 1 };
884
  const v2 = { x: -1, y: 1.5 };
885
-
886
  // Draw basis vectors
887
  drawLine(ctx, centerX, centerY, centerX + v1.x * scale, centerY - v1.y * scale, COLORS.cyan, 3);
888
  drawLine(ctx, centerX, centerY, centerX + v2.x * scale, centerY - v2.y * scale, COLORS.orange, 3);
889
-
890
  if (animating) {
891
  // Draw span (multiple linear combinations)
892
  ctx.globalAlpha = 0.3;
@@ -899,78 +913,78 @@ function initSpanCanvas() {
899
  }
900
  ctx.globalAlpha = 1;
901
  }
902
-
903
  drawText(ctx, 'v₁', centerX + v1.x * scale + 20, centerY - v1.y * scale, 16, COLORS.cyan);
904
  drawText(ctx, 'v₂', centerX + v2.x * scale - 20, centerY - v2.y * scale, 16, COLORS.orange);
905
  }
906
-
907
  const animateBtn = document.getElementById('btn43animate');
908
  const resetBtn = document.getElementById('btn43reset');
909
-
910
  if (animateBtn) {
911
  animateBtn.addEventListener('click', () => {
912
  animating = true;
913
  draw();
914
  });
915
  }
916
-
917
  if (resetBtn) {
918
  resetBtn.addEventListener('click', () => {
919
  animating = false;
920
  draw();
921
  });
922
  }
923
-
924
  draw();
925
  }
926
 
927
  function initTransformationGrid() {
928
  const canvas = document.getElementById('canvas-44');
929
  if (!canvas) return;
930
-
931
  const ctx = canvas.getContext('2d');
932
  let matrix = [[1, 0], [0, 1]]; // Identity
933
-
934
  function drawGrid(transform = false) {
935
  clearCanvas(ctx, canvas);
936
-
937
  const centerX = canvas.width / 2;
938
  const centerY = canvas.height / 2;
939
  const scale = 40;
940
  const gridSize = 5;
941
-
942
  ctx.strokeStyle = transform ? COLORS.cyan : '#555';
943
  ctx.lineWidth = 1;
944
-
945
  // Draw grid lines
946
  for (let i = -gridSize; i <= gridSize; i++) {
947
  for (let j = -gridSize; j <= gridSize; j++) {
948
  let x1 = i, y1 = j;
949
  let x2 = i + 1, y2 = j;
950
  let x3 = i, y3 = j + 1;
951
-
952
  if (transform) {
953
  [x1, y1] = [matrix[0][0] * i + matrix[0][1] * j, matrix[1][0] * i + matrix[1][1] * j];
954
  [x2, y2] = [matrix[0][0] * (i + 1) + matrix[0][1] * j, matrix[1][0] * (i + 1) + matrix[1][1] * j];
955
  [x3, y3] = [matrix[0][0] * i + matrix[0][1] * (j + 1), matrix[1][0] * i + matrix[1][1] * (j + 1)];
956
  }
957
-
958
  drawLine(ctx, centerX + x1 * scale, centerY - y1 * scale, centerX + x2 * scale, centerY - y2 * scale, ctx.strokeStyle, 1);
959
  drawLine(ctx, centerX + x1 * scale, centerY - y1 * scale, centerX + x3 * scale, centerY - y3 * scale, ctx.strokeStyle, 1);
960
  }
961
  }
962
-
963
  // Draw i-hat and j-hat
964
  const iHat = transform ? [matrix[0][0], matrix[1][0]] : [1, 0];
965
  const jHat = transform ? [matrix[0][1], matrix[1][1]] : [0, 1];
966
-
967
  drawLine(ctx, centerX, centerY, centerX + iHat[0] * scale, centerY - iHat[1] * scale, COLORS.orange, 3);
968
  drawLine(ctx, centerX, centerY, centerX + jHat[0] * scale, centerY - jHat[1] * scale, COLORS.green, 3);
969
  }
970
-
971
  const select = document.getElementById('select44');
972
  const applyBtn = document.getElementById('btn44apply');
973
-
974
  if (applyBtn && select) {
975
  applyBtn.addEventListener('click', () => {
976
  const type = select.value;
@@ -990,34 +1004,34 @@ function initTransformationGrid() {
990
  drawGrid(true);
991
  });
992
  }
993
-
994
  drawGrid(false);
995
  }
996
 
997
  function initEigenvectorCanvas() {
998
  const canvas = document.getElementById('canvas-54');
999
  if (!canvas) return;
1000
-
1001
  const ctx = canvas.getContext('2d');
1002
  let transformed = false;
1003
-
1004
  function draw() {
1005
  clearCanvas(ctx, canvas);
1006
-
1007
  const centerX = canvas.width / 2;
1008
  const centerY = canvas.height / 2;
1009
  const scale = 50;
1010
-
1011
  // Draw axes
1012
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
1013
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
1014
-
1015
  // Matrix [[2, 0], [0, 1]] - scaling transformation
1016
  // Eigenvectors: [1, 0] with eigenvalue 2, [0, 1] with eigenvalue 1
1017
-
1018
  const e1Scale = transformed ? 2 : 1;
1019
  const e2Scale = 1;
1020
-
1021
  // Draw regular vectors (affected)
1022
  const regularVecs = [[2, 2], [1, 2], [-2, 1]];
1023
  regularVecs.forEach(([x, y]) => {
@@ -1025,32 +1039,32 @@ function initEigenvectorCanvas() {
1025
  const endY = centerY - y * scale;
1026
  drawLine(ctx, centerX, centerY, endX, endY, '#666', 2);
1027
  });
1028
-
1029
  // Draw eigenvectors (special - stay on their line)
1030
  drawLine(ctx, centerX, centerY, centerX + e1Scale * 2 * scale, centerY, COLORS.cyan, 4);
1031
  drawLine(ctx, centerX, centerY, centerX, centerY - 2 * scale, COLORS.orange, 4);
1032
-
1033
  drawText(ctx, 'Eigenvector 1 (λ=2)', centerX + e1Scale * 2 * scale + 10, centerY - 10, 12, COLORS.cyan, 'left');
1034
  drawText(ctx, 'Eigenvector 2 (λ=1)', centerX + 10, centerY - 2 * scale - 10, 12, COLORS.orange, 'left');
1035
  }
1036
-
1037
  const transformBtn = document.getElementById('btn54transform');
1038
  const resetBtn = document.getElementById('btn54reset');
1039
-
1040
  if (transformBtn) {
1041
  transformBtn.addEventListener('click', () => {
1042
  transformed = true;
1043
  draw();
1044
  });
1045
  }
1046
-
1047
  if (resetBtn) {
1048
  resetBtn.addEventListener('click', () => {
1049
  transformed = false;
1050
  draw();
1051
  });
1052
  }
1053
-
1054
  draw();
1055
  }
1056
 
@@ -1059,18 +1073,18 @@ function initEigenvectorCanvas() {
1059
  function initCircleAreaCanvas() {
1060
  const canvas = document.getElementById('canvas-58');
1061
  if (!canvas) return;
1062
-
1063
  const ctx = canvas.getContext('2d');
1064
  let unwrapping = false;
1065
  let progress = 0;
1066
-
1067
  function draw() {
1068
  clearCanvas(ctx, canvas);
1069
-
1070
  const centerX = canvas.width / 4;
1071
  const centerY = canvas.height / 2;
1072
  const radius = 100;
1073
-
1074
  if (!unwrapping) {
1075
  // Draw circle
1076
  drawCircle(ctx, centerX, centerY, radius, COLORS.cyan, false);
@@ -1084,22 +1098,22 @@ function initCircleAreaCanvas() {
1084
  const rectY = centerY - r;
1085
  const rectHeight = 2 * r;
1086
  const dr = radius / rings;
1087
-
1088
  ctx.fillStyle = COLORS.cyan;
1089
  ctx.globalAlpha = 0.6;
1090
  ctx.fillRect(rectX, rectY, 2, rectHeight);
1091
  }
1092
  ctx.globalAlpha = 1;
1093
-
1094
  if (progress >= 0.99) {
1095
  drawText(ctx, 'Integrated! Area = πr²', canvas.width * 0.7, centerY + 50, 14, COLORS.green);
1096
  }
1097
  }
1098
  }
1099
-
1100
  const animateBtn = document.getElementById('btn58animate');
1101
  const resetBtn = document.getElementById('btn58reset');
1102
-
1103
  if (animateBtn) {
1104
  animateBtn.addEventListener('click', () => {
1105
  unwrapping = true;
@@ -1113,7 +1127,7 @@ function initCircleAreaCanvas() {
1113
  }, 50);
1114
  });
1115
  }
1116
-
1117
  if (resetBtn) {
1118
  resetBtn.addEventListener('click', () => {
1119
  unwrapping = false;
@@ -1121,32 +1135,32 @@ function initCircleAreaCanvas() {
1121
  draw();
1122
  });
1123
  }
1124
-
1125
  draw();
1126
  }
1127
 
1128
  function initDerivativeCanvas() {
1129
  const canvas = document.getElementById('canvas-59');
1130
  if (!canvas) return;
1131
-
1132
  const ctx = canvas.getContext('2d');
1133
  let dx = 1.0;
1134
-
1135
  function f(x) {
1136
  return 0.02 * x * x;
1137
  }
1138
-
1139
  function draw() {
1140
  clearCanvas(ctx, canvas);
1141
-
1142
  const scale = 50;
1143
  const centerX = canvas.width / 2;
1144
  const centerY = canvas.height * 0.8;
1145
-
1146
  // Draw axes
1147
  drawLine(ctx, 50, centerY, canvas.width - 50, centerY, '#555', 1);
1148
  drawLine(ctx, centerX, 50, centerX, canvas.height - 50, '#555', 1);
1149
-
1150
  // Draw function
1151
  ctx.beginPath();
1152
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
@@ -1161,25 +1175,25 @@ function initDerivativeCanvas() {
1161
  ctx.strokeStyle = COLORS.cyan;
1162
  ctx.lineWidth = 2;
1163
  ctx.stroke();
1164
-
1165
  // Draw secant line
1166
  const x0 = 0;
1167
  const x1 = dx * scale;
1168
  const y0 = centerY - f(x0) * scale;
1169
  const y1 = centerY - f(x1) * scale;
1170
-
1171
  drawLine(ctx, centerX + x0 - 50, y0 - (y1 - y0) / x1 * 50, centerX + x1 + 50, y1 + (y1 - y0) / x1 * 50, COLORS.orange, 2);
1172
  drawCircle(ctx, centerX + x0, y0, 5, COLORS.green);
1173
  drawCircle(ctx, centerX + x1, y1, 5, COLORS.green);
1174
-
1175
  const slope = (f(x1 / scale) - f(0)) / (x1 / scale);
1176
  drawText(ctx, `Slope = ${slope.toFixed(3)}`, canvas.width / 2, 40, 14, COLORS.orange);
1177
  drawText(ctx, `As Δx → 0, slope → derivative`, canvas.width / 2, 60, 12, COLORS.text);
1178
  }
1179
-
1180
  const slider = document.getElementById('slider59dx');
1181
  const label = document.getElementById('label59dx');
1182
-
1183
  if (slider) {
1184
  slider.addEventListener('input', (e) => {
1185
  dx = parseFloat(e.target.value);
@@ -1187,32 +1201,32 @@ function initDerivativeCanvas() {
1187
  draw();
1188
  });
1189
  }
1190
-
1191
  draw();
1192
  }
1193
 
1194
  function initRiemannSumCanvas() {
1195
  const canvas = document.getElementById('canvas-64');
1196
  if (!canvas) return;
1197
-
1198
  const ctx = canvas.getContext('2d');
1199
  let n = 8;
1200
-
1201
  function f(x) {
1202
  return 50 + 50 * Math.sin(x / 30);
1203
  }
1204
-
1205
  function draw() {
1206
  clearCanvas(ctx, canvas);
1207
-
1208
  const padding = 50;
1209
  const width = canvas.width - 2 * padding;
1210
  const height = canvas.height - 2 * padding;
1211
-
1212
  // Draw axes
1213
  drawLine(ctx, padding, height + padding, canvas.width - padding, height + padding, '#555', 2);
1214
  drawLine(ctx, padding, padding, padding, height + padding, '#555', 2);
1215
-
1216
  // Draw function
1217
  ctx.beginPath();
1218
  for (let x = 0; x <= width; x += 2) {
@@ -1227,16 +1241,16 @@ function initRiemannSumCanvas() {
1227
  ctx.strokeStyle = COLORS.cyan;
1228
  ctx.lineWidth = 3;
1229
  ctx.stroke();
1230
-
1231
  // Draw rectangles
1232
  const rectWidth = width / n;
1233
  let totalArea = 0;
1234
-
1235
  for (let i = 0; i < n; i++) {
1236
  const x = i * rectWidth;
1237
  const rectHeight = f(x);
1238
  totalArea += rectWidth * rectHeight;
1239
-
1240
  ctx.fillStyle = COLORS.orange;
1241
  ctx.globalAlpha = 0.5;
1242
  ctx.fillRect(padding + x, height + padding - rectHeight, rectWidth, rectHeight);
@@ -1244,14 +1258,14 @@ function initRiemannSumCanvas() {
1244
  ctx.globalAlpha = 1;
1245
  ctx.strokeRect(padding + x, height + padding - rectHeight, rectWidth, rectHeight);
1246
  }
1247
-
1248
  drawText(ctx, `Rectangles: ${n}`, canvas.width / 2, 30, 14, COLORS.text);
1249
  drawText(ctx, `Approximate Area: ${(totalArea / 100).toFixed(2)}`, canvas.width / 2, 50, 12, COLORS.orange);
1250
  }
1251
-
1252
  const slider = document.getElementById('slider64n');
1253
  const label = document.getElementById('label64n');
1254
-
1255
  if (slider) {
1256
  slider.addEventListener('input', (e) => {
1257
  n = parseInt(e.target.value);
@@ -1259,29 +1273,29 @@ function initRiemannSumCanvas() {
1259
  draw();
1260
  });
1261
  }
1262
-
1263
  draw();
1264
  }
1265
 
1266
  function initTaylorSeriesCanvas() {
1267
  const canvas = document.getElementById('canvas-68');
1268
  if (!canvas) return;
1269
-
1270
  const ctx = canvas.getContext('2d');
1271
  let degree = 1;
1272
  let func = 'sin';
1273
-
1274
  function draw() {
1275
  clearCanvas(ctx, canvas);
1276
-
1277
  const scale = 50;
1278
  const centerX = canvas.width / 2;
1279
  const centerY = canvas.height / 2;
1280
-
1281
  // Draw axes
1282
  drawLine(ctx, 50, centerY, canvas.width - 50, centerY, '#555', 1);
1283
  drawLine(ctx, centerX, 50, centerX, canvas.height - 50, '#555', 1);
1284
-
1285
  // Draw actual function
1286
  ctx.beginPath();
1287
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
@@ -1290,7 +1304,7 @@ function initTaylorSeriesCanvas() {
1290
  if (func === 'sin') y = Math.sin(t);
1291
  else if (func === 'cos') y = Math.cos(t);
1292
  else y = Math.exp(t);
1293
-
1294
  const px = centerX + x;
1295
  const py = centerY - y * scale;
1296
  if (x === -canvas.width / 2) {
@@ -1302,13 +1316,13 @@ function initTaylorSeriesCanvas() {
1302
  ctx.strokeStyle = COLORS.cyan;
1303
  ctx.lineWidth = 2;
1304
  ctx.stroke();
1305
-
1306
  // Draw Taylor approximation
1307
  ctx.beginPath();
1308
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
1309
  const t = x / scale;
1310
  let y = 0;
1311
-
1312
  if (func === 'sin') {
1313
  for (let n = 0; n <= degree; n++) {
1314
  const term = Math.pow(-1, n) * Math.pow(t, 2 * n + 1) / factorial(2 * n + 1);
@@ -1324,7 +1338,7 @@ function initTaylorSeriesCanvas() {
1324
  y += Math.pow(t, n) / factorial(n);
1325
  }
1326
  }
1327
-
1328
  const px = centerX + x;
1329
  const py = centerY - y * scale;
1330
  if (x === -canvas.width / 2) {
@@ -1336,20 +1350,20 @@ function initTaylorSeriesCanvas() {
1336
  ctx.strokeStyle = COLORS.orange;
1337
  ctx.lineWidth = 3;
1338
  ctx.stroke();
1339
-
1340
  drawText(ctx, `Function: ${func}(x)`, canvas.width / 2, 30, 14, COLORS.cyan);
1341
  drawText(ctx, `Taylor degree: ${degree}`, canvas.width / 2, 50, 14, COLORS.orange);
1342
  }
1343
-
1344
  function factorial(n) {
1345
  if (n <= 1) return 1;
1346
  return n * factorial(n - 1);
1347
  }
1348
-
1349
  const slider = document.getElementById('slider68degree');
1350
  const label = document.getElementById('label68degree');
1351
  const select = document.getElementById('select68func');
1352
-
1353
  if (slider) {
1354
  slider.addEventListener('input', (e) => {
1355
  degree = parseInt(e.target.value);
@@ -1357,14 +1371,14 @@ function initTaylorSeriesCanvas() {
1357
  draw();
1358
  });
1359
  }
1360
-
1361
  if (select) {
1362
  select.addEventListener('change', (e) => {
1363
  func = e.target.value;
1364
  draw();
1365
  });
1366
  }
1367
-
1368
  draw();
1369
  }
1370
 
@@ -1376,7 +1390,7 @@ function initInteractiveElements() {
1376
 
1377
  // ===== HELPER FUNCTIONS =====
1378
  function generateRandomData(count, min, max) {
1379
- return Array.from({ length: count }, () =>
1380
  Math.floor(Math.random() * (max - min + 1)) + min
1381
  );
1382
  }
@@ -1390,12 +1404,12 @@ function startAnimation(canvasId, animationFunction) {
1390
  if (animationFrames[canvasId]) {
1391
  cancelAnimationFrame(animationFrames[canvasId]);
1392
  }
1393
-
1394
  function animate() {
1395
  animationFunction();
1396
  animationFrames[canvasId] = requestAnimationFrame(animate);
1397
  }
1398
-
1399
  animate();
1400
  }
1401
 
@@ -1413,160 +1427,160 @@ function stopAnimation(canvasId) {
1413
  function initMLSVMCanvas() {
1414
  const canvas = document.getElementById('canvas-ml-9');
1415
  if (!canvas) return;
1416
-
1417
  const ctx = canvas.getContext('2d');
1418
-
1419
  // Generate two-class data
1420
- const class1 = Array.from({length: 20}, () => ({
1421
  x: Math.random() * 100 + 50,
1422
  y: Math.random() * 100 + 50
1423
  }));
1424
- const class2 = Array.from({length: 20}, () => ({
1425
  x: Math.random() * 100 + 200,
1426
  y: Math.random() * 100 + 200
1427
  }));
1428
-
1429
  function draw() {
1430
  clearCanvas(ctx, canvas);
1431
-
1432
  const padding = 50;
1433
-
1434
  // Draw decision boundary (simplified)
1435
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, padding, COLORS.orange, 3);
1436
- drawText(ctx, 'Decision Boundary', canvas.width/2, 30, 14, COLORS.orange);
1437
-
1438
  // Draw margin lines
1439
  drawLine(ctx, padding, canvas.height - padding - 30, canvas.width - padding, padding - 30, COLORS.green, 1);
1440
  drawLine(ctx, padding, canvas.height - padding + 30, canvas.width - padding, padding + 30, COLORS.green, 1);
1441
- drawText(ctx, 'Maximum Margin', canvas.width/2, 50, 12, COLORS.green);
1442
-
1443
  // Draw data points
1444
  class1.forEach(p => drawCircle(ctx, p.x, p.y, 6, COLORS.cyan));
1445
  class2.forEach(p => drawCircle(ctx, p.x, p.y, 6, COLORS.primary));
1446
  }
1447
-
1448
  draw();
1449
  }
1450
 
1451
  function initMLRandomForestCanvas() {
1452
  const canvas = document.getElementById('canvas-ml-12');
1453
  if (!canvas) return;
1454
-
1455
  const ctx = canvas.getContext('2d');
1456
-
1457
  function draw() {
1458
  clearCanvas(ctx, canvas);
1459
-
1460
- drawText(ctx, 'Random Forest: Ensemble of Decision Trees', canvas.width/2, 50, 16, COLORS.cyan);
1461
-
1462
  // Draw multiple trees
1463
  const treeCount = 5;
1464
  const treeWidth = (canvas.width - 100) / treeCount;
1465
-
1466
  for (let i = 0; i < treeCount; i++) {
1467
- const x = 50 + i * treeWidth + treeWidth/2;
1468
  const y = 100;
1469
-
1470
  // Draw simple tree structure
1471
  drawLine(ctx, x, y, x - 30, y + 60, COLORS.green, 2);
1472
  drawLine(ctx, x, y, x + 30, y + 60, COLORS.green, 2);
1473
  drawCircle(ctx, x, y, 8, COLORS.cyan);
1474
  drawCircle(ctx, x - 30, y + 60, 6, COLORS.orange);
1475
  drawCircle(ctx, x + 30, y + 60, 6, COLORS.orange);
1476
-
1477
- drawText(ctx, `Tree ${i+1}`, x, y + 100, 12, COLORS.text);
1478
  }
1479
-
1480
  // Draw voting arrow
1481
- drawLine(ctx, canvas.width/2, 200, canvas.width/2, 280, COLORS.orange, 3);
1482
- drawText(ctx, '↓ Majority Vote', canvas.width/2 + 10, 250, 14, COLORS.orange, 'left');
1483
-
1484
- drawRect(ctx, canvas.width/2 - 80, 280, 160, 40, COLORS.green);
1485
- drawText(ctx, 'Final Prediction', canvas.width/2, 305, 14, '#000');
1486
  }
1487
-
1488
  draw();
1489
  }
1490
 
1491
  function initMLGradientBoostingCanvas() {
1492
  const canvas = document.getElementById('canvas-ml-13');
1493
  if (!canvas) return;
1494
-
1495
  const ctx = canvas.getContext('2d');
1496
-
1497
  function draw() {
1498
  clearCanvas(ctx, canvas);
1499
-
1500
- drawText(ctx, 'Gradient Boosting: Sequential Error Correction', canvas.width/2, 40, 16, COLORS.cyan);
1501
-
1502
  const stages = 4;
1503
  const stageWidth = (canvas.width - 100) / stages;
1504
-
1505
  for (let i = 0; i < stages; i++) {
1506
  const x = 50 + i * stageWidth;
1507
- const y = canvas.height/2;
1508
-
1509
  // Draw tree
1510
  drawRect(ctx, x, y - 40, stageWidth - 40, 80, i === 0 ? COLORS.cyan : COLORS.orange, false);
1511
- drawText(ctx, `Tree ${i+1}`, x + (stageWidth-40)/2, y, 12, COLORS.text);
1512
- drawText(ctx, i === 0 ? 'Base' : 'Fix Errors', x + (stageWidth-40)/2, y + 20, 10, COLORS.textSecondary);
1513
-
1514
  // Draw arrow
1515
  if (i < stages - 1) {
1516
  drawLine(ctx, x + stageWidth - 40, y, x + stageWidth, y, COLORS.green, 2);
1517
  drawText(ctx, '+', x + stageWidth - 20, y - 10, 16, COLORS.green);
1518
  }
1519
  }
1520
-
1521
- drawText(ctx, 'Each tree learns from previous mistakes', canvas.width/2, canvas.height - 30, 12, COLORS.text);
1522
  }
1523
-
1524
  draw();
1525
  }
1526
 
1527
  function initMLNeuralNetworkCanvas() {
1528
  const canvas = document.getElementById('canvas-ml-14');
1529
  if (!canvas) return;
1530
-
1531
  const ctx = canvas.getContext('2d');
1532
-
1533
  function draw() {
1534
  clearCanvas(ctx, canvas);
1535
-
1536
- drawText(ctx, 'Neural Network Architecture', canvas.width/2, 30, 16, COLORS.cyan);
1537
-
1538
  const layers = [3, 5, 4, 2]; // neurons per layer
1539
  const layerSpacing = (canvas.width - 100) / (layers.length - 1);
1540
-
1541
  // Draw connections
1542
  ctx.globalAlpha = 0.3;
1543
  for (let l = 0; l < layers.length - 1; l++) {
1544
  const x1 = 50 + l * layerSpacing;
1545
  const x2 = 50 + (l + 1) * layerSpacing;
1546
-
1547
  for (let i = 0; i < layers[l]; i++) {
1548
- const y1 = canvas.height/2 - (layers[l] - 1) * 15 + i * 30;
1549
  for (let j = 0; j < layers[l + 1]; j++) {
1550
- const y2 = canvas.height/2 - (layers[l + 1] - 1) * 15 + j * 30;
1551
  drawLine(ctx, x1, y1, x2, y2, COLORS.textSecondary, 1);
1552
  }
1553
  }
1554
  }
1555
  ctx.globalAlpha = 1;
1556
-
1557
  // Draw neurons
1558
  layers.forEach((count, l) => {
1559
  const x = 50 + l * layerSpacing;
1560
  for (let i = 0; i < count; i++) {
1561
- const y = canvas.height/2 - (count - 1) * 15 + i * 30;
1562
  drawCircle(ctx, x, y, 12, l === 0 ? COLORS.cyan : (l === layers.length - 1 ? COLORS.green : COLORS.orange));
1563
  }
1564
-
1565
  const layerNames = ['Input', 'Hidden 1', 'Hidden 2', 'Output'];
1566
  drawText(ctx, layerNames[l], x, canvas.height - 30, 12, COLORS.text);
1567
  });
1568
  }
1569
-
1570
  draw();
1571
  }
1572
 
@@ -1575,68 +1589,68 @@ function initMLNeuralNetworkCanvas() {
1575
  function initSimpleRegressionCanvas() {
1576
  const canvas = document.getElementById('canvas-70');
1577
  if (!canvas) return;
1578
-
1579
  const ctx = canvas.getContext('2d');
1580
  let showLine = false;
1581
-
1582
  // Sample data
1583
  const data = [
1584
- {x: 1, y: 2.1}, {x: 2, y: 4.2}, {x: 3, y: 5.8}, {x: 4, y: 8.1},
1585
- {x: 5, y: 10.3}, {x: 6, y: 12.1}, {x: 7, y: 13.9}, {x: 8, y: 16.2},
1586
- {x: 9, y: 18.1}, {x: 10, y: 20.0}
1587
  ];
1588
-
1589
  function calculateRegression() {
1590
  const n = data.length;
1591
  const sumX = data.reduce((s, p) => s + p.x, 0);
1592
  const sumY = data.reduce((s, p) => s + p.y, 0);
1593
  const sumXY = data.reduce((s, p) => s + p.x * p.y, 0);
1594
  const sumX2 = data.reduce((s, p) => s + p.x * p.x, 0);
1595
-
1596
  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
1597
  const intercept = (sumY - slope * sumX) / n;
1598
-
1599
  return { slope, intercept };
1600
  }
1601
-
1602
  function draw() {
1603
  clearCanvas(ctx, canvas);
1604
-
1605
  const padding = 60;
1606
  const width = canvas.width - 2 * padding;
1607
  const height = canvas.height - 2 * padding;
1608
-
1609
  const maxX = 11;
1610
  const maxY = 22;
1611
-
1612
  // Draw axes
1613
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1614
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1615
-
1616
  // Draw grid
1617
  for (let i = 0; i <= 10; i++) {
1618
  const x = padding + (i / maxX) * width;
1619
  const y = canvas.height - padding - (i * 2 / maxY) * height;
1620
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
1621
  drawText(ctx, i.toString(), x, canvas.height - padding + 20, 10, COLORS.textSecondary);
1622
-
1623
  if (i * 2 <= maxY) {
1624
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
1625
  drawText(ctx, (i * 2).toString(), padding - 15, y + 4, 10, COLORS.textSecondary, 'right');
1626
  }
1627
  }
1628
-
1629
  // Draw labels
1630
  drawText(ctx, 'X', canvas.width - padding + 20, canvas.height - padding + 4, 12, COLORS.cyan);
1631
  drawText(ctx, 'Y', padding - 4, padding - 20, 12, COLORS.cyan);
1632
-
1633
  // Draw data points
1634
  data.forEach(point => {
1635
  const x = padding + (point.x / maxX) * width;
1636
  const y = canvas.height - padding - (point.y / maxY) * height;
1637
  drawCircle(ctx, x, y, 6, COLORS.cyan);
1638
  });
1639
-
1640
  // Draw regression line
1641
  if (showLine) {
1642
  const { slope, intercept } = calculateRegression();
@@ -1644,14 +1658,14 @@ function initSimpleRegressionCanvas() {
1644
  const y1 = intercept;
1645
  const x2 = maxX;
1646
  const y2 = slope * x2 + intercept;
1647
-
1648
  const px1 = padding + (x1 / maxX) * width;
1649
  const py1 = canvas.height - padding - (y1 / maxY) * height;
1650
  const px2 = padding + (x2 / maxX) * width;
1651
  const py2 = canvas.height - padding - (y2 / maxY) * height;
1652
-
1653
  drawLine(ctx, px1, py1, px2, py2, COLORS.orange, 3);
1654
-
1655
  // Calculate R²
1656
  const meanY = data.reduce((s, p) => s + p.y, 0) / data.length;
1657
  let ssTot = 0, ssRes = 0;
@@ -1661,57 +1675,57 @@ function initSimpleRegressionCanvas() {
1661
  ssTot += Math.pow(point.y - meanY, 2);
1662
  });
1663
  const r2 = 1 - (ssRes / ssTot);
1664
-
1665
  drawText(ctx, `y = ${intercept.toFixed(2)} + ${slope.toFixed(2)}x`, canvas.width / 2, 30, 14, COLORS.orange);
1666
  drawText(ctx, `R² = ${r2.toFixed(4)}`, canvas.width / 2, 50, 14, COLORS.green);
1667
  } else {
1668
  drawText(ctx, 'Click "Fit Regression Line" to see the best fit', canvas.width / 2, 30, 14, COLORS.text);
1669
  }
1670
  }
1671
-
1672
  const fitBtn = document.getElementById('btn70fit');
1673
  const resetBtn = document.getElementById('btn70reset');
1674
-
1675
  if (fitBtn) {
1676
  fitBtn.addEventListener('click', () => {
1677
  showLine = true;
1678
  draw();
1679
  });
1680
  }
1681
-
1682
  if (resetBtn) {
1683
  resetBtn.addEventListener('click', () => {
1684
  showLine = false;
1685
  draw();
1686
  });
1687
  }
1688
-
1689
  draw();
1690
  }
1691
 
1692
  function initLogisticRegressionCanvas() {
1693
  const canvas = document.getElementById('canvas-72');
1694
  if (!canvas) return;
1695
-
1696
  const ctx = canvas.getContext('2d');
1697
  let threshold = 0.5;
1698
-
1699
  function sigmoid(z) {
1700
  return 1 / (1 + Math.exp(-z));
1701
  }
1702
-
1703
  function draw() {
1704
  clearCanvas(ctx, canvas);
1705
-
1706
  const padding = 60;
1707
  const width = canvas.width - 2 * padding;
1708
  const height = canvas.height - 2 * padding;
1709
-
1710
  // Draw axes
1711
  const centerY = canvas.height / 2;
1712
  drawLine(ctx, padding, centerY, canvas.width - padding, centerY, COLORS.text, 2);
1713
  drawLine(ctx, canvas.width / 2, padding, canvas.width / 2, canvas.height - padding, COLORS.text, 2);
1714
-
1715
  // Draw sigmoid curve
1716
  ctx.beginPath();
1717
  for (let x = -6; x <= 6; x += 0.1) {
@@ -1727,25 +1741,25 @@ function initLogisticRegressionCanvas() {
1727
  ctx.strokeStyle = COLORS.cyan;
1728
  ctx.lineWidth = 3;
1729
  ctx.stroke();
1730
-
1731
  // Draw threshold line
1732
  const thresholdY = canvas.height - padding - threshold * height;
1733
  drawLine(ctx, padding, thresholdY, canvas.width - padding, thresholdY, COLORS.orange, 2);
1734
  drawText(ctx, `Threshold = ${threshold.toFixed(2)}`, canvas.width - 100, thresholdY - 10, 12, COLORS.orange);
1735
-
1736
  // Draw labels
1737
  drawText(ctx, 'P(y=1) = σ(z) = 1/(1+e^(-z))', canvas.width / 2, 30, 14, COLORS.cyan);
1738
  drawText(ctx, 'Class 1 (if P ≥ threshold)', canvas.width - 150, thresholdY - 30, 12, COLORS.green);
1739
  drawText(ctx, 'Class 0 (if P < threshold)', canvas.width - 150, thresholdY + 30, 12, COLORS.textSecondary);
1740
-
1741
  // Draw axis labels
1742
  drawText(ctx, '0', canvas.width / 2 + 5, centerY + 20, 10, COLORS.text, 'left');
1743
  drawText(ctx, '1', padding - 20, padding + 10, 10, COLORS.text);
1744
  }
1745
-
1746
  const slider = document.getElementById('slider72');
1747
  const label = document.getElementById('label72');
1748
-
1749
  if (slider) {
1750
  slider.addEventListener('input', (e) => {
1751
  threshold = parseFloat(e.target.value);
@@ -1753,24 +1767,24 @@ function initLogisticRegressionCanvas() {
1753
  draw();
1754
  });
1755
  }
1756
-
1757
  draw();
1758
  }
1759
 
1760
  function initPolynomialRegressionCanvas() {
1761
  const canvas = document.getElementById('canvas-74');
1762
  if (!canvas) return;
1763
-
1764
  const ctx = canvas.getContext('2d');
1765
  let degree = 1;
1766
-
1767
  // Generate sample data with some noise
1768
  const trueFunc = (x) => 0.5 * x * x - 3 * x + 5;
1769
  const data = [];
1770
  for (let x = 0; x <= 10; x += 0.5) {
1771
  data.push({ x, y: trueFunc(x) + (Math.random() - 0.5) * 2 });
1772
  }
1773
-
1774
  function fitPolynomial(degree) {
1775
  // Simple polynomial fit (not production-quality)
1776
  return (x) => {
@@ -1780,28 +1794,28 @@ function initPolynomialRegressionCanvas() {
1780
  return trueFunc(x);
1781
  };
1782
  }
1783
-
1784
  function draw() {
1785
  clearCanvas(ctx, canvas);
1786
-
1787
  const padding = 60;
1788
  const width = canvas.width - 2 * padding;
1789
  const height = canvas.height - 2 * padding;
1790
-
1791
  const maxX = 10;
1792
  const maxY = 15;
1793
-
1794
  // Draw axes
1795
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1796
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1797
-
1798
  // Draw data points
1799
  data.forEach(point => {
1800
  const px = padding + (point.x / maxX) * width;
1801
  const py = canvas.height - padding - (point.y / maxY) * height;
1802
  drawCircle(ctx, px, py, 4, COLORS.cyan);
1803
  });
1804
-
1805
  // Draw polynomial fit
1806
  const polyFunc = fitPolynomial(degree);
1807
  ctx.beginPath();
@@ -1818,17 +1832,17 @@ function initPolynomialRegressionCanvas() {
1818
  ctx.strokeStyle = COLORS.orange;
1819
  ctx.lineWidth = 3;
1820
  ctx.stroke();
1821
-
1822
  drawText(ctx, `Polynomial Degree: ${degree}`, canvas.width / 2, 30, 14, COLORS.orange);
1823
-
1824
  if (degree > 5) {
1825
  drawText(ctx, 'High degree may overfit!', canvas.width / 2, 50, 12, COLORS.orange);
1826
  }
1827
  }
1828
-
1829
  const slider = document.getElementById('slider74');
1830
  const label = document.getElementById('label74');
1831
-
1832
  if (slider) {
1833
  slider.addEventListener('input', (e) => {
1834
  degree = parseInt(e.target.value);
@@ -1836,17 +1850,17 @@ function initPolynomialRegressionCanvas() {
1836
  draw();
1837
  });
1838
  }
1839
-
1840
  draw();
1841
  }
1842
 
1843
  function initPCACanvas() {
1844
  const canvas = document.getElementById('canvas-77');
1845
  if (!canvas) return;
1846
-
1847
  const ctx = canvas.getContext('2d');
1848
  let showPCs = false;
1849
-
1850
  // Generate 2D data
1851
  const data = [];
1852
  for (let i = 0; i < 50; i++) {
@@ -1854,100 +1868,100 @@ function initPCACanvas() {
1854
  const y = 0.8 * x + Math.random() * 0.5;
1855
  data.push({ x, y });
1856
  }
1857
-
1858
  function draw() {
1859
  clearCanvas(ctx, canvas);
1860
-
1861
  const centerX = canvas.width / 2;
1862
  const centerY = canvas.height / 2;
1863
  const scale = 80;
1864
-
1865
  // Draw axes
1866
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
1867
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
1868
-
1869
  // Draw data points
1870
  data.forEach(point => {
1871
  const px = centerX + point.x * scale;
1872
  const py = centerY - point.y * scale;
1873
  drawCircle(ctx, px, py, 5, COLORS.cyan);
1874
  });
1875
-
1876
  if (showPCs) {
1877
  // Draw PC1 (main direction)
1878
  const pc1Angle = Math.atan(0.8);
1879
  const pc1Length = 150;
1880
- drawLine(ctx,
1881
- centerX - pc1Length * Math.cos(pc1Angle),
1882
  centerY + pc1Length * Math.sin(pc1Angle),
1883
- centerX + pc1Length * Math.cos(pc1Angle),
1884
  centerY - pc1Length * Math.sin(pc1Angle),
1885
  COLORS.orange, 3
1886
  );
1887
  drawText(ctx, 'PC1 (80% variance)', centerX + 100, centerY - 80, 14, COLORS.orange);
1888
-
1889
  // Draw PC2 (perpendicular)
1890
  const pc2Angle = pc1Angle + Math.PI / 2;
1891
  const pc2Length = 80;
1892
- drawLine(ctx,
1893
- centerX - pc2Length * Math.cos(pc2Angle),
1894
  centerY + pc2Length * Math.sin(pc2Angle),
1895
- centerX + pc2Length * Math.cos(pc2Angle),
1896
  centerY - pc2Length * Math.sin(pc2Angle),
1897
  COLORS.green, 3
1898
  );
1899
  drawText(ctx, 'PC2 (20% variance)', centerX - 100, centerY - 80, 14, COLORS.green);
1900
  }
1901
-
1902
  drawText(ctx, 'PCA finds directions of maximum variance', canvas.width / 2, 30, 14, COLORS.cyan);
1903
  }
1904
-
1905
  const projectBtn = document.getElementById('btn77project');
1906
  const resetBtn = document.getElementById('btn77reset');
1907
-
1908
  if (projectBtn) {
1909
  projectBtn.addEventListener('click', () => {
1910
  showPCs = true;
1911
  draw();
1912
  });
1913
  }
1914
-
1915
  if (resetBtn) {
1916
  resetBtn.addEventListener('click', () => {
1917
  showPCs = false;
1918
  draw();
1919
  });
1920
  }
1921
-
1922
  draw();
1923
  }
1924
 
1925
  function initGradientDescentCanvas() {
1926
  const canvas = document.getElementById('canvas-80');
1927
  if (!canvas) return;
1928
-
1929
  const ctx = canvas.getContext('2d');
1930
  let learningRate = 0.1;
1931
  let animating = false;
1932
  let path = [];
1933
-
1934
  // Simple quadratic function
1935
  const f = (x) => (x - 3) * (x - 3) + 2;
1936
  const df = (x) => 2 * (x - 3);
1937
-
1938
  function draw() {
1939
  clearCanvas(ctx, canvas);
1940
-
1941
  const padding = 60;
1942
  const width = canvas.width - 2 * padding;
1943
  const height = canvas.height - 2 * padding;
1944
  const xMin = 0, xMax = 6;
1945
  const yMax = 12;
1946
-
1947
  // Draw axes
1948
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1949
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1950
-
1951
  // Draw function
1952
  ctx.beginPath();
1953
  for (let x = xMin; x <= xMax; x += 0.05) {
@@ -1963,14 +1977,14 @@ function initGradientDescentCanvas() {
1963
  ctx.strokeStyle = COLORS.cyan;
1964
  ctx.lineWidth = 2;
1965
  ctx.stroke();
1966
-
1967
  // Draw path
1968
  if (path.length > 0) {
1969
  ctx.beginPath();
1970
  path.forEach((point, i) => {
1971
  const px = padding + ((point.x - xMin) / (xMax - xMin)) * width;
1972
  const py = canvas.height - padding - (point.y / yMax) * height;
1973
-
1974
  if (i === 0) {
1975
  ctx.moveTo(px, py);
1976
  } else {
@@ -1982,48 +1996,48 @@ function initGradientDescentCanvas() {
1982
  ctx.lineWidth = 2;
1983
  ctx.stroke();
1984
  }
1985
-
1986
  drawText(ctx, 'Gradient Descent: Following negative gradient to minimum', canvas.width / 2, 30, 14, COLORS.cyan);
1987
  drawText(ctx, `Learning Rate: ${learningRate.toFixed(2)}`, canvas.width / 2, 50, 12, COLORS.text);
1988
  }
1989
-
1990
  function startDescent() {
1991
  if (animating) return;
1992
  animating = true;
1993
  path = [{ x: 0.5, y: f(0.5) }];
1994
-
1995
  const interval = setInterval(() => {
1996
  const current = path[path.length - 1];
1997
  const grad = df(current.x);
1998
  const nextX = current.x - learningRate * grad;
1999
-
2000
  if (Math.abs(grad) < 0.01 || path.length > 50) {
2001
  clearInterval(interval);
2002
  animating = false;
2003
  return;
2004
  }
2005
-
2006
  path.push({ x: nextX, y: f(nextX) });
2007
  draw();
2008
  }, 200);
2009
  }
2010
-
2011
  const slider = document.getElementById('slider80');
2012
  const label = document.getElementById('label80');
2013
  const startBtn = document.getElementById('btn80start');
2014
  const resetBtn = document.getElementById('btn80reset');
2015
-
2016
  if (slider) {
2017
  slider.addEventListener('input', (e) => {
2018
  learningRate = parseFloat(e.target.value);
2019
  if (label) label.textContent = learningRate.toFixed(2);
2020
  });
2021
  }
2022
-
2023
  if (startBtn) {
2024
  startBtn.addEventListener('click', startDescent);
2025
  }
2026
-
2027
  if (resetBtn) {
2028
  resetBtn.addEventListener('click', () => {
2029
  path = [];
@@ -2031,31 +2045,31 @@ function initGradientDescentCanvas() {
2031
  draw();
2032
  });
2033
  }
2034
-
2035
  draw();
2036
  }
2037
 
2038
  function initLossLandscapeCanvas() {
2039
  const canvas = document.getElementById('canvas-85');
2040
  if (!canvas) return;
2041
-
2042
  const ctx = canvas.getContext('2d');
2043
  let lossType = 'mse';
2044
-
2045
  function draw() {
2046
  clearCanvas(ctx, canvas);
2047
-
2048
  const padding = 60;
2049
  const width = canvas.width - 2 * padding;
2050
  const height = canvas.height - 2 * padding;
2051
-
2052
  // True value
2053
  const trueVal = 5;
2054
-
2055
  // Draw axes
2056
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
2057
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
2058
-
2059
  // Draw loss function
2060
  ctx.beginPath();
2061
  for (let pred = 0; pred <= 10; pred += 0.1) {
@@ -2068,10 +2082,10 @@ function initLossLandscapeCanvas() {
2068
  // Simplified cross-entropy
2069
  loss = -Math.log(Math.max(0.01, 1 - Math.abs(trueVal - pred) / 10));
2070
  }
2071
-
2072
  const px = padding + (pred / 10) * width;
2073
  const py = canvas.height - padding - (loss / 30) * height;
2074
-
2075
  if (pred === 0) {
2076
  ctx.moveTo(px, py);
2077
  } else {
@@ -2081,23 +2095,23 @@ function initLossLandscapeCanvas() {
2081
  ctx.strokeStyle = COLORS.cyan;
2082
  ctx.lineWidth = 3;
2083
  ctx.stroke();
2084
-
2085
  // Draw minimum
2086
  const minX = padding + (trueVal / 10) * width;
2087
  drawLine(ctx, minX, canvas.height - padding, minX, padding, COLORS.orange, 2);
2088
  drawText(ctx, 'Minimum', minX + 10, padding + 20, 12, COLORS.orange);
2089
-
2090
  const lossNames = {
2091
  'mse': 'Mean Squared Error: (y - ŷ)²',
2092
  'mae': 'Mean Absolute Error: |y - ŷ|',
2093
  'cross': 'Cross-Entropy Loss'
2094
  };
2095
-
2096
  drawText(ctx, lossNames[lossType], canvas.width / 2, 30, 14, COLORS.cyan);
2097
  drawText(ctx, 'Predicted Value →', canvas.width - 100, canvas.height - 30, 12, COLORS.text);
2098
  drawText(ctx, 'Loss ↑', padding - 40, padding, 12, COLORS.text);
2099
  }
2100
-
2101
  const select = document.getElementById('select85');
2102
  if (select) {
2103
  select.addEventListener('change', (e) => {
@@ -2105,10 +2119,316 @@ function initLossLandscapeCanvas() {
2105
  draw();
2106
  });
2107
  }
2108
-
2109
  draw();
2110
  }
2111
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2112
  console.log('%c📊 Ultimate Learning Platform Loaded', 'color: #64ffda; font-size: 16px; font-weight: bold;');
2113
  console.log('%cReady to explore 125+ comprehensive topics across 5 subjects!', 'color: #4a90e2; font-size: 14px;');
2114
  console.log('%c✓ Statistics (41) ✓ Linear Algebra (16) ✓ Calculus (12) ✓ Data Science (16) ✓ Machine Learning (40+)', 'color: #51cf66; font-size: 12px;');
 
18
 
19
  // ===== INITIALIZATION =====
20
  document.addEventListener('DOMContentLoaded', () => {
21
+ // Core initialization
22
  initNavigation();
23
  initSubjectTabs();
24
  initInteractiveElements();
25
  setupScrollObserver();
26
  initializeAllVisualizations();
27
+
28
+ // Enhanced features
29
+ initSearch();
30
+ initProgressTracking();
31
+ initKeyboardShortcuts();
32
+ initLazyLoading();
33
+
34
  // Show initial subject
35
  switchSubject('statistics');
36
+
37
+ // Log features
38
+ console.log('%c🚀 Enhanced Features Active:', 'color: #64ffda; font-size: 14px; font-weight: bold;');
39
+ console.log('%c ✓ Global Search (Ctrl/Cmd+K)', 'color: #4a90e2;');
40
+ console.log('%c ✓ Progress Tracking (saved locally)', 'color: #4a90e2;');
41
+ console.log('%c ✓ MathJax Formula Rendering', 'color: #4a90e2;');
42
+ console.log('%c ✓ Keyboard Navigation (Alt+↑/↓)', 'color: #4a90e2;');
43
  });
44
 
45
  // ===== SUBJECT SWITCHING =====
 
55
 
56
  function switchSubject(subject) {
57
  currentSubject = subject;
58
+
59
  // Update active tab
60
  document.querySelectorAll('.subject-tab').forEach(tab => {
61
  tab.classList.remove('active');
 
63
  tab.classList.add('active');
64
  }
65
  });
66
+
67
  // Update sidebar title
68
  const titles = {
69
  'statistics': 'Statistics Content',
 
76
  if (sidebarTitle) {
77
  sidebarTitle.textContent = titles[subject];
78
  }
79
+
80
  // Show/hide sidebar modules
81
  document.querySelectorAll('.module').forEach(module => {
82
  const moduleSubject = module.dataset.subject;
 
87
  module.style.display = subject === 'statistics' ? 'block' : 'none';
88
  }
89
  });
90
+
91
  // Show/hide topic sections
92
  document.querySelectorAll('.topic-section').forEach(section => {
93
  const sectionSubject = section.dataset.subject || 'statistics';
94
  section.style.display = sectionSubject === subject ? 'block' : 'none';
95
  });
96
+
97
  // Scroll to first topic of subject
98
  const firstTopic = document.querySelector(`.topic-section[data-subject="${subject}"], .topic-section:not([data-subject])`);
99
  if (firstTopic && subject !== 'statistics') {
 
115
  // Mobile menu toggle
116
  const mobileMenuBtn = document.getElementById('mobileMenuBtn');
117
  const sidebar = document.getElementById('sidebar');
118
+
119
  if (mobileMenuBtn) {
120
  mobileMenuBtn.addEventListener('click', () => {
121
  sidebar.classList.toggle('active');
 
128
  link.addEventListener('click', (e) => {
129
  e.preventDefault();
130
  const topicId = link.getAttribute('data-topic');
131
+
132
  // Handle both 'topic-X' and 'ml-topic-X' formats
133
  let target = document.getElementById(topicId);
134
  if (!target && !topicId.includes('-')) {
135
  target = document.getElementById(`topic-${topicId}`);
136
  }
137
+
138
  if (target) {
139
  target.scrollIntoView({ behavior: 'smooth', block: 'start' });
140
  updateActiveLink(topicId);
141
+
142
  // Close mobile menu if open
143
  if (window.innerWidth <= 1024) {
144
  sidebar.classList.remove('active');
 
244
  function calculateMode(data) {
245
  const frequency = {};
246
  let maxFreq = 0;
247
+
248
  data.forEach(val => {
249
  frequency[val] = (frequency[val] || 0) + 1;
250
  maxFreq = Math.max(maxFreq, frequency[val]);
251
  });
252
+
253
  if (maxFreq === 1) return 'None';
254
+
255
  const modes = Object.keys(frequency).filter(key => frequency[key] === maxFreq);
256
  return modes.join(', ');
257
  }
 
271
  const sorted = [...data].sort((a, b) => a - b);
272
  const q2 = calculateMedian(sorted);
273
  const midIndex = Math.floor(sorted.length / 2);
274
+
275
  const lowerHalf = sorted.length % 2 === 0
276
  ? sorted.slice(0, midIndex)
277
  : sorted.slice(0, midIndex);
278
  const upperHalf = sorted.length % 2 === 0
279
  ? sorted.slice(midIndex)
280
  : sorted.slice(midIndex + 1);
281
+
282
  const q1 = calculateMedian(lowerHalf);
283
  const q3 = calculateMedian(upperHalf);
284
+
285
  return { q1, q2, q3 };
286
  }
287
 
 
290
  const iqr = q3 - q1;
291
  const lowerFence = q1 - 1.5 * iqr;
292
  const upperFence = q3 + 1.5 * iqr;
293
+
294
  return { q1, q3, iqr, lowerFence, upperFence };
295
  }
296
 
 
303
  const meanX = calculateMean(x);
304
  const meanY = calculateMean(y);
305
  let sum = 0;
306
+
307
  for (let i = 0; i < x.length; i++) {
308
  sum += (x[i] - meanX) * (y[i] - meanY);
309
  }
310
+
311
  return sum / (x.length - 1);
312
  }
313
 
 
324
  function initPopulationSampleViz() {
325
  const canvas = document.getElementById('populationSampleCanvas');
326
  if (!canvas) return;
327
+
328
  const ctx = canvas.getContext('2d');
329
  let population = [];
330
  let sample = [];
331
  let sampleSize = 30;
332
+
333
  // Initialize population
334
  for (let i = 0; i < 200; i++) {
335
  population.push({
 
338
  inSample: false
339
  });
340
  }
341
+
342
  function draw() {
343
  clearCanvas(ctx, canvas);
344
+
345
  // Draw title
346
  drawText(ctx, 'Population (All dots) vs Sample (Highlighted)', canvas.width / 2, 30, 16, COLORS.cyan);
347
+
348
  // Draw population
349
  population.forEach(point => {
350
  const color = point.inSample ? COLORS.orange : COLORS.primary;
351
  const radius = point.inSample ? 6 : 4;
352
  drawCircle(ctx, point.x, point.y, radius, color);
353
  });
354
+
355
  // Draw statistics
356
  const popCount = population.length;
357
  const sampleCount = population.filter(p => p.inSample).length;
358
  drawText(ctx, `Population Size: N = ${popCount}`, 150, canvas.height - 20, 14, COLORS.text, 'center');
359
  drawText(ctx, `Sample Size: n = ${sampleCount}`, canvas.width - 150, canvas.height - 20, 14, COLORS.orange, 'center');
360
  }
361
+
362
  function takeSample() {
363
  // Reset all
364
  population.forEach(p => p.inSample = false);
365
+
366
  // Randomly select sample
367
  const shuffled = [...population].sort(() => Math.random() - 0.5);
368
  for (let i = 0; i < Math.min(sampleSize, population.length); i++) {
369
  shuffled[i].inSample = true;
370
  }
371
+
372
  draw();
373
  }
374
+
375
  // Event listeners
376
  const sampleBtn = document.getElementById('sampleBtn');
377
  const resetBtn = document.getElementById('resetPopBtn');
378
  const sizeSlider = document.getElementById('sampleSizeSlider');
379
  const sizeLabel = document.getElementById('sampleSizeLabel');
380
+
381
  if (sampleBtn) {
382
  sampleBtn.addEventListener('click', takeSample);
383
  }
384
+
385
  if (resetBtn) {
386
  resetBtn.addEventListener('click', () => {
387
  population.forEach(p => p.inSample = false);
388
  draw();
389
  });
390
  }
391
+
392
  if (sizeSlider) {
393
  sizeSlider.addEventListener('input', (e) => {
394
  sampleSize = parseInt(e.target.value);
 
397
  }
398
  });
399
  }
400
+
401
  draw();
402
  }
403
 
 
405
  function initCentralTendencyViz() {
406
  const canvas = document.getElementById('centralTendencyCanvas');
407
  if (!canvas) return;
408
+
409
  const ctx = canvas.getContext('2d');
410
  let data = [10, 20, 30, 40, 50];
411
+
412
  function parseInput(input) {
413
  return input.split(',').map(s => parseFloat(s.trim())).filter(n => !isNaN(n));
414
  }
415
+
416
  function draw() {
417
  clearCanvas(ctx, canvas);
418
+
419
  if (data.length === 0) {
420
  drawText(ctx, 'Please enter valid numbers', canvas.width / 2, canvas.height / 2, 16, COLORS.orange);
421
  return;
422
  }
423
+
424
  const sorted = [...data].sort((a, b) => a - b);
425
  const min = Math.min(...sorted);
426
  const max = Math.max(...sorted);
427
  const range = max - min || 1;
428
  const padding = 80;
429
  const width = canvas.width - 2 * padding;
430
+
431
  // Calculate statistics
432
  const mean = calculateMean(data);
433
  const median = calculateMedian(data);
434
  const mode = calculateMode(data);
435
+
436
  // Update results display
437
  document.getElementById('meanResult').textContent = mean.toFixed(2);
438
  document.getElementById('medianResult').textContent = median.toFixed(2);
439
  document.getElementById('modeResult').textContent = mode;
440
+
441
  // Draw axis
442
  const axisY = canvas.height / 2;
443
  drawLine(ctx, padding, axisY, canvas.width - padding, axisY, COLORS.text, 2);
444
+
445
  // Draw data points
446
  sorted.forEach((val, idx) => {
447
  const x = padding + ((val - min) / range) * width;
448
  drawCircle(ctx, x, axisY, 8, COLORS.primary);
449
  drawText(ctx, val.toString(), x, axisY + 30, 12, COLORS.text);
450
  });
451
+
452
  // Draw mean
453
  const meanX = padding + ((mean - min) / range) * width;
454
  drawLine(ctx, meanX, axisY - 60, meanX, axisY + 60, COLORS.cyan, 3);
455
  drawText(ctx, `Mean: ${mean.toFixed(2)}`, meanX, axisY - 70, 14, COLORS.cyan);
456
+
457
  // Draw median
458
  const medianX = padding + ((median - min) / range) * width;
459
  drawLine(ctx, medianX, axisY - 50, medianX, axisY + 50, COLORS.orange, 2);
460
  drawText(ctx, `Median: ${median.toFixed(2)}`, medianX, axisY - 55, 12, COLORS.orange);
461
  }
462
+
463
  // Event listeners
464
  const input = document.getElementById('centralTendencyInput');
465
  const calcBtn = document.getElementById('calculateCentralBtn');
466
  const randomBtn = document.getElementById('randomDataBtn');
467
+
468
  if (calcBtn && input) {
469
  calcBtn.addEventListener('click', () => {
470
  data = parseInput(input.value);
471
  draw();
472
  });
473
  }
474
+
475
  if (randomBtn && input) {
476
  randomBtn.addEventListener('click', () => {
477
  data = Array.from({ length: 10 }, () => Math.floor(Math.random() * 100));
 
479
  draw();
480
  });
481
  }
482
+
483
  if (input) {
484
  input.addEventListener('keypress', (e) => {
485
  if (e.key === 'Enter') {
 
488
  }
489
  });
490
  }
491
+
492
  draw();
493
  }
494
 
 
497
  // Statistics visualizations
498
  initPopulationSampleViz();
499
  initCentralTendencyViz();
500
+
501
  // Linear Algebra visualizations
502
  initVectorCanvas();
503
  initSpanCanvas();
504
  initTransformationGrid();
505
  initEigenvectorCanvas();
506
+
507
  // Calculus visualizations
508
  initCircleAreaCanvas();
509
  initDerivativeCanvas();
510
  initRiemannSumCanvas();
511
  initTaylorSeriesCanvas();
512
+
513
  // Data Science visualizations
514
  initSimpleRegressionCanvas();
515
  initLogisticRegressionCanvas();
 
517
  initPCACanvas();
518
  initGradientDescentCanvas();
519
  initLossLandscapeCanvas();
520
+
521
  // Machine Learning visualizations
522
  initMLLinearRegressionCanvas();
523
  initMLKMeansCanvas();
 
532
  function initMLLinearRegressionCanvas() {
533
  const canvas = document.getElementById('canvas-ml-1');
534
  if (!canvas) return;
535
+
536
  const ctx = canvas.getContext('2d');
537
  let showLine = false;
538
+
539
  // House price data from worked example
540
  const data = [
541
+ { x: 1000, y: 150 },
542
+ { x: 1500, y: 200 },
543
+ { x: 2000, y: 250 },
544
+ { x: 3000, y: 350 }
545
  ];
546
+
547
  function draw() {
548
  clearCanvas(ctx, canvas);
549
+
550
  const padding = 80;
551
  const width = canvas.width - 2 * padding;
552
  const height = canvas.height - 2 * padding;
553
+
554
  const maxX = 3500;
555
  const maxY = 400;
556
+
557
  // Draw axes
558
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
559
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
560
+
561
  // Draw grid
562
  for (let i = 0; i <= 7; i++) {
563
  const x = padding + (i / 7) * width;
 
565
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
566
  drawText(ctx, xVal, x, canvas.height - padding + 20, 10, COLORS.textSecondary);
567
  }
568
+
569
  for (let i = 0; i <= 8; i++) {
570
  const y = canvas.height - padding - (i / 8) * height;
571
  const yVal = (i * 50).toString();
572
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
573
  drawText(ctx, yVal, padding - 15, y + 4, 10, COLORS.textSecondary, 'right');
574
  }
575
+
576
  // Draw labels
577
  drawText(ctx, 'Size (sq ft)', canvas.width / 2, canvas.height - 10, 12, COLORS.cyan);
578
  ctx.save();
 
580
  ctx.rotate(-Math.PI / 2);
581
  drawText(ctx, 'Price ($1000s)', 0, 0, 12, COLORS.cyan);
582
  ctx.restore();
583
+
584
  // Draw data points
585
  data.forEach(point => {
586
  const px = padding + (point.x / maxX) * width;
 
588
  drawCircle(ctx, px, py, 8, COLORS.cyan);
589
  drawText(ctx, `${point.y}k`, px + 15, py - 10, 10, COLORS.cyan, 'left');
590
  });
591
+
592
  // Draw regression line if enabled
593
  if (showLine) {
594
  // From worked example: y = 50 + 0.1x
595
  const slope = 0.1;
596
  const intercept = 50;
597
+
598
  const x1 = 0;
599
  const y1 = intercept;
600
  const x2 = maxX;
601
  const y2 = intercept + slope * x2;
602
+
603
  const px1 = padding + (x1 / maxX) * width;
604
  const py1 = canvas.height - padding - (y1 / maxY) * height;
605
  const px2 = padding + (x2 / maxX) * width;
606
  const py2 = canvas.height - padding - (y2 / maxY) * height;
607
+
608
  drawLine(ctx, px1, py1, px2, py2, COLORS.orange, 3);
609
+
610
  // Show equation
611
  drawText(ctx, 'y = 50 + 0.10x', canvas.width / 2, 30, 16, COLORS.orange);
612
  drawText(ctx, 'R² = 1.00 (Perfect Fit!)', canvas.width / 2, 50, 14, COLORS.green);
613
+
614
  // Highlight prediction point (2500, 300)
615
  const predX = 2500;
616
  const predY = 50 + 0.1 * predX;
 
620
  drawText(ctx, '2500 sq ft → $300k', ppx - 80, ppy - 15, 12, COLORS.green, 'left');
621
  }
622
  }
623
+
624
  const fitBtn = document.getElementById('btn-ml-1-fit');
625
  const resetBtn = document.getElementById('btn-ml-1-reset');
626
+
627
  if (fitBtn) {
628
  fitBtn.addEventListener('click', () => {
629
  showLine = true;
630
  draw();
631
  });
632
  }
633
+
634
  if (resetBtn) {
635
  resetBtn.addEventListener('click', () => {
636
  showLine = false;
637
  draw();
638
  });
639
  }
640
+
641
  draw();
642
  }
643
 
644
  function initMLKMeansCanvas() {
645
  const canvas = document.getElementById('canvas-ml-15');
646
  if (!canvas) return;
647
+
648
  const ctx = canvas.getContext('2d');
649
  let clustered = false;
650
+
651
  // Customer data from worked example
652
  const customers = [
653
+ { name: 'A', age: 25, income: 40, cluster: null },
654
+ { name: 'B', age: 30, income: 50, cluster: null },
655
+ { name: 'C', age: 28, income: 45, cluster: null },
656
+ { name: 'D', age: 55, income: 80, cluster: null },
657
+ { name: 'E', age: 60, income: 90, cluster: null },
658
+ { name: 'F', age: 52, income: 75, cluster: null }
659
  ];
660
+
661
  let centroids = [
662
+ { age: 25, income: 40, color: COLORS.cyan },
663
+ { age: 60, income: 90, color: COLORS.orange }
664
  ];
665
+
666
  function assignClusters() {
667
  customers.forEach(customer => {
668
  // Calculate distance to each centroid
669
  const d1 = Math.sqrt(Math.pow(customer.age - centroids[0].age, 2) + Math.pow(customer.income - centroids[0].income, 2));
670
  const d2 = Math.sqrt(Math.pow(customer.age - centroids[1].age, 2) + Math.pow(customer.income - centroids[1].income, 2));
671
+
672
  customer.cluster = d1 < d2 ? 0 : 1;
673
  });
674
+
675
  // Update centroids
676
  const cluster0 = customers.filter(c => c.cluster === 0);
677
  const cluster1 = customers.filter(c => c.cluster === 1);
678
+
679
  if (cluster0.length > 0) {
680
  centroids[0].age = cluster0.reduce((s, c) => s + c.age, 0) / cluster0.length;
681
  centroids[0].income = cluster0.reduce((s, c) => s + c.income, 0) / cluster0.length;
 
685
  centroids[1].income = cluster1.reduce((s, c) => s + c.income, 0) / cluster1.length;
686
  }
687
  }
688
+
689
  function draw() {
690
  clearCanvas(ctx, canvas);
691
+
692
  const padding = 80;
693
  const width = canvas.width - 2 * padding;
694
  const height = canvas.height - 2 * padding;
695
+
696
  const minAge = 20, maxAge = 70;
697
  const minIncome = 30, maxIncome = 100;
698
+
699
  // Draw axes
700
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
701
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
702
+
703
  // Draw grid
704
  for (let age = 20; age <= 70; age += 10) {
705
  const x = padding + ((age - minAge) / (maxAge - minAge)) * width;
706
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
707
  drawText(ctx, age.toString(), x, canvas.height - padding + 20, 10, COLORS.textSecondary);
708
  }
709
+
710
  for (let income = 30; income <= 100; income += 10) {
711
  const y = canvas.height - padding - ((income - minIncome) / (maxIncome - minIncome)) * height;
712
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
713
  drawText(ctx, `$${income}k`, padding - 40, y + 4, 10, COLORS.textSecondary, 'right');
714
  }
715
+
716
  // Draw labels
717
  drawText(ctx, 'Age', canvas.width / 2, canvas.height - 10, 12, COLORS.cyan);
718
  ctx.save();
 
720
  ctx.rotate(-Math.PI / 2);
721
  drawText(ctx, 'Income ($k)', 0, 0, 12, COLORS.cyan);
722
  ctx.restore();
723
+
724
  // Draw customers
725
  customers.forEach(customer => {
726
  const px = padding + ((customer.age - minAge) / (maxAge - minAge)) * width;
727
  const py = canvas.height - padding - ((customer.income - minIncome) / (maxIncome - minIncome)) * height;
728
+
729
  const color = clustered ? (customer.cluster === 0 ? COLORS.cyan : COLORS.orange) : COLORS.primary;
730
  drawCircle(ctx, px, py, 10, color);
731
  drawText(ctx, customer.name, px, py - 15, 12, COLORS.text);
732
  });
733
+
734
  // Draw centroids if clustered
735
  if (clustered) {
736
  centroids.forEach((centroid, i) => {
737
  const cx = padding + ((centroid.age - minAge) / (maxAge - minAge)) * width;
738
  const cy = canvas.height - padding - ((centroid.income - minIncome) / (maxIncome - minIncome)) * height;
739
+
740
  // Draw X marker for centroid
741
  ctx.strokeStyle = centroid.color;
742
  ctx.lineWidth = 4;
 
746
  ctx.moveTo(cx + 12, cy - 12);
747
  ctx.lineTo(cx - 12, cy + 12);
748
  ctx.stroke();
749
+
750
+ drawText(ctx, `C${i + 1} [${centroid.age.toFixed(1)}, ${centroid.income.toFixed(1)}]`,
751
+ cx + 20, cy, 11, centroid.color, 'left');
752
  });
753
+
754
  drawText(ctx, 'Cluster 1 (Young, Lower Income)', 150, 30, 12, COLORS.cyan);
755
  drawText(ctx, 'Cluster 2 (Mature, Higher Income)', 150, 50, 12, COLORS.orange);
756
  }
757
  }
758
+
759
  const clusterBtn = document.getElementById('btn-ml-15-cluster');
760
  const resetBtn = document.getElementById('btn-ml-15-reset');
761
+
762
  if (clusterBtn) {
763
  clusterBtn.addEventListener('click', () => {
764
  clustered = true;
 
766
  draw();
767
  });
768
  }
769
+
770
  if (resetBtn) {
771
  resetBtn.addEventListener('click', () => {
772
  clustered = false;
773
  customers.forEach(c => c.cluster = null);
774
  centroids = [
775
+ { age: 25, income: 40, color: COLORS.cyan },
776
+ { age: 60, income: 90, color: COLORS.orange }
777
  ];
778
  draw();
779
  });
780
  }
781
+
782
  draw();
783
  }
784
 
 
787
  function initVectorCanvas() {
788
  const canvas = document.getElementById('canvas-42');
789
  if (!canvas) return;
790
+
791
  const ctx = canvas.getContext('2d');
792
  let vx = 3, vy = 2;
793
+
794
  function draw() {
795
  clearCanvas(ctx, canvas);
796
+
797
  const centerX = canvas.width / 2;
798
  const centerY = canvas.height / 2;
799
  const scale = 40;
800
+
801
  // Draw axes
802
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
803
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
804
+
805
  // Draw grid
806
  ctx.strokeStyle = '#333';
807
  ctx.lineWidth = 0.5;
 
811
  drawLine(ctx, 0, centerY + i * scale, canvas.width, centerY + i * scale, '#333', 0.5);
812
  }
813
  }
814
+
815
  // Draw vector
816
  const endX = centerX + vx * scale;
817
  const endY = centerY - vy * scale;
818
+
819
  // Arrow shaft
820
  drawLine(ctx, centerX, centerY, endX, endY, COLORS.cyan, 3);
821
+
822
  // Arrow head
823
  const angle = Math.atan2(vy, vx);
824
  const arrowSize = 15;
 
829
  ctx.closePath();
830
  ctx.fillStyle = COLORS.cyan;
831
  ctx.fill();
832
+
833
  // Draw vector label
834
  drawText(ctx, `v = [${vx.toFixed(1)}, ${vy.toFixed(1)}]`, endX + 20, endY - 10, 14, COLORS.cyan, 'left');
835
+
836
  // Draw magnitude
837
  const magnitude = Math.sqrt(vx * vx + vy * vy);
838
  drawText(ctx, `|v| = ${magnitude.toFixed(2)}`, canvas.width / 2, 30, 14, COLORS.text);
839
  }
840
+
841
  const sliderX = document.getElementById('slider42x');
842
  const sliderY = document.getElementById('slider42y');
843
  const labelX = document.getElementById('vec42x');
844
  const labelY = document.getElementById('vec42y');
845
+
846
  if (sliderX) {
847
  sliderX.addEventListener('input', (e) => {
848
  vx = parseFloat(e.target.value);
 
850
  draw();
851
  });
852
  }
853
+
854
  if (sliderY) {
855
  sliderY.addEventListener('input', (e) => {
856
  vy = parseFloat(e.target.value);
 
858
  draw();
859
  });
860
  }
861
+
862
  const resetBtn = document.getElementById('btn42reset');
863
  if (resetBtn) {
864
  resetBtn.addEventListener('click', () => {
 
870
  draw();
871
  });
872
  }
873
+
874
  draw();
875
  }
876
 
877
  function initSpanCanvas() {
878
  const canvas = document.getElementById('canvas-43');
879
  if (!canvas) return;
880
+
881
  const ctx = canvas.getContext('2d');
882
  let animating = false;
883
  let t = 0;
884
+
885
  function draw() {
886
  clearCanvas(ctx, canvas);
887
+
888
  const centerX = canvas.width / 2;
889
  const centerY = canvas.height / 2;
890
  const scale = 50;
891
+
892
  // Draw axes
893
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
894
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
895
+
896
  // Basis vectors
897
  const v1 = { x: 2, y: 1 };
898
  const v2 = { x: -1, y: 1.5 };
899
+
900
  // Draw basis vectors
901
  drawLine(ctx, centerX, centerY, centerX + v1.x * scale, centerY - v1.y * scale, COLORS.cyan, 3);
902
  drawLine(ctx, centerX, centerY, centerX + v2.x * scale, centerY - v2.y * scale, COLORS.orange, 3);
903
+
904
  if (animating) {
905
  // Draw span (multiple linear combinations)
906
  ctx.globalAlpha = 0.3;
 
913
  }
914
  ctx.globalAlpha = 1;
915
  }
916
+
917
  drawText(ctx, 'v₁', centerX + v1.x * scale + 20, centerY - v1.y * scale, 16, COLORS.cyan);
918
  drawText(ctx, 'v₂', centerX + v2.x * scale - 20, centerY - v2.y * scale, 16, COLORS.orange);
919
  }
920
+
921
  const animateBtn = document.getElementById('btn43animate');
922
  const resetBtn = document.getElementById('btn43reset');
923
+
924
  if (animateBtn) {
925
  animateBtn.addEventListener('click', () => {
926
  animating = true;
927
  draw();
928
  });
929
  }
930
+
931
  if (resetBtn) {
932
  resetBtn.addEventListener('click', () => {
933
  animating = false;
934
  draw();
935
  });
936
  }
937
+
938
  draw();
939
  }
940
 
941
  function initTransformationGrid() {
942
  const canvas = document.getElementById('canvas-44');
943
  if (!canvas) return;
944
+
945
  const ctx = canvas.getContext('2d');
946
  let matrix = [[1, 0], [0, 1]]; // Identity
947
+
948
  function drawGrid(transform = false) {
949
  clearCanvas(ctx, canvas);
950
+
951
  const centerX = canvas.width / 2;
952
  const centerY = canvas.height / 2;
953
  const scale = 40;
954
  const gridSize = 5;
955
+
956
  ctx.strokeStyle = transform ? COLORS.cyan : '#555';
957
  ctx.lineWidth = 1;
958
+
959
  // Draw grid lines
960
  for (let i = -gridSize; i <= gridSize; i++) {
961
  for (let j = -gridSize; j <= gridSize; j++) {
962
  let x1 = i, y1 = j;
963
  let x2 = i + 1, y2 = j;
964
  let x3 = i, y3 = j + 1;
965
+
966
  if (transform) {
967
  [x1, y1] = [matrix[0][0] * i + matrix[0][1] * j, matrix[1][0] * i + matrix[1][1] * j];
968
  [x2, y2] = [matrix[0][0] * (i + 1) + matrix[0][1] * j, matrix[1][0] * (i + 1) + matrix[1][1] * j];
969
  [x3, y3] = [matrix[0][0] * i + matrix[0][1] * (j + 1), matrix[1][0] * i + matrix[1][1] * (j + 1)];
970
  }
971
+
972
  drawLine(ctx, centerX + x1 * scale, centerY - y1 * scale, centerX + x2 * scale, centerY - y2 * scale, ctx.strokeStyle, 1);
973
  drawLine(ctx, centerX + x1 * scale, centerY - y1 * scale, centerX + x3 * scale, centerY - y3 * scale, ctx.strokeStyle, 1);
974
  }
975
  }
976
+
977
  // Draw i-hat and j-hat
978
  const iHat = transform ? [matrix[0][0], matrix[1][0]] : [1, 0];
979
  const jHat = transform ? [matrix[0][1], matrix[1][1]] : [0, 1];
980
+
981
  drawLine(ctx, centerX, centerY, centerX + iHat[0] * scale, centerY - iHat[1] * scale, COLORS.orange, 3);
982
  drawLine(ctx, centerX, centerY, centerX + jHat[0] * scale, centerY - jHat[1] * scale, COLORS.green, 3);
983
  }
984
+
985
  const select = document.getElementById('select44');
986
  const applyBtn = document.getElementById('btn44apply');
987
+
988
  if (applyBtn && select) {
989
  applyBtn.addEventListener('click', () => {
990
  const type = select.value;
 
1004
  drawGrid(true);
1005
  });
1006
  }
1007
+
1008
  drawGrid(false);
1009
  }
1010
 
1011
  function initEigenvectorCanvas() {
1012
  const canvas = document.getElementById('canvas-54');
1013
  if (!canvas) return;
1014
+
1015
  const ctx = canvas.getContext('2d');
1016
  let transformed = false;
1017
+
1018
  function draw() {
1019
  clearCanvas(ctx, canvas);
1020
+
1021
  const centerX = canvas.width / 2;
1022
  const centerY = canvas.height / 2;
1023
  const scale = 50;
1024
+
1025
  // Draw axes
1026
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
1027
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
1028
+
1029
  // Matrix [[2, 0], [0, 1]] - scaling transformation
1030
  // Eigenvectors: [1, 0] with eigenvalue 2, [0, 1] with eigenvalue 1
1031
+
1032
  const e1Scale = transformed ? 2 : 1;
1033
  const e2Scale = 1;
1034
+
1035
  // Draw regular vectors (affected)
1036
  const regularVecs = [[2, 2], [1, 2], [-2, 1]];
1037
  regularVecs.forEach(([x, y]) => {
 
1039
  const endY = centerY - y * scale;
1040
  drawLine(ctx, centerX, centerY, endX, endY, '#666', 2);
1041
  });
1042
+
1043
  // Draw eigenvectors (special - stay on their line)
1044
  drawLine(ctx, centerX, centerY, centerX + e1Scale * 2 * scale, centerY, COLORS.cyan, 4);
1045
  drawLine(ctx, centerX, centerY, centerX, centerY - 2 * scale, COLORS.orange, 4);
1046
+
1047
  drawText(ctx, 'Eigenvector 1 (λ=2)', centerX + e1Scale * 2 * scale + 10, centerY - 10, 12, COLORS.cyan, 'left');
1048
  drawText(ctx, 'Eigenvector 2 (λ=1)', centerX + 10, centerY - 2 * scale - 10, 12, COLORS.orange, 'left');
1049
  }
1050
+
1051
  const transformBtn = document.getElementById('btn54transform');
1052
  const resetBtn = document.getElementById('btn54reset');
1053
+
1054
  if (transformBtn) {
1055
  transformBtn.addEventListener('click', () => {
1056
  transformed = true;
1057
  draw();
1058
  });
1059
  }
1060
+
1061
  if (resetBtn) {
1062
  resetBtn.addEventListener('click', () => {
1063
  transformed = false;
1064
  draw();
1065
  });
1066
  }
1067
+
1068
  draw();
1069
  }
1070
 
 
1073
  function initCircleAreaCanvas() {
1074
  const canvas = document.getElementById('canvas-58');
1075
  if (!canvas) return;
1076
+
1077
  const ctx = canvas.getContext('2d');
1078
  let unwrapping = false;
1079
  let progress = 0;
1080
+
1081
  function draw() {
1082
  clearCanvas(ctx, canvas);
1083
+
1084
  const centerX = canvas.width / 4;
1085
  const centerY = canvas.height / 2;
1086
  const radius = 100;
1087
+
1088
  if (!unwrapping) {
1089
  // Draw circle
1090
  drawCircle(ctx, centerX, centerY, radius, COLORS.cyan, false);
 
1098
  const rectY = centerY - r;
1099
  const rectHeight = 2 * r;
1100
  const dr = radius / rings;
1101
+
1102
  ctx.fillStyle = COLORS.cyan;
1103
  ctx.globalAlpha = 0.6;
1104
  ctx.fillRect(rectX, rectY, 2, rectHeight);
1105
  }
1106
  ctx.globalAlpha = 1;
1107
+
1108
  if (progress >= 0.99) {
1109
  drawText(ctx, 'Integrated! Area = πr²', canvas.width * 0.7, centerY + 50, 14, COLORS.green);
1110
  }
1111
  }
1112
  }
1113
+
1114
  const animateBtn = document.getElementById('btn58animate');
1115
  const resetBtn = document.getElementById('btn58reset');
1116
+
1117
  if (animateBtn) {
1118
  animateBtn.addEventListener('click', () => {
1119
  unwrapping = true;
 
1127
  }, 50);
1128
  });
1129
  }
1130
+
1131
  if (resetBtn) {
1132
  resetBtn.addEventListener('click', () => {
1133
  unwrapping = false;
 
1135
  draw();
1136
  });
1137
  }
1138
+
1139
  draw();
1140
  }
1141
 
1142
  function initDerivativeCanvas() {
1143
  const canvas = document.getElementById('canvas-59');
1144
  if (!canvas) return;
1145
+
1146
  const ctx = canvas.getContext('2d');
1147
  let dx = 1.0;
1148
+
1149
  function f(x) {
1150
  return 0.02 * x * x;
1151
  }
1152
+
1153
  function draw() {
1154
  clearCanvas(ctx, canvas);
1155
+
1156
  const scale = 50;
1157
  const centerX = canvas.width / 2;
1158
  const centerY = canvas.height * 0.8;
1159
+
1160
  // Draw axes
1161
  drawLine(ctx, 50, centerY, canvas.width - 50, centerY, '#555', 1);
1162
  drawLine(ctx, centerX, 50, centerX, canvas.height - 50, '#555', 1);
1163
+
1164
  // Draw function
1165
  ctx.beginPath();
1166
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
 
1175
  ctx.strokeStyle = COLORS.cyan;
1176
  ctx.lineWidth = 2;
1177
  ctx.stroke();
1178
+
1179
  // Draw secant line
1180
  const x0 = 0;
1181
  const x1 = dx * scale;
1182
  const y0 = centerY - f(x0) * scale;
1183
  const y1 = centerY - f(x1) * scale;
1184
+
1185
  drawLine(ctx, centerX + x0 - 50, y0 - (y1 - y0) / x1 * 50, centerX + x1 + 50, y1 + (y1 - y0) / x1 * 50, COLORS.orange, 2);
1186
  drawCircle(ctx, centerX + x0, y0, 5, COLORS.green);
1187
  drawCircle(ctx, centerX + x1, y1, 5, COLORS.green);
1188
+
1189
  const slope = (f(x1 / scale) - f(0)) / (x1 / scale);
1190
  drawText(ctx, `Slope = ${slope.toFixed(3)}`, canvas.width / 2, 40, 14, COLORS.orange);
1191
  drawText(ctx, `As Δx → 0, slope → derivative`, canvas.width / 2, 60, 12, COLORS.text);
1192
  }
1193
+
1194
  const slider = document.getElementById('slider59dx');
1195
  const label = document.getElementById('label59dx');
1196
+
1197
  if (slider) {
1198
  slider.addEventListener('input', (e) => {
1199
  dx = parseFloat(e.target.value);
 
1201
  draw();
1202
  });
1203
  }
1204
+
1205
  draw();
1206
  }
1207
 
1208
  function initRiemannSumCanvas() {
1209
  const canvas = document.getElementById('canvas-64');
1210
  if (!canvas) return;
1211
+
1212
  const ctx = canvas.getContext('2d');
1213
  let n = 8;
1214
+
1215
  function f(x) {
1216
  return 50 + 50 * Math.sin(x / 30);
1217
  }
1218
+
1219
  function draw() {
1220
  clearCanvas(ctx, canvas);
1221
+
1222
  const padding = 50;
1223
  const width = canvas.width - 2 * padding;
1224
  const height = canvas.height - 2 * padding;
1225
+
1226
  // Draw axes
1227
  drawLine(ctx, padding, height + padding, canvas.width - padding, height + padding, '#555', 2);
1228
  drawLine(ctx, padding, padding, padding, height + padding, '#555', 2);
1229
+
1230
  // Draw function
1231
  ctx.beginPath();
1232
  for (let x = 0; x <= width; x += 2) {
 
1241
  ctx.strokeStyle = COLORS.cyan;
1242
  ctx.lineWidth = 3;
1243
  ctx.stroke();
1244
+
1245
  // Draw rectangles
1246
  const rectWidth = width / n;
1247
  let totalArea = 0;
1248
+
1249
  for (let i = 0; i < n; i++) {
1250
  const x = i * rectWidth;
1251
  const rectHeight = f(x);
1252
  totalArea += rectWidth * rectHeight;
1253
+
1254
  ctx.fillStyle = COLORS.orange;
1255
  ctx.globalAlpha = 0.5;
1256
  ctx.fillRect(padding + x, height + padding - rectHeight, rectWidth, rectHeight);
 
1258
  ctx.globalAlpha = 1;
1259
  ctx.strokeRect(padding + x, height + padding - rectHeight, rectWidth, rectHeight);
1260
  }
1261
+
1262
  drawText(ctx, `Rectangles: ${n}`, canvas.width / 2, 30, 14, COLORS.text);
1263
  drawText(ctx, `Approximate Area: ${(totalArea / 100).toFixed(2)}`, canvas.width / 2, 50, 12, COLORS.orange);
1264
  }
1265
+
1266
  const slider = document.getElementById('slider64n');
1267
  const label = document.getElementById('label64n');
1268
+
1269
  if (slider) {
1270
  slider.addEventListener('input', (e) => {
1271
  n = parseInt(e.target.value);
 
1273
  draw();
1274
  });
1275
  }
1276
+
1277
  draw();
1278
  }
1279
 
1280
  function initTaylorSeriesCanvas() {
1281
  const canvas = document.getElementById('canvas-68');
1282
  if (!canvas) return;
1283
+
1284
  const ctx = canvas.getContext('2d');
1285
  let degree = 1;
1286
  let func = 'sin';
1287
+
1288
  function draw() {
1289
  clearCanvas(ctx, canvas);
1290
+
1291
  const scale = 50;
1292
  const centerX = canvas.width / 2;
1293
  const centerY = canvas.height / 2;
1294
+
1295
  // Draw axes
1296
  drawLine(ctx, 50, centerY, canvas.width - 50, centerY, '#555', 1);
1297
  drawLine(ctx, centerX, 50, centerX, canvas.height - 50, '#555', 1);
1298
+
1299
  // Draw actual function
1300
  ctx.beginPath();
1301
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
 
1304
  if (func === 'sin') y = Math.sin(t);
1305
  else if (func === 'cos') y = Math.cos(t);
1306
  else y = Math.exp(t);
1307
+
1308
  const px = centerX + x;
1309
  const py = centerY - y * scale;
1310
  if (x === -canvas.width / 2) {
 
1316
  ctx.strokeStyle = COLORS.cyan;
1317
  ctx.lineWidth = 2;
1318
  ctx.stroke();
1319
+
1320
  // Draw Taylor approximation
1321
  ctx.beginPath();
1322
  for (let x = -canvas.width / 2; x < canvas.width / 2; x += 2) {
1323
  const t = x / scale;
1324
  let y = 0;
1325
+
1326
  if (func === 'sin') {
1327
  for (let n = 0; n <= degree; n++) {
1328
  const term = Math.pow(-1, n) * Math.pow(t, 2 * n + 1) / factorial(2 * n + 1);
 
1338
  y += Math.pow(t, n) / factorial(n);
1339
  }
1340
  }
1341
+
1342
  const px = centerX + x;
1343
  const py = centerY - y * scale;
1344
  if (x === -canvas.width / 2) {
 
1350
  ctx.strokeStyle = COLORS.orange;
1351
  ctx.lineWidth = 3;
1352
  ctx.stroke();
1353
+
1354
  drawText(ctx, `Function: ${func}(x)`, canvas.width / 2, 30, 14, COLORS.cyan);
1355
  drawText(ctx, `Taylor degree: ${degree}`, canvas.width / 2, 50, 14, COLORS.orange);
1356
  }
1357
+
1358
  function factorial(n) {
1359
  if (n <= 1) return 1;
1360
  return n * factorial(n - 1);
1361
  }
1362
+
1363
  const slider = document.getElementById('slider68degree');
1364
  const label = document.getElementById('label68degree');
1365
  const select = document.getElementById('select68func');
1366
+
1367
  if (slider) {
1368
  slider.addEventListener('input', (e) => {
1369
  degree = parseInt(e.target.value);
 
1371
  draw();
1372
  });
1373
  }
1374
+
1375
  if (select) {
1376
  select.addEventListener('change', (e) => {
1377
  func = e.target.value;
1378
  draw();
1379
  });
1380
  }
1381
+
1382
  draw();
1383
  }
1384
 
 
1390
 
1391
  // ===== HELPER FUNCTIONS =====
1392
  function generateRandomData(count, min, max) {
1393
+ return Array.from({ length: count }, () =>
1394
  Math.floor(Math.random() * (max - min + 1)) + min
1395
  );
1396
  }
 
1404
  if (animationFrames[canvasId]) {
1405
  cancelAnimationFrame(animationFrames[canvasId]);
1406
  }
1407
+
1408
  function animate() {
1409
  animationFunction();
1410
  animationFrames[canvasId] = requestAnimationFrame(animate);
1411
  }
1412
+
1413
  animate();
1414
  }
1415
 
 
1427
  function initMLSVMCanvas() {
1428
  const canvas = document.getElementById('canvas-ml-9');
1429
  if (!canvas) return;
1430
+
1431
  const ctx = canvas.getContext('2d');
1432
+
1433
  // Generate two-class data
1434
+ const class1 = Array.from({ length: 20 }, () => ({
1435
  x: Math.random() * 100 + 50,
1436
  y: Math.random() * 100 + 50
1437
  }));
1438
+ const class2 = Array.from({ length: 20 }, () => ({
1439
  x: Math.random() * 100 + 200,
1440
  y: Math.random() * 100 + 200
1441
  }));
1442
+
1443
  function draw() {
1444
  clearCanvas(ctx, canvas);
1445
+
1446
  const padding = 50;
1447
+
1448
  // Draw decision boundary (simplified)
1449
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, padding, COLORS.orange, 3);
1450
+ drawText(ctx, 'Decision Boundary', canvas.width / 2, 30, 14, COLORS.orange);
1451
+
1452
  // Draw margin lines
1453
  drawLine(ctx, padding, canvas.height - padding - 30, canvas.width - padding, padding - 30, COLORS.green, 1);
1454
  drawLine(ctx, padding, canvas.height - padding + 30, canvas.width - padding, padding + 30, COLORS.green, 1);
1455
+ drawText(ctx, 'Maximum Margin', canvas.width / 2, 50, 12, COLORS.green);
1456
+
1457
  // Draw data points
1458
  class1.forEach(p => drawCircle(ctx, p.x, p.y, 6, COLORS.cyan));
1459
  class2.forEach(p => drawCircle(ctx, p.x, p.y, 6, COLORS.primary));
1460
  }
1461
+
1462
  draw();
1463
  }
1464
 
1465
  function initMLRandomForestCanvas() {
1466
  const canvas = document.getElementById('canvas-ml-12');
1467
  if (!canvas) return;
1468
+
1469
  const ctx = canvas.getContext('2d');
1470
+
1471
  function draw() {
1472
  clearCanvas(ctx, canvas);
1473
+
1474
+ drawText(ctx, 'Random Forest: Ensemble of Decision Trees', canvas.width / 2, 50, 16, COLORS.cyan);
1475
+
1476
  // Draw multiple trees
1477
  const treeCount = 5;
1478
  const treeWidth = (canvas.width - 100) / treeCount;
1479
+
1480
  for (let i = 0; i < treeCount; i++) {
1481
+ const x = 50 + i * treeWidth + treeWidth / 2;
1482
  const y = 100;
1483
+
1484
  // Draw simple tree structure
1485
  drawLine(ctx, x, y, x - 30, y + 60, COLORS.green, 2);
1486
  drawLine(ctx, x, y, x + 30, y + 60, COLORS.green, 2);
1487
  drawCircle(ctx, x, y, 8, COLORS.cyan);
1488
  drawCircle(ctx, x - 30, y + 60, 6, COLORS.orange);
1489
  drawCircle(ctx, x + 30, y + 60, 6, COLORS.orange);
1490
+
1491
+ drawText(ctx, `Tree ${i + 1}`, x, y + 100, 12, COLORS.text);
1492
  }
1493
+
1494
  // Draw voting arrow
1495
+ drawLine(ctx, canvas.width / 2, 200, canvas.width / 2, 280, COLORS.orange, 3);
1496
+ drawText(ctx, '↓ Majority Vote', canvas.width / 2 + 10, 250, 14, COLORS.orange, 'left');
1497
+
1498
+ drawRect(ctx, canvas.width / 2 - 80, 280, 160, 40, COLORS.green);
1499
+ drawText(ctx, 'Final Prediction', canvas.width / 2, 305, 14, '#000');
1500
  }
1501
+
1502
  draw();
1503
  }
1504
 
1505
  function initMLGradientBoostingCanvas() {
1506
  const canvas = document.getElementById('canvas-ml-13');
1507
  if (!canvas) return;
1508
+
1509
  const ctx = canvas.getContext('2d');
1510
+
1511
  function draw() {
1512
  clearCanvas(ctx, canvas);
1513
+
1514
+ drawText(ctx, 'Gradient Boosting: Sequential Error Correction', canvas.width / 2, 40, 16, COLORS.cyan);
1515
+
1516
  const stages = 4;
1517
  const stageWidth = (canvas.width - 100) / stages;
1518
+
1519
  for (let i = 0; i < stages; i++) {
1520
  const x = 50 + i * stageWidth;
1521
+ const y = canvas.height / 2;
1522
+
1523
  // Draw tree
1524
  drawRect(ctx, x, y - 40, stageWidth - 40, 80, i === 0 ? COLORS.cyan : COLORS.orange, false);
1525
+ drawText(ctx, `Tree ${i + 1}`, x + (stageWidth - 40) / 2, y, 12, COLORS.text);
1526
+ drawText(ctx, i === 0 ? 'Base' : 'Fix Errors', x + (stageWidth - 40) / 2, y + 20, 10, COLORS.textSecondary);
1527
+
1528
  // Draw arrow
1529
  if (i < stages - 1) {
1530
  drawLine(ctx, x + stageWidth - 40, y, x + stageWidth, y, COLORS.green, 2);
1531
  drawText(ctx, '+', x + stageWidth - 20, y - 10, 16, COLORS.green);
1532
  }
1533
  }
1534
+
1535
+ drawText(ctx, 'Each tree learns from previous mistakes', canvas.width / 2, canvas.height - 30, 12, COLORS.text);
1536
  }
1537
+
1538
  draw();
1539
  }
1540
 
1541
  function initMLNeuralNetworkCanvas() {
1542
  const canvas = document.getElementById('canvas-ml-14');
1543
  if (!canvas) return;
1544
+
1545
  const ctx = canvas.getContext('2d');
1546
+
1547
  function draw() {
1548
  clearCanvas(ctx, canvas);
1549
+
1550
+ drawText(ctx, 'Neural Network Architecture', canvas.width / 2, 30, 16, COLORS.cyan);
1551
+
1552
  const layers = [3, 5, 4, 2]; // neurons per layer
1553
  const layerSpacing = (canvas.width - 100) / (layers.length - 1);
1554
+
1555
  // Draw connections
1556
  ctx.globalAlpha = 0.3;
1557
  for (let l = 0; l < layers.length - 1; l++) {
1558
  const x1 = 50 + l * layerSpacing;
1559
  const x2 = 50 + (l + 1) * layerSpacing;
1560
+
1561
  for (let i = 0; i < layers[l]; i++) {
1562
+ const y1 = canvas.height / 2 - (layers[l] - 1) * 15 + i * 30;
1563
  for (let j = 0; j < layers[l + 1]; j++) {
1564
+ const y2 = canvas.height / 2 - (layers[l + 1] - 1) * 15 + j * 30;
1565
  drawLine(ctx, x1, y1, x2, y2, COLORS.textSecondary, 1);
1566
  }
1567
  }
1568
  }
1569
  ctx.globalAlpha = 1;
1570
+
1571
  // Draw neurons
1572
  layers.forEach((count, l) => {
1573
  const x = 50 + l * layerSpacing;
1574
  for (let i = 0; i < count; i++) {
1575
+ const y = canvas.height / 2 - (count - 1) * 15 + i * 30;
1576
  drawCircle(ctx, x, y, 12, l === 0 ? COLORS.cyan : (l === layers.length - 1 ? COLORS.green : COLORS.orange));
1577
  }
1578
+
1579
  const layerNames = ['Input', 'Hidden 1', 'Hidden 2', 'Output'];
1580
  drawText(ctx, layerNames[l], x, canvas.height - 30, 12, COLORS.text);
1581
  });
1582
  }
1583
+
1584
  draw();
1585
  }
1586
 
 
1589
  function initSimpleRegressionCanvas() {
1590
  const canvas = document.getElementById('canvas-70');
1591
  if (!canvas) return;
1592
+
1593
  const ctx = canvas.getContext('2d');
1594
  let showLine = false;
1595
+
1596
  // Sample data
1597
  const data = [
1598
+ { x: 1, y: 2.1 }, { x: 2, y: 4.2 }, { x: 3, y: 5.8 }, { x: 4, y: 8.1 },
1599
+ { x: 5, y: 10.3 }, { x: 6, y: 12.1 }, { x: 7, y: 13.9 }, { x: 8, y: 16.2 },
1600
+ { x: 9, y: 18.1 }, { x: 10, y: 20.0 }
1601
  ];
1602
+
1603
  function calculateRegression() {
1604
  const n = data.length;
1605
  const sumX = data.reduce((s, p) => s + p.x, 0);
1606
  const sumY = data.reduce((s, p) => s + p.y, 0);
1607
  const sumXY = data.reduce((s, p) => s + p.x * p.y, 0);
1608
  const sumX2 = data.reduce((s, p) => s + p.x * p.x, 0);
1609
+
1610
  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
1611
  const intercept = (sumY - slope * sumX) / n;
1612
+
1613
  return { slope, intercept };
1614
  }
1615
+
1616
  function draw() {
1617
  clearCanvas(ctx, canvas);
1618
+
1619
  const padding = 60;
1620
  const width = canvas.width - 2 * padding;
1621
  const height = canvas.height - 2 * padding;
1622
+
1623
  const maxX = 11;
1624
  const maxY = 22;
1625
+
1626
  // Draw axes
1627
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1628
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1629
+
1630
  // Draw grid
1631
  for (let i = 0; i <= 10; i++) {
1632
  const x = padding + (i / maxX) * width;
1633
  const y = canvas.height - padding - (i * 2 / maxY) * height;
1634
  drawLine(ctx, x, canvas.height - padding, x, canvas.height - padding + 5, COLORS.textSecondary, 1);
1635
  drawText(ctx, i.toString(), x, canvas.height - padding + 20, 10, COLORS.textSecondary);
1636
+
1637
  if (i * 2 <= maxY) {
1638
  drawLine(ctx, padding - 5, y, padding, y, COLORS.textSecondary, 1);
1639
  drawText(ctx, (i * 2).toString(), padding - 15, y + 4, 10, COLORS.textSecondary, 'right');
1640
  }
1641
  }
1642
+
1643
  // Draw labels
1644
  drawText(ctx, 'X', canvas.width - padding + 20, canvas.height - padding + 4, 12, COLORS.cyan);
1645
  drawText(ctx, 'Y', padding - 4, padding - 20, 12, COLORS.cyan);
1646
+
1647
  // Draw data points
1648
  data.forEach(point => {
1649
  const x = padding + (point.x / maxX) * width;
1650
  const y = canvas.height - padding - (point.y / maxY) * height;
1651
  drawCircle(ctx, x, y, 6, COLORS.cyan);
1652
  });
1653
+
1654
  // Draw regression line
1655
  if (showLine) {
1656
  const { slope, intercept } = calculateRegression();
 
1658
  const y1 = intercept;
1659
  const x2 = maxX;
1660
  const y2 = slope * x2 + intercept;
1661
+
1662
  const px1 = padding + (x1 / maxX) * width;
1663
  const py1 = canvas.height - padding - (y1 / maxY) * height;
1664
  const px2 = padding + (x2 / maxX) * width;
1665
  const py2 = canvas.height - padding - (y2 / maxY) * height;
1666
+
1667
  drawLine(ctx, px1, py1, px2, py2, COLORS.orange, 3);
1668
+
1669
  // Calculate R²
1670
  const meanY = data.reduce((s, p) => s + p.y, 0) / data.length;
1671
  let ssTot = 0, ssRes = 0;
 
1675
  ssTot += Math.pow(point.y - meanY, 2);
1676
  });
1677
  const r2 = 1 - (ssRes / ssTot);
1678
+
1679
  drawText(ctx, `y = ${intercept.toFixed(2)} + ${slope.toFixed(2)}x`, canvas.width / 2, 30, 14, COLORS.orange);
1680
  drawText(ctx, `R² = ${r2.toFixed(4)}`, canvas.width / 2, 50, 14, COLORS.green);
1681
  } else {
1682
  drawText(ctx, 'Click "Fit Regression Line" to see the best fit', canvas.width / 2, 30, 14, COLORS.text);
1683
  }
1684
  }
1685
+
1686
  const fitBtn = document.getElementById('btn70fit');
1687
  const resetBtn = document.getElementById('btn70reset');
1688
+
1689
  if (fitBtn) {
1690
  fitBtn.addEventListener('click', () => {
1691
  showLine = true;
1692
  draw();
1693
  });
1694
  }
1695
+
1696
  if (resetBtn) {
1697
  resetBtn.addEventListener('click', () => {
1698
  showLine = false;
1699
  draw();
1700
  });
1701
  }
1702
+
1703
  draw();
1704
  }
1705
 
1706
  function initLogisticRegressionCanvas() {
1707
  const canvas = document.getElementById('canvas-72');
1708
  if (!canvas) return;
1709
+
1710
  const ctx = canvas.getContext('2d');
1711
  let threshold = 0.5;
1712
+
1713
  function sigmoid(z) {
1714
  return 1 / (1 + Math.exp(-z));
1715
  }
1716
+
1717
  function draw() {
1718
  clearCanvas(ctx, canvas);
1719
+
1720
  const padding = 60;
1721
  const width = canvas.width - 2 * padding;
1722
  const height = canvas.height - 2 * padding;
1723
+
1724
  // Draw axes
1725
  const centerY = canvas.height / 2;
1726
  drawLine(ctx, padding, centerY, canvas.width - padding, centerY, COLORS.text, 2);
1727
  drawLine(ctx, canvas.width / 2, padding, canvas.width / 2, canvas.height - padding, COLORS.text, 2);
1728
+
1729
  // Draw sigmoid curve
1730
  ctx.beginPath();
1731
  for (let x = -6; x <= 6; x += 0.1) {
 
1741
  ctx.strokeStyle = COLORS.cyan;
1742
  ctx.lineWidth = 3;
1743
  ctx.stroke();
1744
+
1745
  // Draw threshold line
1746
  const thresholdY = canvas.height - padding - threshold * height;
1747
  drawLine(ctx, padding, thresholdY, canvas.width - padding, thresholdY, COLORS.orange, 2);
1748
  drawText(ctx, `Threshold = ${threshold.toFixed(2)}`, canvas.width - 100, thresholdY - 10, 12, COLORS.orange);
1749
+
1750
  // Draw labels
1751
  drawText(ctx, 'P(y=1) = σ(z) = 1/(1+e^(-z))', canvas.width / 2, 30, 14, COLORS.cyan);
1752
  drawText(ctx, 'Class 1 (if P ≥ threshold)', canvas.width - 150, thresholdY - 30, 12, COLORS.green);
1753
  drawText(ctx, 'Class 0 (if P < threshold)', canvas.width - 150, thresholdY + 30, 12, COLORS.textSecondary);
1754
+
1755
  // Draw axis labels
1756
  drawText(ctx, '0', canvas.width / 2 + 5, centerY + 20, 10, COLORS.text, 'left');
1757
  drawText(ctx, '1', padding - 20, padding + 10, 10, COLORS.text);
1758
  }
1759
+
1760
  const slider = document.getElementById('slider72');
1761
  const label = document.getElementById('label72');
1762
+
1763
  if (slider) {
1764
  slider.addEventListener('input', (e) => {
1765
  threshold = parseFloat(e.target.value);
 
1767
  draw();
1768
  });
1769
  }
1770
+
1771
  draw();
1772
  }
1773
 
1774
  function initPolynomialRegressionCanvas() {
1775
  const canvas = document.getElementById('canvas-74');
1776
  if (!canvas) return;
1777
+
1778
  const ctx = canvas.getContext('2d');
1779
  let degree = 1;
1780
+
1781
  // Generate sample data with some noise
1782
  const trueFunc = (x) => 0.5 * x * x - 3 * x + 5;
1783
  const data = [];
1784
  for (let x = 0; x <= 10; x += 0.5) {
1785
  data.push({ x, y: trueFunc(x) + (Math.random() - 0.5) * 2 });
1786
  }
1787
+
1788
  function fitPolynomial(degree) {
1789
  // Simple polynomial fit (not production-quality)
1790
  return (x) => {
 
1794
  return trueFunc(x);
1795
  };
1796
  }
1797
+
1798
  function draw() {
1799
  clearCanvas(ctx, canvas);
1800
+
1801
  const padding = 60;
1802
  const width = canvas.width - 2 * padding;
1803
  const height = canvas.height - 2 * padding;
1804
+
1805
  const maxX = 10;
1806
  const maxY = 15;
1807
+
1808
  // Draw axes
1809
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1810
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1811
+
1812
  // Draw data points
1813
  data.forEach(point => {
1814
  const px = padding + (point.x / maxX) * width;
1815
  const py = canvas.height - padding - (point.y / maxY) * height;
1816
  drawCircle(ctx, px, py, 4, COLORS.cyan);
1817
  });
1818
+
1819
  // Draw polynomial fit
1820
  const polyFunc = fitPolynomial(degree);
1821
  ctx.beginPath();
 
1832
  ctx.strokeStyle = COLORS.orange;
1833
  ctx.lineWidth = 3;
1834
  ctx.stroke();
1835
+
1836
  drawText(ctx, `Polynomial Degree: ${degree}`, canvas.width / 2, 30, 14, COLORS.orange);
1837
+
1838
  if (degree > 5) {
1839
  drawText(ctx, 'High degree may overfit!', canvas.width / 2, 50, 12, COLORS.orange);
1840
  }
1841
  }
1842
+
1843
  const slider = document.getElementById('slider74');
1844
  const label = document.getElementById('label74');
1845
+
1846
  if (slider) {
1847
  slider.addEventListener('input', (e) => {
1848
  degree = parseInt(e.target.value);
 
1850
  draw();
1851
  });
1852
  }
1853
+
1854
  draw();
1855
  }
1856
 
1857
  function initPCACanvas() {
1858
  const canvas = document.getElementById('canvas-77');
1859
  if (!canvas) return;
1860
+
1861
  const ctx = canvas.getContext('2d');
1862
  let showPCs = false;
1863
+
1864
  // Generate 2D data
1865
  const data = [];
1866
  for (let i = 0; i < 50; i++) {
 
1868
  const y = 0.8 * x + Math.random() * 0.5;
1869
  data.push({ x, y });
1870
  }
1871
+
1872
  function draw() {
1873
  clearCanvas(ctx, canvas);
1874
+
1875
  const centerX = canvas.width / 2;
1876
  const centerY = canvas.height / 2;
1877
  const scale = 80;
1878
+
1879
  // Draw axes
1880
  drawLine(ctx, 0, centerY, canvas.width, centerY, '#555', 1);
1881
  drawLine(ctx, centerX, 0, centerX, canvas.height, '#555', 1);
1882
+
1883
  // Draw data points
1884
  data.forEach(point => {
1885
  const px = centerX + point.x * scale;
1886
  const py = centerY - point.y * scale;
1887
  drawCircle(ctx, px, py, 5, COLORS.cyan);
1888
  });
1889
+
1890
  if (showPCs) {
1891
  // Draw PC1 (main direction)
1892
  const pc1Angle = Math.atan(0.8);
1893
  const pc1Length = 150;
1894
+ drawLine(ctx,
1895
+ centerX - pc1Length * Math.cos(pc1Angle),
1896
  centerY + pc1Length * Math.sin(pc1Angle),
1897
+ centerX + pc1Length * Math.cos(pc1Angle),
1898
  centerY - pc1Length * Math.sin(pc1Angle),
1899
  COLORS.orange, 3
1900
  );
1901
  drawText(ctx, 'PC1 (80% variance)', centerX + 100, centerY - 80, 14, COLORS.orange);
1902
+
1903
  // Draw PC2 (perpendicular)
1904
  const pc2Angle = pc1Angle + Math.PI / 2;
1905
  const pc2Length = 80;
1906
+ drawLine(ctx,
1907
+ centerX - pc2Length * Math.cos(pc2Angle),
1908
  centerY + pc2Length * Math.sin(pc2Angle),
1909
+ centerX + pc2Length * Math.cos(pc2Angle),
1910
  centerY - pc2Length * Math.sin(pc2Angle),
1911
  COLORS.green, 3
1912
  );
1913
  drawText(ctx, 'PC2 (20% variance)', centerX - 100, centerY - 80, 14, COLORS.green);
1914
  }
1915
+
1916
  drawText(ctx, 'PCA finds directions of maximum variance', canvas.width / 2, 30, 14, COLORS.cyan);
1917
  }
1918
+
1919
  const projectBtn = document.getElementById('btn77project');
1920
  const resetBtn = document.getElementById('btn77reset');
1921
+
1922
  if (projectBtn) {
1923
  projectBtn.addEventListener('click', () => {
1924
  showPCs = true;
1925
  draw();
1926
  });
1927
  }
1928
+
1929
  if (resetBtn) {
1930
  resetBtn.addEventListener('click', () => {
1931
  showPCs = false;
1932
  draw();
1933
  });
1934
  }
1935
+
1936
  draw();
1937
  }
1938
 
1939
  function initGradientDescentCanvas() {
1940
  const canvas = document.getElementById('canvas-80');
1941
  if (!canvas) return;
1942
+
1943
  const ctx = canvas.getContext('2d');
1944
  let learningRate = 0.1;
1945
  let animating = false;
1946
  let path = [];
1947
+
1948
  // Simple quadratic function
1949
  const f = (x) => (x - 3) * (x - 3) + 2;
1950
  const df = (x) => 2 * (x - 3);
1951
+
1952
  function draw() {
1953
  clearCanvas(ctx, canvas);
1954
+
1955
  const padding = 60;
1956
  const width = canvas.width - 2 * padding;
1957
  const height = canvas.height - 2 * padding;
1958
  const xMin = 0, xMax = 6;
1959
  const yMax = 12;
1960
+
1961
  // Draw axes
1962
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
1963
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
1964
+
1965
  // Draw function
1966
  ctx.beginPath();
1967
  for (let x = xMin; x <= xMax; x += 0.05) {
 
1977
  ctx.strokeStyle = COLORS.cyan;
1978
  ctx.lineWidth = 2;
1979
  ctx.stroke();
1980
+
1981
  // Draw path
1982
  if (path.length > 0) {
1983
  ctx.beginPath();
1984
  path.forEach((point, i) => {
1985
  const px = padding + ((point.x - xMin) / (xMax - xMin)) * width;
1986
  const py = canvas.height - padding - (point.y / yMax) * height;
1987
+
1988
  if (i === 0) {
1989
  ctx.moveTo(px, py);
1990
  } else {
 
1996
  ctx.lineWidth = 2;
1997
  ctx.stroke();
1998
  }
1999
+
2000
  drawText(ctx, 'Gradient Descent: Following negative gradient to minimum', canvas.width / 2, 30, 14, COLORS.cyan);
2001
  drawText(ctx, `Learning Rate: ${learningRate.toFixed(2)}`, canvas.width / 2, 50, 12, COLORS.text);
2002
  }
2003
+
2004
  function startDescent() {
2005
  if (animating) return;
2006
  animating = true;
2007
  path = [{ x: 0.5, y: f(0.5) }];
2008
+
2009
  const interval = setInterval(() => {
2010
  const current = path[path.length - 1];
2011
  const grad = df(current.x);
2012
  const nextX = current.x - learningRate * grad;
2013
+
2014
  if (Math.abs(grad) < 0.01 || path.length > 50) {
2015
  clearInterval(interval);
2016
  animating = false;
2017
  return;
2018
  }
2019
+
2020
  path.push({ x: nextX, y: f(nextX) });
2021
  draw();
2022
  }, 200);
2023
  }
2024
+
2025
  const slider = document.getElementById('slider80');
2026
  const label = document.getElementById('label80');
2027
  const startBtn = document.getElementById('btn80start');
2028
  const resetBtn = document.getElementById('btn80reset');
2029
+
2030
  if (slider) {
2031
  slider.addEventListener('input', (e) => {
2032
  learningRate = parseFloat(e.target.value);
2033
  if (label) label.textContent = learningRate.toFixed(2);
2034
  });
2035
  }
2036
+
2037
  if (startBtn) {
2038
  startBtn.addEventListener('click', startDescent);
2039
  }
2040
+
2041
  if (resetBtn) {
2042
  resetBtn.addEventListener('click', () => {
2043
  path = [];
 
2045
  draw();
2046
  });
2047
  }
2048
+
2049
  draw();
2050
  }
2051
 
2052
  function initLossLandscapeCanvas() {
2053
  const canvas = document.getElementById('canvas-85');
2054
  if (!canvas) return;
2055
+
2056
  const ctx = canvas.getContext('2d');
2057
  let lossType = 'mse';
2058
+
2059
  function draw() {
2060
  clearCanvas(ctx, canvas);
2061
+
2062
  const padding = 60;
2063
  const width = canvas.width - 2 * padding;
2064
  const height = canvas.height - 2 * padding;
2065
+
2066
  // True value
2067
  const trueVal = 5;
2068
+
2069
  // Draw axes
2070
  drawLine(ctx, padding, canvas.height - padding, canvas.width - padding, canvas.height - padding, COLORS.text, 2);
2071
  drawLine(ctx, padding, padding, padding, canvas.height - padding, COLORS.text, 2);
2072
+
2073
  // Draw loss function
2074
  ctx.beginPath();
2075
  for (let pred = 0; pred <= 10; pred += 0.1) {
 
2082
  // Simplified cross-entropy
2083
  loss = -Math.log(Math.max(0.01, 1 - Math.abs(trueVal - pred) / 10));
2084
  }
2085
+
2086
  const px = padding + (pred / 10) * width;
2087
  const py = canvas.height - padding - (loss / 30) * height;
2088
+
2089
  if (pred === 0) {
2090
  ctx.moveTo(px, py);
2091
  } else {
 
2095
  ctx.strokeStyle = COLORS.cyan;
2096
  ctx.lineWidth = 3;
2097
  ctx.stroke();
2098
+
2099
  // Draw minimum
2100
  const minX = padding + (trueVal / 10) * width;
2101
  drawLine(ctx, minX, canvas.height - padding, minX, padding, COLORS.orange, 2);
2102
  drawText(ctx, 'Minimum', minX + 10, padding + 20, 12, COLORS.orange);
2103
+
2104
  const lossNames = {
2105
  'mse': 'Mean Squared Error: (y - ŷ)²',
2106
  'mae': 'Mean Absolute Error: |y - ŷ|',
2107
  'cross': 'Cross-Entropy Loss'
2108
  };
2109
+
2110
  drawText(ctx, lossNames[lossType], canvas.width / 2, 30, 14, COLORS.cyan);
2111
  drawText(ctx, 'Predicted Value →', canvas.width - 100, canvas.height - 30, 12, COLORS.text);
2112
  drawText(ctx, 'Loss ↑', padding - 40, padding, 12, COLORS.text);
2113
  }
2114
+
2115
  const select = document.getElementById('select85');
2116
  if (select) {
2117
  select.addEventListener('change', (e) => {
 
2119
  draw();
2120
  });
2121
  }
2122
+
2123
  draw();
2124
  }
2125
 
2126
+ // ========== SEARCH FUNCTIONALITY ==========
2127
+ function initSearch() {
2128
+ const searchInput = document.getElementById('globalSearch');
2129
+ const searchResults = document.getElementById('searchResults');
2130
+
2131
+ if (!searchInput || !searchResults) return;
2132
+
2133
+ // Build search index from all topics
2134
+ const searchIndex = [];
2135
+ document.querySelectorAll('.topic-section').forEach(section => {
2136
+ const id = section.id;
2137
+ const header = section.querySelector('h2');
2138
+ const subtitle = section.querySelector('.topic-subtitle');
2139
+ const content = section.textContent.substring(0, 500);
2140
+ const subject = section.dataset.subject || 'statistics';
2141
+
2142
+ if (header) {
2143
+ searchIndex.push({
2144
+ id: id,
2145
+ title: header.textContent,
2146
+ subtitle: subtitle ? subtitle.textContent : '',
2147
+ content: content,
2148
+ subject: subject
2149
+ });
2150
+ }
2151
+ });
2152
+
2153
+ searchInput.addEventListener('input', (e) => {
2154
+ const query = e.target.value.toLowerCase().trim();
2155
+
2156
+ if (query.length < 2) {
2157
+ searchResults.classList.remove('active');
2158
+ return;
2159
+ }
2160
+
2161
+ const results = searchIndex.filter(item =>
2162
+ item.title.toLowerCase().includes(query) ||
2163
+ item.subtitle.toLowerCase().includes(query) ||
2164
+ item.content.toLowerCase().includes(query)
2165
+ ).slice(0, 8);
2166
+
2167
+ if (results.length === 0) {
2168
+ searchResults.innerHTML = '<div class="no-results">No topics found for "' + query + '"</div>';
2169
+ } else {
2170
+ searchResults.innerHTML = results.map(result => `
2171
+ <div class="search-result-item" data-topic-id="${result.id}" data-subject="${result.subject}">
2172
+ <div class="search-result-title">${highlightMatch(result.title, query)}</div>
2173
+ <div class="search-result-subject">${result.subject.replace('-', ' ')}</div>
2174
+ </div>
2175
+ `).join('');
2176
+ }
2177
+
2178
+ searchResults.classList.add('active');
2179
+ });
2180
+
2181
+ // Handle result clicks
2182
+ searchResults.addEventListener('click', (e) => {
2183
+ const item = e.target.closest('.search-result-item');
2184
+ if (item) {
2185
+ const topicId = item.dataset.topicId;
2186
+ const subject = item.dataset.subject;
2187
+
2188
+ // Switch to correct subject first
2189
+ switchSubject(subject);
2190
+
2191
+ // Then scroll to topic
2192
+ setTimeout(() => {
2193
+ const target = document.getElementById(topicId);
2194
+ if (target) {
2195
+ target.scrollIntoView({ behavior: 'smooth', block: 'start' });
2196
+ }
2197
+ }, 200);
2198
+
2199
+ searchResults.classList.remove('active');
2200
+ searchInput.value = '';
2201
+ }
2202
+ });
2203
+
2204
+ // Close on outside click
2205
+ document.addEventListener('click', (e) => {
2206
+ if (!searchInput.contains(e.target) && !searchResults.contains(e.target)) {
2207
+ searchResults.classList.remove('active');
2208
+ }
2209
+ });
2210
+
2211
+ // Close on Escape
2212
+ searchInput.addEventListener('keydown', (e) => {
2213
+ if (e.key === 'Escape') {
2214
+ searchResults.classList.remove('active');
2215
+ searchInput.blur();
2216
+ }
2217
+ });
2218
+ }
2219
+
2220
+ function highlightMatch(text, query) {
2221
+ const regex = new RegExp(`(${query})`, 'gi');
2222
+ return text.replace(regex, '<span class="search-highlight">$1</span>');
2223
+ }
2224
+
2225
+ // ========== PROGRESS TRACKING ==========
2226
+ const STORAGE_KEY = 'math_mastery_progress';
2227
+
2228
+ function initProgressTracking() {
2229
+ loadProgress();
2230
+ addCompletionButtons();
2231
+ updateProgressDisplay();
2232
+ }
2233
+
2234
+ function loadProgress() {
2235
+ try {
2236
+ const saved = localStorage.getItem(STORAGE_KEY);
2237
+ if (saved) {
2238
+ const completed = JSON.parse(saved);
2239
+ completed.forEach(topicId => {
2240
+ markTopicComplete(topicId, false);
2241
+ });
2242
+ }
2243
+ } catch (e) {
2244
+ console.warn('Could not load progress:', e);
2245
+ }
2246
+ }
2247
+
2248
+ function saveProgress() {
2249
+ try {
2250
+ const completed = [];
2251
+ document.querySelectorAll('.topic-complete-btn.completed').forEach(btn => {
2252
+ completed.push(btn.dataset.topicId);
2253
+ });
2254
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(completed));
2255
+ } catch (e) {
2256
+ console.warn('Could not save progress:', e);
2257
+ }
2258
+ }
2259
+
2260
+ function addCompletionButtons() {
2261
+ document.querySelectorAll('.topic-section').forEach(section => {
2262
+ const topicId = section.id;
2263
+ const summaryCard = section.querySelector('.summary-card');
2264
+
2265
+ if (summaryCard && !section.querySelector('.topic-complete-btn')) {
2266
+ const btn = document.createElement('button');
2267
+ btn.className = 'topic-complete-btn';
2268
+ btn.dataset.topicId = topicId;
2269
+ btn.innerHTML = '<span class="check-icon"></span> Mark as Complete';
2270
+
2271
+ btn.addEventListener('click', () => {
2272
+ toggleTopicComplete(topicId);
2273
+ });
2274
+
2275
+ summaryCard.appendChild(btn);
2276
+ }
2277
+ });
2278
+ }
2279
+
2280
+ function toggleTopicComplete(topicId) {
2281
+ const btn = document.querySelector(`.topic-complete-btn[data-topic-id="${topicId}"]`);
2282
+ if (btn) {
2283
+ const isCompleted = btn.classList.toggle('completed');
2284
+ btn.innerHTML = isCompleted
2285
+ ? '<span class="check-icon"></span> Completed!'
2286
+ : '<span class="check-icon"></span> Mark as Complete';
2287
+
2288
+ // Update sidebar link
2289
+ const sidebarLink = document.querySelector(`[data-topic="${topicId}"], [data-topic="${topicId.replace('topic-', '')}"]`);
2290
+ if (sidebarLink) {
2291
+ sidebarLink.classList.toggle('completed', isCompleted);
2292
+ }
2293
+
2294
+ saveProgress();
2295
+ updateProgressDisplay();
2296
+ }
2297
+ }
2298
+
2299
+ function markTopicComplete(topicId, save = true) {
2300
+ const btn = document.querySelector(`.topic-complete-btn[data-topic-id="${topicId}"]`);
2301
+ if (btn && !btn.classList.contains('completed')) {
2302
+ btn.classList.add('completed');
2303
+ btn.innerHTML = '<span class="check-icon"></span> Completed!';
2304
+
2305
+ const sidebarLink = document.querySelector(`[data-topic="${topicId}"], [data-topic="${topicId.replace('topic-', '')}"]`);
2306
+ if (sidebarLink) {
2307
+ sidebarLink.classList.add('completed');
2308
+ }
2309
+
2310
+ if (save) {
2311
+ saveProgress();
2312
+ updateProgressDisplay();
2313
+ }
2314
+ }
2315
+ }
2316
+
2317
+ function updateProgressDisplay() {
2318
+ // Progress bar removed - keeping function for compatibility
2319
+ // Can be used for console logging if needed
2320
+ }
2321
+
2322
+ // ========== ROBUST CANVAS INITIALIZATION ==========
2323
+ function ensureCanvasVisible(canvasId, callback) {
2324
+ const canvas = document.getElementById(canvasId);
2325
+ if (!canvas) return;
2326
+
2327
+ // Already initialized
2328
+ if (canvas.dataset.initialized === 'true') return;
2329
+
2330
+ // Check visibility
2331
+ if (canvas.offsetWidth === 0) {
2332
+ setTimeout(() => ensureCanvasVisible(canvasId, callback), 100);
2333
+ return;
2334
+ }
2335
+
2336
+ // Mark and execute
2337
+ canvas.dataset.initialized = 'true';
2338
+ callback();
2339
+ }
2340
+
2341
+ // ========== LAZY LOADING FOR VISUALIZATIONS ==========
2342
+ function initLazyLoading() {
2343
+ const observerOptions = {
2344
+ root: null,
2345
+ rootMargin: '100px',
2346
+ threshold: 0.1
2347
+ };
2348
+
2349
+ const lazyObserver = new IntersectionObserver((entries) => {
2350
+ entries.forEach(entry => {
2351
+ if (entry.isIntersecting) {
2352
+ const section = entry.target;
2353
+ const canvases = section.querySelectorAll('canvas:not([data-initialized="true"])');
2354
+
2355
+ canvases.forEach(canvas => {
2356
+ const initFn = canvas.dataset.initFn;
2357
+ if (initFn && window[initFn]) {
2358
+ ensureCanvasVisible(canvas.id, window[initFn]);
2359
+ }
2360
+ });
2361
+
2362
+ lazyObserver.unobserve(section);
2363
+ }
2364
+ });
2365
+ }, observerOptions);
2366
+
2367
+ document.querySelectorAll('.topic-section').forEach(section => {
2368
+ lazyObserver.observe(section);
2369
+ });
2370
+ }
2371
+
2372
+ // ========== INTERACTIVE ELEMENTS ==========
2373
+ function initInteractiveElements() {
2374
+ // Toggle visibility for practice answers
2375
+ document.querySelectorAll('.show-answers-btn').forEach(btn => {
2376
+ btn.addEventListener('click', () => {
2377
+ const answers = btn.nextElementSibling;
2378
+ if (answers && answers.classList.contains('practice-answers')) {
2379
+ const isHidden = answers.style.display === 'none' || !answers.style.display;
2380
+ answers.style.display = isHidden ? 'block' : 'none';
2381
+ btn.textContent = isHidden ? 'Hide Answers' : 'Show Answers';
2382
+ }
2383
+ });
2384
+ });
2385
+
2386
+ // Initialize formula tooltips
2387
+ document.querySelectorAll('.formula-var').forEach(el => {
2388
+ el.style.cursor = 'help';
2389
+ });
2390
+ }
2391
+
2392
+ // ========== KEYBOARD SHORTCUTS ==========
2393
+ function initKeyboardShortcuts() {
2394
+ document.addEventListener('keydown', (e) => {
2395
+ // Ctrl/Cmd + K for search
2396
+ if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
2397
+ e.preventDefault();
2398
+ const searchInput = document.getElementById('globalSearch');
2399
+ if (searchInput) {
2400
+ searchInput.focus();
2401
+ }
2402
+ }
2403
+
2404
+ // Arrow keys for topic navigation
2405
+ if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
2406
+ e.preventDefault();
2407
+ navigateTopics(e.key === 'ArrowDown' ? 1 : -1);
2408
+ }
2409
+ });
2410
+ }
2411
+
2412
+ function navigateTopics(direction) {
2413
+ const visibleTopics = Array.from(document.querySelectorAll('.topic-section'))
2414
+ .filter(t => t.style.display !== 'none');
2415
+
2416
+ const currentActive = document.querySelector('.topic-link.active');
2417
+ let currentIndex = 0;
2418
+
2419
+ if (currentActive) {
2420
+ const currentId = currentActive.getAttribute('data-topic');
2421
+ currentIndex = visibleTopics.findIndex(t =>
2422
+ t.id === currentId || t.id === `topic-${currentId}`
2423
+ );
2424
+ }
2425
+
2426
+ const newIndex = Math.max(0, Math.min(visibleTopics.length - 1, currentIndex + direction));
2427
+ if (visibleTopics[newIndex]) {
2428
+ visibleTopics[newIndex].scrollIntoView({ behavior: 'smooth', block: 'start' });
2429
+ }
2430
+ }
2431
+
2432
  console.log('%c📊 Ultimate Learning Platform Loaded', 'color: #64ffda; font-size: 16px; font-weight: bold;');
2433
  console.log('%cReady to explore 125+ comprehensive topics across 5 subjects!', 'color: #4a90e2; font-size: 14px;');
2434
  console.log('%c✓ Statistics (41) ✓ Linear Algebra (16) ✓ Calculus (12) ✓ Data Science (16) ✓ Machine Learning (40+)', 'color: #51cf66; font-size: 12px;');
math-ds-complete/index.html CHANGED
The diff for this file is too large to render. See raw diff
 
math-ds-complete/style.css CHANGED
@@ -1,167 +1,251 @@
1
  :root {
2
- /* Primitive Color Tokens */
3
- --color-white: rgba(255, 255, 255, 1);
4
- --color-black: rgba(0, 0, 0, 1);
5
- --color-cream-50: rgba(252, 252, 249, 1);
6
- --color-cream-100: rgba(255, 255, 253, 1);
7
- --color-gray-200: rgba(245, 245, 245, 1);
8
- --color-gray-300: rgba(167, 169, 169, 1);
9
- --color-gray-400: rgba(119, 124, 124, 1);
10
- --color-slate-500: rgba(98, 108, 113, 1);
11
- --color-brown-600: rgba(94, 82, 64, 1);
12
- --color-charcoal-700: rgba(31, 33, 33, 1);
13
- --color-charcoal-800: rgba(38, 40, 40, 1);
14
- --color-slate-900: rgba(19, 52, 59, 1);
15
- --color-teal-300: rgba(50, 184, 198, 1);
16
- --color-teal-400: rgba(45, 166, 178, 1);
17
- --color-teal-500: rgba(33, 128, 141, 1);
18
- --color-teal-600: rgba(29, 116, 128, 1);
19
- --color-teal-700: rgba(26, 104, 115, 1);
20
- --color-teal-800: rgba(41, 150, 161, 1);
21
- --color-red-400: rgba(255, 84, 89, 1);
22
- --color-red-500: rgba(192, 21, 47, 1);
23
- --color-orange-400: rgba(230, 129, 97, 1);
24
- --color-orange-500: rgba(168, 75, 47, 1);
25
-
26
- /* RGB versions for opacity control */
27
- --color-brown-600-rgb: 94, 82, 64;
28
- --color-teal-500-rgb: 33, 128, 141;
29
- --color-slate-900-rgb: 19, 52, 59;
30
- --color-slate-500-rgb: 98, 108, 113;
31
- --color-red-500-rgb: 192, 21, 47;
32
- --color-red-400-rgb: 255, 84, 89;
33
- --color-orange-500-rgb: 168, 75, 47;
34
- --color-orange-400-rgb: 230, 129, 97;
35
-
36
- /* Background color tokens (Light Mode) */
37
- --color-bg-1: rgba(59, 130, 246, 0.08); /* Light blue */
38
- --color-bg-2: rgba(245, 158, 11, 0.08); /* Light yellow */
39
- --color-bg-3: rgba(34, 197, 94, 0.08); /* Light green */
40
- --color-bg-4: rgba(239, 68, 68, 0.08); /* Light red */
41
- --color-bg-5: rgba(147, 51, 234, 0.08); /* Light purple */
42
- --color-bg-6: rgba(249, 115, 22, 0.08); /* Light orange */
43
- --color-bg-7: rgba(236, 72, 153, 0.08); /* Light pink */
44
- --color-bg-8: rgba(6, 182, 212, 0.08); /* Light cyan */
45
-
46
- /* Semantic Color Tokens (Light Mode) */
47
- --color-background: var(--color-cream-50);
48
- --color-surface: var(--color-cream-100);
49
- --color-text: var(--color-slate-900);
50
- --color-text-secondary: var(--color-slate-500);
51
- --color-primary: var(--color-teal-500);
52
- --color-primary-hover: var(--color-teal-600);
53
- --color-primary-active: var(--color-teal-700);
54
- --color-secondary: rgba(var(--color-brown-600-rgb), 0.12);
55
- --color-secondary-hover: rgba(var(--color-brown-600-rgb), 0.2);
56
- --color-secondary-active: rgba(var(--color-brown-600-rgb), 0.25);
57
- --color-border: rgba(var(--color-brown-600-rgb), 0.2);
58
- --color-btn-primary-text: var(--color-cream-50);
59
- --color-card-border: rgba(var(--color-brown-600-rgb), 0.12);
60
- --color-card-border-inner: rgba(var(--color-brown-600-rgb), 0.12);
61
- --color-error: var(--color-red-500);
62
- --color-success: var(--color-teal-500);
63
- --color-warning: var(--color-orange-500);
64
- --color-info: var(--color-slate-500);
65
- --color-focus-ring: rgba(var(--color-teal-500-rgb), 0.4);
66
- --color-select-caret: rgba(var(--color-slate-900-rgb), 0.8);
67
-
68
- /* Common style patterns */
69
- --focus-ring: 0 0 0 3px var(--color-focus-ring);
70
- --focus-outline: 2px solid var(--color-primary);
71
- --status-bg-opacity: 0.15;
72
- --status-border-opacity: 0.25;
73
- --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
74
- --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
75
-
76
- /* RGB versions for opacity control */
77
- --color-success-rgb: 33, 128, 141;
78
- --color-error-rgb: 192, 21, 47;
79
- --color-warning-rgb: 168, 75, 47;
80
- --color-info-rgb: 98, 108, 113;
81
-
82
- /* Typography */
83
- --font-family-base: "FKGroteskNeue", "Geist", "Inter", -apple-system,
84
- BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
85
- --font-family-mono: "Berkeley Mono", ui-monospace, SFMono-Regular, Menlo,
86
- Monaco, Consolas, monospace;
87
- --font-size-xs: 11px;
88
- --font-size-sm: 12px;
89
- --font-size-base: 14px;
90
- --font-size-md: 14px;
91
- --font-size-lg: 16px;
92
- --font-size-xl: 18px;
93
- --font-size-2xl: 20px;
94
- --font-size-3xl: 24px;
95
- --font-size-4xl: 30px;
96
- --font-weight-normal: 400;
97
- --font-weight-medium: 500;
98
- --font-weight-semibold: 550;
99
- --font-weight-bold: 600;
100
- --line-height-tight: 1.2;
101
- --line-height-normal: 1.5;
102
- --letter-spacing-tight: -0.01em;
103
-
104
- /* Spacing */
105
- --space-0: 0;
106
- --space-1: 1px;
107
- --space-2: 2px;
108
- --space-4: 4px;
109
- --space-6: 6px;
110
- --space-8: 8px;
111
- --space-10: 10px;
112
- --space-12: 12px;
113
- --space-16: 16px;
114
- --space-20: 20px;
115
- --space-24: 24px;
116
- --space-32: 32px;
117
-
118
- /* Border Radius */
119
- --radius-sm: 6px;
120
- --radius-base: 8px;
121
- --radius-md: 10px;
122
- --radius-lg: 12px;
123
- --radius-full: 9999px;
124
-
125
- /* Shadows */
126
- --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.02);
127
- --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
128
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04),
129
- 0 2px 4px -1px rgba(0, 0, 0, 0.02);
130
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.04),
131
- 0 4px 6px -2px rgba(0, 0, 0, 0.02);
132
- --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.15),
133
- inset 0 -1px 0 rgba(0, 0, 0, 0.03);
134
-
135
- /* Animation */
136
- --duration-fast: 150ms;
137
- --duration-normal: 250ms;
138
- --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
139
-
140
- /* Layout */
141
- --container-sm: 640px;
142
- --container-md: 768px;
143
- --container-lg: 1024px;
144
- --container-xl: 1280px;
 
 
 
 
 
 
 
 
145
  }
146
 
147
  /* Dark mode colors */
148
  @media (prefers-color-scheme: dark) {
149
- :root {
150
- /* RGB versions for opacity control (Dark Mode) */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  --color-gray-400-rgb: 119, 124, 124;
152
  --color-teal-300-rgb: 50, 184, 198;
153
  --color-gray-300-rgb: 167, 169, 169;
154
  --color-gray-200-rgb: 245, 245, 245;
155
 
156
- /* Background color tokens (Dark Mode) */
157
- --color-bg-1: rgba(29, 78, 216, 0.15); /* Dark blue */
158
- --color-bg-2: rgba(180, 83, 9, 0.15); /* Dark yellow */
159
- --color-bg-3: rgba(21, 128, 61, 0.15); /* Dark green */
160
- --color-bg-4: rgba(185, 28, 28, 0.15); /* Dark red */
161
- --color-bg-5: rgba(107, 33, 168, 0.15); /* Dark purple */
162
- --color-bg-6: rgba(194, 65, 12, 0.15); /* Dark orange */
163
- --color-bg-7: rgba(190, 24, 93, 0.15); /* Dark pink */
164
- --color-bg-8: rgba(8, 145, 178, 0.15); /* Dark cyan */
 
 
 
 
 
 
 
 
165
 
166
  /* Semantic Color Tokens (Dark Mode) */
167
  --color-background: var(--color-charcoal-700);
@@ -181,11 +265,10 @@
181
  --color-info: var(--color-gray-300);
182
  --color-focus-ring: rgba(var(--color-teal-300-rgb), 0.4);
183
  --color-btn-primary-text: var(--color-slate-900);
184
- --color-card-border: rgba(var(--color-gray-400-rgb), 0.2);
185
  --color-card-border-inner: rgba(var(--color-gray-400-rgb), 0.15);
186
  --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
187
- inset 0 -1px 0 rgba(0, 0, 0, 0.15);
188
- --button-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
189
  --color-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
190
  --color-select-caret: rgba(var(--color-gray-200-rgb), 0.8);
191
 
@@ -202,121 +285,62 @@
202
  --color-error-rgb: var(--color-red-400-rgb);
203
  --color-warning-rgb: var(--color-orange-400-rgb);
204
  --color-info-rgb: var(--color-gray-300-rgb);
205
- }
206
- }
207
-
208
- /* Data attribute for manual theme switching */
209
- [data-color-scheme="dark"] {
210
- /* RGB versions for opacity control (dark mode) */
211
- --color-gray-400-rgb: 119, 124, 124;
212
- --color-teal-300-rgb: 50, 184, 198;
213
- --color-gray-300-rgb: 167, 169, 169;
214
- --color-gray-200-rgb: 245, 245, 245;
215
-
216
- /* Colorful background palette - Dark Mode */
217
- --color-bg-1: rgba(29, 78, 216, 0.15); /* Dark blue */
218
- --color-bg-2: rgba(180, 83, 9, 0.15); /* Dark yellow */
219
- --color-bg-3: rgba(21, 128, 61, 0.15); /* Dark green */
220
- --color-bg-4: rgba(185, 28, 28, 0.15); /* Dark red */
221
- --color-bg-5: rgba(107, 33, 168, 0.15); /* Dark purple */
222
- --color-bg-6: rgba(194, 65, 12, 0.15); /* Dark orange */
223
- --color-bg-7: rgba(190, 24, 93, 0.15); /* Dark pink */
224
- --color-bg-8: rgba(8, 145, 178, 0.15); /* Dark cyan */
225
-
226
- /* Semantic Color Tokens (Dark Mode) */
227
- --color-background: var(--color-charcoal-700);
228
- --color-surface: var(--color-charcoal-800);
229
- --color-text: var(--color-gray-200);
230
- --color-text-secondary: rgba(var(--color-gray-300-rgb), 0.7);
231
- --color-primary: var(--color-teal-300);
232
- --color-primary-hover: var(--color-teal-400);
233
- --color-primary-active: var(--color-teal-800);
234
- --color-secondary: rgba(var(--color-gray-400-rgb), 0.15);
235
- --color-secondary-hover: rgba(var(--color-gray-400-rgb), 0.25);
236
- --color-secondary-active: rgba(var(--color-gray-400-rgb), 0.3);
237
- --color-border: rgba(var(--color-gray-400-rgb), 0.3);
238
- --color-error: var(--color-red-400);
239
- --color-success: var(--color-teal-300);
240
- --color-warning: var(--color-orange-400);
241
- --color-info: var(--color-gray-300);
242
- --color-focus-ring: rgba(var(--color-teal-300-rgb), 0.4);
243
- --color-btn-primary-text: var(--color-slate-900);
244
- --color-card-border: rgba(var(--color-gray-400-rgb), 0.15);
245
- --color-card-border-inner: rgba(var(--color-gray-400-rgb), 0.15);
246
- --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
247
- inset 0 -1px 0 rgba(0, 0, 0, 0.15);
248
- --color-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
249
- --color-select-caret: rgba(var(--color-gray-200-rgb), 0.8);
250
-
251
- /* Common style patterns - updated for dark mode */
252
- --focus-ring: 0 0 0 3px var(--color-focus-ring);
253
- --focus-outline: 2px solid var(--color-primary);
254
- --status-bg-opacity: 0.15;
255
- --status-border-opacity: 0.25;
256
- --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
257
- --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
258
-
259
- /* RGB versions for dark mode */
260
- --color-success-rgb: var(--color-teal-300-rgb);
261
- --color-error-rgb: var(--color-red-400-rgb);
262
- --color-warning-rgb: var(--color-orange-400-rgb);
263
- --color-info-rgb: var(--color-gray-300-rgb);
264
  }
265
 
266
  [data-color-scheme="light"] {
267
- /* RGB versions for opacity control (light mode) */
268
- --color-brown-600-rgb: 94, 82, 64;
269
- --color-teal-500-rgb: 33, 128, 141;
270
- --color-slate-900-rgb: 19, 52, 59;
271
-
272
- /* Semantic Color Tokens (Light Mode) */
273
- --color-background: var(--color-cream-50);
274
- --color-surface: var(--color-cream-100);
275
- --color-text: var(--color-slate-900);
276
- --color-text-secondary: var(--color-slate-500);
277
- --color-primary: var(--color-teal-500);
278
- --color-primary-hover: var(--color-teal-600);
279
- --color-primary-active: var(--color-teal-700);
280
- --color-secondary: rgba(var(--color-brown-600-rgb), 0.12);
281
- --color-secondary-hover: rgba(var(--color-brown-600-rgb), 0.2);
282
- --color-secondary-active: rgba(var(--color-brown-600-rgb), 0.25);
283
- --color-border: rgba(var(--color-brown-600-rgb), 0.2);
284
- --color-btn-primary-text: var(--color-cream-50);
285
- --color-card-border: rgba(var(--color-brown-600-rgb), 0.12);
286
- --color-card-border-inner: rgba(var(--color-brown-600-rgb), 0.12);
287
- --color-error: var(--color-red-500);
288
- --color-success: var(--color-teal-500);
289
- --color-warning: var(--color-orange-500);
290
- --color-info: var(--color-slate-500);
291
- --color-focus-ring: rgba(var(--color-teal-500-rgb), 0.4);
292
-
293
- /* RGB versions for light mode */
294
- --color-success-rgb: var(--color-teal-500-rgb);
295
- --color-error-rgb: var(--color-red-500-rgb);
296
- --color-warning-rgb: var(--color-orange-500-rgb);
297
- --color-info-rgb: var(--color-slate-500-rgb);
298
  }
299
 
300
  /* Base styles */
301
  html {
302
- font-size: var(--font-size-base);
303
- font-family: var(--font-family-base);
304
- line-height: var(--line-height-normal);
305
- color: var(--color-text);
306
- background-color: var(--color-background);
307
- -webkit-font-smoothing: antialiased;
308
- box-sizing: border-box;
309
  }
310
 
311
  body {
312
- margin: 0;
313
- padding: 0;
314
  }
315
 
316
  *,
317
  *::before,
318
  *::after {
319
- box-sizing: inherit;
320
  }
321
 
322
  /* Typography */
@@ -326,415 +350,426 @@ h3,
326
  h4,
327
  h5,
328
  h6 {
329
- margin: 0;
330
- font-weight: var(--font-weight-semibold);
331
- line-height: var(--line-height-tight);
332
- color: var(--color-text);
333
- letter-spacing: var(--letter-spacing-tight);
334
  }
335
 
336
  h1 {
337
- font-size: var(--font-size-4xl);
338
  }
 
339
  h2 {
340
- font-size: var(--font-size-3xl);
341
  }
 
342
  h3 {
343
- font-size: var(--font-size-2xl);
344
  }
 
345
  h4 {
346
- font-size: var(--font-size-xl);
347
  }
 
348
  h5 {
349
- font-size: var(--font-size-lg);
350
  }
 
351
  h6 {
352
- font-size: var(--font-size-md);
353
  }
354
 
355
  p {
356
- margin: 0 0 var(--space-16) 0;
357
  }
358
 
359
  a {
360
- color: var(--color-primary);
361
- text-decoration: none;
362
- transition: color var(--duration-fast) var(--ease-standard);
363
  }
364
 
365
  a:hover {
366
- color: var(--color-primary-hover);
367
  }
368
 
369
  code,
370
  pre {
371
- font-family: var(--font-family-mono);
372
- font-size: calc(var(--font-size-base) * 0.95);
373
- background-color: var(--color-secondary);
374
- border-radius: var(--radius-sm);
375
  }
376
 
377
  code {
378
- padding: var(--space-1) var(--space-4);
379
  }
380
 
381
  pre {
382
- padding: var(--space-16);
383
- margin: var(--space-16) 0;
384
- overflow: auto;
385
- border: 1px solid var(--color-border);
386
  }
387
 
388
  pre code {
389
- background: none;
390
- padding: 0;
391
  }
392
 
393
  /* Buttons */
394
  .btn {
395
- display: inline-flex;
396
- align-items: center;
397
- justify-content: center;
398
- padding: var(--space-8) var(--space-16);
399
- border-radius: var(--radius-base);
400
- font-size: var(--font-size-base);
401
- font-weight: 500;
402
- line-height: 1.5;
403
- cursor: pointer;
404
- transition: all var(--duration-normal) var(--ease-standard);
405
- border: none;
406
- text-decoration: none;
407
- position: relative;
408
  }
409
 
410
  .btn:focus-visible {
411
- outline: none;
412
- box-shadow: var(--focus-ring);
413
  }
414
 
415
  .btn--primary {
416
- background: var(--color-primary);
417
- color: var(--color-btn-primary-text);
418
  }
419
 
420
  .btn--primary:hover {
421
- background: var(--color-primary-hover);
422
  }
423
 
424
  .btn--primary:active {
425
- background: var(--color-primary-active);
426
  }
427
 
428
  .btn--secondary {
429
- background: var(--color-secondary);
430
- color: var(--color-text);
431
  }
432
 
433
  .btn--secondary:hover {
434
- background: var(--color-secondary-hover);
435
  }
436
 
437
  .btn--secondary:active {
438
- background: var(--color-secondary-active);
439
  }
440
 
441
  .btn--outline {
442
- background: transparent;
443
- border: 1px solid var(--color-border);
444
- color: var(--color-text);
445
  }
446
 
447
  .btn--outline:hover {
448
- background: var(--color-secondary);
449
  }
450
 
451
  .btn--sm {
452
- padding: var(--space-4) var(--space-12);
453
- font-size: var(--font-size-sm);
454
- border-radius: var(--radius-sm);
455
  }
456
 
457
  .btn--lg {
458
- padding: var(--space-10) var(--space-20);
459
- font-size: var(--font-size-lg);
460
- border-radius: var(--radius-md);
461
  }
462
 
463
  .btn--full-width {
464
- width: 100%;
465
  }
466
 
467
  .btn:disabled {
468
- opacity: 0.5;
469
- cursor: not-allowed;
470
  }
471
 
472
  /* Form elements */
473
  .form-control {
474
- display: block;
475
- width: 100%;
476
- padding: var(--space-8) var(--space-12);
477
- font-size: var(--font-size-md);
478
- line-height: 1.5;
479
- color: var(--color-text);
480
- background-color: var(--color-surface);
481
- border: 1px solid var(--color-border);
482
- border-radius: var(--radius-base);
483
- transition: border-color var(--duration-fast) var(--ease-standard),
484
- box-shadow var(--duration-fast) var(--ease-standard);
485
  }
486
 
487
  textarea.form-control {
488
- font-family: var(--font-family-base);
489
- font-size: var(--font-size-base);
490
  }
491
 
492
  select.form-control {
493
- padding: var(--space-8) var(--space-12);
494
- -webkit-appearance: none;
495
- -moz-appearance: none;
496
- appearance: none;
497
- background-image: var(--select-caret-light);
498
- background-repeat: no-repeat;
499
- background-position: right var(--space-12) center;
500
- background-size: 16px;
501
- padding-right: var(--space-32);
502
  }
503
 
504
  /* Add a dark mode specific caret */
505
  @media (prefers-color-scheme: dark) {
506
- select.form-control {
507
- background-image: var(--select-caret-dark);
508
- }
509
  }
510
 
511
  /* Also handle data-color-scheme */
512
  [data-color-scheme="dark"] select.form-control {
513
- background-image: var(--select-caret-dark);
514
  }
515
 
516
  [data-color-scheme="light"] select.form-control {
517
- background-image: var(--select-caret-light);
518
  }
519
 
520
  .form-control:focus {
521
- border-color: var(--color-primary);
522
- outline: var(--focus-outline);
523
  }
524
 
525
  .form-label {
526
- display: block;
527
- margin-bottom: var(--space-8);
528
- font-weight: var(--font-weight-medium);
529
- font-size: var(--font-size-sm);
530
  }
531
 
532
  .form-group {
533
- margin-bottom: var(--space-16);
534
  }
535
 
536
  /* Card component */
537
  .card {
538
- background-color: var(--color-surface);
539
- border-radius: var(--radius-lg);
540
- border: 1px solid var(--color-card-border);
541
- box-shadow: var(--shadow-sm);
542
- overflow: hidden;
543
- transition: box-shadow var(--duration-normal) var(--ease-standard);
544
  }
545
 
546
  .card:hover {
547
- box-shadow: var(--shadow-md);
548
  }
549
 
550
  .card__body {
551
- padding: var(--space-16);
552
  }
553
 
554
  .card__header,
555
  .card__footer {
556
- padding: var(--space-16);
557
- border-bottom: 1px solid var(--color-card-border-inner);
558
  }
559
 
560
  /* Status indicators - simplified with CSS variables */
561
  .status {
562
- display: inline-flex;
563
- align-items: center;
564
- padding: var(--space-6) var(--space-12);
565
- border-radius: var(--radius-full);
566
- font-weight: var(--font-weight-medium);
567
- font-size: var(--font-size-sm);
568
  }
569
 
570
  .status--success {
571
- background-color: rgba(
572
- var(--color-success-rgb, 33, 128, 141),
573
- var(--status-bg-opacity)
574
- );
575
- color: var(--color-success);
576
- border: 1px solid
577
- rgba(var(--color-success-rgb, 33, 128, 141), var(--status-border-opacity));
578
  }
579
 
580
  .status--error {
581
- background-color: rgba(
582
- var(--color-error-rgb, 192, 21, 47),
583
- var(--status-bg-opacity)
584
- );
585
- color: var(--color-error);
586
- border: 1px solid
587
- rgba(var(--color-error-rgb, 192, 21, 47), var(--status-border-opacity));
588
  }
589
 
590
  .status--warning {
591
- background-color: rgba(
592
- var(--color-warning-rgb, 168, 75, 47),
593
- var(--status-bg-opacity)
594
- );
595
- color: var(--color-warning);
596
- border: 1px solid
597
- rgba(var(--color-warning-rgb, 168, 75, 47), var(--status-border-opacity));
598
  }
599
 
600
  .status--info {
601
- background-color: rgba(
602
- var(--color-info-rgb, 98, 108, 113),
603
- var(--status-bg-opacity)
604
- );
605
- color: var(--color-info);
606
- border: 1px solid
607
- rgba(var(--color-info-rgb, 98, 108, 113), var(--status-border-opacity));
608
  }
609
 
610
  /* Container layout */
611
  .container {
612
- width: 100%;
613
- margin-right: auto;
614
- margin-left: auto;
615
- padding-right: var(--space-16);
616
- padding-left: var(--space-16);
617
  }
618
 
619
  @media (min-width: 640px) {
620
- .container {
621
- max-width: var(--container-sm);
622
- }
623
  }
 
624
  @media (min-width: 768px) {
625
- .container {
626
- max-width: var(--container-md);
627
- }
628
  }
 
629
  @media (min-width: 1024px) {
630
- .container {
631
- max-width: var(--container-lg);
632
- }
633
  }
 
634
  @media (min-width: 1280px) {
635
- .container {
636
- max-width: var(--container-xl);
637
- }
638
  }
639
 
640
  /* Utility classes */
641
  .flex {
642
- display: flex;
643
  }
 
644
  .flex-col {
645
- flex-direction: column;
646
  }
 
647
  .items-center {
648
- align-items: center;
649
  }
 
650
  .justify-center {
651
- justify-content: center;
652
  }
 
653
  .justify-between {
654
- justify-content: space-between;
655
  }
 
656
  .gap-4 {
657
- gap: var(--space-4);
658
  }
 
659
  .gap-8 {
660
- gap: var(--space-8);
661
  }
 
662
  .gap-16 {
663
- gap: var(--space-16);
664
  }
665
 
666
  .m-0 {
667
- margin: 0;
668
  }
 
669
  .mt-8 {
670
- margin-top: var(--space-8);
671
  }
 
672
  .mb-8 {
673
- margin-bottom: var(--space-8);
674
  }
 
675
  .mx-8 {
676
- margin-left: var(--space-8);
677
- margin-right: var(--space-8);
678
  }
 
679
  .my-8 {
680
- margin-top: var(--space-8);
681
- margin-bottom: var(--space-8);
682
  }
683
 
684
  .p-0 {
685
- padding: 0;
686
  }
 
687
  .py-8 {
688
- padding-top: var(--space-8);
689
- padding-bottom: var(--space-8);
690
  }
 
691
  .px-8 {
692
- padding-left: var(--space-8);
693
- padding-right: var(--space-8);
694
  }
 
695
  .py-16 {
696
- padding-top: var(--space-16);
697
- padding-bottom: var(--space-16);
698
  }
 
699
  .px-16 {
700
- padding-left: var(--space-16);
701
- padding-right: var(--space-16);
702
  }
703
 
704
  .block {
705
- display: block;
706
  }
 
707
  .hidden {
708
- display: none;
709
  }
710
 
711
  /* Accessibility */
712
  .sr-only {
713
- position: absolute;
714
- width: 1px;
715
- height: 1px;
716
- padding: 0;
717
- margin: -1px;
718
- overflow: hidden;
719
- clip: rect(0, 0, 0, 0);
720
- white-space: nowrap;
721
- border-width: 0;
722
  }
723
 
724
  :focus-visible {
725
- outline: var(--focus-outline);
726
- outline-offset: 2px;
727
  }
728
 
729
  /* Dark mode specifics */
730
  [data-color-scheme="dark"] .btn--outline {
731
- border: 1px solid var(--color-border-secondary);
732
  }
733
 
734
  @font-face {
735
- font-family: 'FKGroteskNeue';
736
- src: url('https://r2cdn.perplexity.ai/fonts/FKGroteskNeue.woff2')
737
- format('woff2');
738
  }
739
 
740
  /* END PERPLEXITY DESIGN SYSTEM */
@@ -1335,6 +1370,7 @@ body {
1335
  background: #0f3460;
1336
  outline: none;
1337
  -webkit-appearance: none;
 
1338
  }
1339
 
1340
  .slider::-webkit-slider-thumb {
@@ -1653,11 +1689,12 @@ body {
1653
  flex-wrap: wrap;
1654
  gap: 0.5rem;
1655
  }
1656
-
1657
  .subject-tab {
1658
  font-size: 0.8rem;
1659
  padding: 0.5rem 1rem;
1660
  }
 
1661
  .main-container {
1662
  flex-direction: column;
1663
  }
@@ -1728,11 +1765,11 @@ body {
1728
  padding: 30px;
1729
  margin: 40px 0;
1730
  border-left: 5px solid #64ffda;
1731
- box-shadow: 0 8px 16px rgba(0,0,0,0.3);
1732
  }
1733
 
1734
  .example-problem {
1735
- background: rgba(255,255,255,0.08);
1736
  padding: 25px;
1737
  border-radius: 10px;
1738
  margin-bottom: 30px;
@@ -1751,14 +1788,14 @@ body {
1751
  gap: 20px;
1752
  margin: 25px 0;
1753
  padding: 25px;
1754
- background: rgba(0,0,0,0.3);
1755
  border-radius: 10px;
1756
  border-left: 4px solid #4a90e2;
1757
  transition: all 0.3s ease;
1758
  }
1759
 
1760
  .solution-step:hover {
1761
- background: rgba(0,0,0,0.4);
1762
  border-left-width: 6px;
1763
  transform: translateX(5px);
1764
  }
@@ -1783,7 +1820,7 @@ body {
1783
  }
1784
 
1785
  .step-work {
1786
- background: rgba(0,0,0,0.4);
1787
  padding: 18px;
1788
  border-radius: 8px;
1789
  margin: 12px 0;
@@ -1811,7 +1848,7 @@ body {
1811
  width: 100%;
1812
  border-collapse: collapse;
1813
  margin: 15px 0;
1814
- background: rgba(0,0,0,0.2);
1815
  border-radius: 8px;
1816
  overflow: hidden;
1817
  }
@@ -1819,7 +1856,7 @@ body {
1819
  .calculation-table th,
1820
  .calculation-table td {
1821
  padding: 12px;
1822
- border: 1px solid rgba(255,255,255,0.1);
1823
  text-align: center;
1824
  }
1825
 
@@ -1840,7 +1877,7 @@ body {
1840
  margin-top: 30px;
1841
  text-align: center;
1842
  font-size: 18px;
1843
- box-shadow: 0 4px 12px rgba(81,207,102,0.3);
1844
  }
1845
 
1846
  .answer-highlight {
@@ -1849,7 +1886,7 @@ body {
1849
  color: white;
1850
  display: block;
1851
  margin-top: 12px;
1852
- text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
1853
  }
1854
 
1855
  .verification {
@@ -1914,7 +1951,7 @@ body {
1914
  }
1915
 
1916
  .practice-answers {
1917
- background: rgba(0,0,0,0.3);
1918
  padding: 20px;
1919
  border-radius: 8px;
1920
  margin-top: 15px;
@@ -1932,9 +1969,12 @@ body {
1932
 
1933
  /* Animations */
1934
  @keyframes pulse {
1935
- 0%, 100% {
 
 
1936
  opacity: 1;
1937
  }
 
1938
  50% {
1939
  opacity: 0.5;
1940
  }
@@ -1964,4 +2004,362 @@ body {
1964
  /* Smooth Transitions */
1965
  * {
1966
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1967
  }
 
1
  :root {
2
+ /* Primitive Color Tokens */
3
+ --color-white: rgba(255, 255, 255, 1);
4
+ --color-black: rgba(0, 0, 0, 1);
5
+ --color-cream-50: rgba(252, 252, 249, 1);
6
+ --color-cream-100: rgba(255, 255, 253, 1);
7
+ --color-gray-200: rgba(245, 245, 245, 1);
8
+ --color-gray-300: rgba(167, 169, 169, 1);
9
+ --color-gray-400: rgba(119, 124, 124, 1);
10
+ --color-slate-500: rgba(98, 108, 113, 1);
11
+ --color-brown-600: rgba(94, 82, 64, 1);
12
+ --color-charcoal-700: rgba(31, 33, 33, 1);
13
+ --color-charcoal-800: rgba(38, 40, 40, 1);
14
+ --color-slate-900: rgba(19, 52, 59, 1);
15
+ --color-teal-300: rgba(50, 184, 198, 1);
16
+ --color-teal-400: rgba(45, 166, 178, 1);
17
+ --color-teal-500: rgba(33, 128, 141, 1);
18
+ --color-teal-600: rgba(29, 116, 128, 1);
19
+ --color-teal-700: rgba(26, 104, 115, 1);
20
+ --color-teal-800: rgba(41, 150, 161, 1);
21
+ --color-red-400: rgba(255, 84, 89, 1);
22
+ --color-red-500: rgba(192, 21, 47, 1);
23
+ --color-orange-400: rgba(230, 129, 97, 1);
24
+ --color-orange-500: rgba(168, 75, 47, 1);
25
+
26
+ /* RGB versions for opacity control */
27
+ --color-brown-600-rgb: 94, 82, 64;
28
+ --color-teal-500-rgb: 33, 128, 141;
29
+ --color-slate-900-rgb: 19, 52, 59;
30
+ --color-slate-500-rgb: 98, 108, 113;
31
+ --color-red-500-rgb: 192, 21, 47;
32
+ --color-red-400-rgb: 255, 84, 89;
33
+ --color-orange-500-rgb: 168, 75, 47;
34
+ --color-orange-400-rgb: 230, 129, 97;
35
+
36
+ /* Background color tokens (Light Mode) */
37
+ --color-bg-1: rgba(59, 130, 246, 0.08);
38
+ /* Light blue */
39
+ --color-bg-2: rgba(245, 158, 11, 0.08);
40
+ /* Light yellow */
41
+ --color-bg-3: rgba(34, 197, 94, 0.08);
42
+ /* Light green */
43
+ --color-bg-4: rgba(239, 68, 68, 0.08);
44
+ /* Light red */
45
+ --color-bg-5: rgba(147, 51, 234, 0.08);
46
+ /* Light purple */
47
+ --color-bg-6: rgba(249, 115, 22, 0.08);
48
+ /* Light orange */
49
+ --color-bg-7: rgba(236, 72, 153, 0.08);
50
+ /* Light pink */
51
+ --color-bg-8: rgba(6, 182, 212, 0.08);
52
+ /* Light cyan */
53
+
54
+ /* Semantic Color Tokens (Light Mode) */
55
+ --color-background: var(--color-cream-50);
56
+ --color-surface: var(--color-cream-100);
57
+ --color-text: var(--color-slate-900);
58
+ --color-text-secondary: var(--color-slate-500);
59
+ --color-primary: var(--color-teal-500);
60
+ --color-primary-hover: var(--color-teal-600);
61
+ --color-primary-active: var(--color-teal-700);
62
+ --color-secondary: rgba(var(--color-brown-600-rgb), 0.12);
63
+ --color-secondary-hover: rgba(var(--color-brown-600-rgb), 0.2);
64
+ --color-secondary-active: rgba(var(--color-brown-600-rgb), 0.25);
65
+ --color-border: rgba(var(--color-brown-600-rgb), 0.2);
66
+ --color-btn-primary-text: var(--color-cream-50);
67
+ --color-card-border: rgba(var(--color-brown-600-rgb), 0.12);
68
+ --color-card-border-inner: rgba(var(--color-brown-600-rgb), 0.12);
69
+ --color-error: var(--color-red-500);
70
+ --color-success: var(--color-teal-500);
71
+ --color-warning: var(--color-orange-500);
72
+ --color-info: var(--color-slate-500);
73
+ --color-focus-ring: rgba(var(--color-teal-500-rgb), 0.4);
74
+ --color-select-caret: rgba(var(--color-slate-900-rgb), 0.8);
75
+
76
+ /* Common style patterns */
77
+ --focus-ring: 0 0 0 3px var(--color-focus-ring);
78
+ --focus-outline: 2px solid var(--color-primary);
79
+ --status-bg-opacity: 0.15;
80
+ --status-border-opacity: 0.25;
81
+ --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
82
+ --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
83
+
84
+ /* RGB versions for opacity control */
85
+ --color-success-rgb: 33, 128, 141;
86
+ --color-error-rgb: 192, 21, 47;
87
+ --color-warning-rgb: 168, 75, 47;
88
+ --color-info-rgb: 98, 108, 113;
89
+
90
+ /* Typography */
91
+ --font-family-base: "FKGroteskNeue", "Geist", "Inter", -apple-system,
92
+ BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
93
+ --font-family-mono: "Berkeley Mono", ui-monospace, SFMono-Regular, Menlo,
94
+ Monaco, Consolas, monospace;
95
+ --font-size-xs: 11px;
96
+ --font-size-sm: 12px;
97
+ --font-size-base: 14px;
98
+ --font-size-md: 14px;
99
+ --font-size-lg: 16px;
100
+ --font-size-xl: 18px;
101
+ --font-size-2xl: 20px;
102
+ --font-size-3xl: 24px;
103
+ --font-size-4xl: 30px;
104
+ --font-weight-normal: 400;
105
+ --font-weight-medium: 500;
106
+ --font-weight-semibold: 550;
107
+ --font-weight-bold: 600;
108
+ --line-height-tight: 1.2;
109
+ --line-height-normal: 1.5;
110
+ --letter-spacing-tight: -0.01em;
111
+
112
+ /* Spacing */
113
+ --space-0: 0;
114
+ --space-1: 1px;
115
+ --space-2: 2px;
116
+ --space-4: 4px;
117
+ --space-6: 6px;
118
+ --space-8: 8px;
119
+ --space-10: 10px;
120
+ --space-12: 12px;
121
+ --space-16: 16px;
122
+ --space-20: 20px;
123
+ --space-24: 24px;
124
+ --space-32: 32px;
125
+
126
+ /* Border Radius */
127
+ --radius-sm: 6px;
128
+ --radius-base: 8px;
129
+ --radius-md: 10px;
130
+ --radius-lg: 12px;
131
+ --radius-full: 9999px;
132
+
133
+ /* Shadows */
134
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.02);
135
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
136
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.04),
137
+ 0 2px 4px -1px rgba(0, 0, 0, 0.02);
138
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.04),
139
+ 0 4px 6px -2px rgba(0, 0, 0, 0.02);
140
+ --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.15),
141
+ inset 0 -1px 0 rgba(0, 0, 0, 0.03);
142
+
143
+ /* Animation */
144
+ --duration-fast: 150ms;
145
+ --duration-normal: 250ms;
146
+ --ease-standard: cubic-bezier(0.16, 1, 0.3, 1);
147
+
148
+ /* Layout */
149
+ --container-sm: 640px;
150
+ --container-md: 768px;
151
+ --container-lg: 1024px;
152
+ --container-xl: 1280px;
153
  }
154
 
155
  /* Dark mode colors */
156
  @media (prefers-color-scheme: dark) {
157
+ :root {
158
+ /* RGB versions for opacity control (Dark Mode) */
159
+ --color-gray-400-rgb: 119, 124, 124;
160
+ --color-teal-300-rgb: 50, 184, 198;
161
+ --color-gray-300-rgb: 167, 169, 169;
162
+ --color-gray-200-rgb: 245, 245, 245;
163
+
164
+ /* Background color tokens (Dark Mode) */
165
+ --color-bg-1: rgba(29, 78, 216, 0.15);
166
+ /* Dark blue */
167
+ --color-bg-2: rgba(180, 83, 9, 0.15);
168
+ /* Dark yellow */
169
+ --color-bg-3: rgba(21, 128, 61, 0.15);
170
+ /* Dark green */
171
+ --color-bg-4: rgba(185, 28, 28, 0.15);
172
+ /* Dark red */
173
+ --color-bg-5: rgba(107, 33, 168, 0.15);
174
+ /* Dark purple */
175
+ --color-bg-6: rgba(194, 65, 12, 0.15);
176
+ /* Dark orange */
177
+ --color-bg-7: rgba(190, 24, 93, 0.15);
178
+ /* Dark pink */
179
+ --color-bg-8: rgba(8, 145, 178, 0.15);
180
+ /* Dark cyan */
181
+
182
+ /* Semantic Color Tokens (Dark Mode) */
183
+ --color-background: var(--color-charcoal-700);
184
+ --color-surface: var(--color-charcoal-800);
185
+ --color-text: var(--color-gray-200);
186
+ --color-text-secondary: rgba(var(--color-gray-300-rgb), 0.7);
187
+ --color-primary: var(--color-teal-300);
188
+ --color-primary-hover: var(--color-teal-400);
189
+ --color-primary-active: var(--color-teal-800);
190
+ --color-secondary: rgba(var(--color-gray-400-rgb), 0.15);
191
+ --color-secondary-hover: rgba(var(--color-gray-400-rgb), 0.25);
192
+ --color-secondary-active: rgba(var(--color-gray-400-rgb), 0.3);
193
+ --color-border: rgba(var(--color-gray-400-rgb), 0.3);
194
+ --color-error: var(--color-red-400);
195
+ --color-success: var(--color-teal-300);
196
+ --color-warning: var(--color-orange-400);
197
+ --color-info: var(--color-gray-300);
198
+ --color-focus-ring: rgba(var(--color-teal-300-rgb), 0.4);
199
+ --color-btn-primary-text: var(--color-slate-900);
200
+ --color-card-border: rgba(var(--color-gray-400-rgb), 0.2);
201
+ --color-card-border-inner: rgba(var(--color-gray-400-rgb), 0.15);
202
+ --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
203
+ inset 0 -1px 0 rgba(0, 0, 0, 0.15);
204
+ --button-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
205
+ --color-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
206
+ --color-select-caret: rgba(var(--color-gray-200-rgb), 0.8);
207
+
208
+ /* Common style patterns - updated for dark mode */
209
+ --focus-ring: 0 0 0 3px var(--color-focus-ring);
210
+ --focus-outline: 2px solid var(--color-primary);
211
+ --status-bg-opacity: 0.15;
212
+ --status-border-opacity: 0.25;
213
+ --select-caret-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23134252' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
214
+ --select-caret-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23f5f5f5' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
215
+
216
+ /* RGB versions for dark mode */
217
+ --color-success-rgb: var(--color-teal-300-rgb);
218
+ --color-error-rgb: var(--color-red-400-rgb);
219
+ --color-warning-rgb: var(--color-orange-400-rgb);
220
+ --color-info-rgb: var(--color-gray-300-rgb);
221
+ }
222
+ }
223
+
224
+ /* Data attribute for manual theme switching */
225
+ [data-color-scheme="dark"] {
226
+ /* RGB versions for opacity control (dark mode) */
227
  --color-gray-400-rgb: 119, 124, 124;
228
  --color-teal-300-rgb: 50, 184, 198;
229
  --color-gray-300-rgb: 167, 169, 169;
230
  --color-gray-200-rgb: 245, 245, 245;
231
 
232
+ /* Colorful background palette - Dark Mode */
233
+ --color-bg-1: rgba(29, 78, 216, 0.15);
234
+ /* Dark blue */
235
+ --color-bg-2: rgba(180, 83, 9, 0.15);
236
+ /* Dark yellow */
237
+ --color-bg-3: rgba(21, 128, 61, 0.15);
238
+ /* Dark green */
239
+ --color-bg-4: rgba(185, 28, 28, 0.15);
240
+ /* Dark red */
241
+ --color-bg-5: rgba(107, 33, 168, 0.15);
242
+ /* Dark purple */
243
+ --color-bg-6: rgba(194, 65, 12, 0.15);
244
+ /* Dark orange */
245
+ --color-bg-7: rgba(190, 24, 93, 0.15);
246
+ /* Dark pink */
247
+ --color-bg-8: rgba(8, 145, 178, 0.15);
248
+ /* Dark cyan */
249
 
250
  /* Semantic Color Tokens (Dark Mode) */
251
  --color-background: var(--color-charcoal-700);
 
265
  --color-info: var(--color-gray-300);
266
  --color-focus-ring: rgba(var(--color-teal-300-rgb), 0.4);
267
  --color-btn-primary-text: var(--color-slate-900);
268
+ --color-card-border: rgba(var(--color-gray-400-rgb), 0.15);
269
  --color-card-border-inner: rgba(var(--color-gray-400-rgb), 0.15);
270
  --shadow-inset-sm: inset 0 1px 0 rgba(255, 255, 255, 0.1),
271
+ inset 0 -1px 0 rgba(0, 0, 0, 0.15);
 
272
  --color-border-secondary: rgba(var(--color-gray-400-rgb), 0.2);
273
  --color-select-caret: rgba(var(--color-gray-200-rgb), 0.8);
274
 
 
285
  --color-error-rgb: var(--color-red-400-rgb);
286
  --color-warning-rgb: var(--color-orange-400-rgb);
287
  --color-info-rgb: var(--color-gray-300-rgb);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
289
 
290
  [data-color-scheme="light"] {
291
+ /* RGB versions for opacity control (light mode) */
292
+ --color-brown-600-rgb: 94, 82, 64;
293
+ --color-teal-500-rgb: 33, 128, 141;
294
+ --color-slate-900-rgb: 19, 52, 59;
295
+
296
+ /* Semantic Color Tokens (Light Mode) */
297
+ --color-background: var(--color-cream-50);
298
+ --color-surface: var(--color-cream-100);
299
+ --color-text: var(--color-slate-900);
300
+ --color-text-secondary: var(--color-slate-500);
301
+ --color-primary: var(--color-teal-500);
302
+ --color-primary-hover: var(--color-teal-600);
303
+ --color-primary-active: var(--color-teal-700);
304
+ --color-secondary: rgba(var(--color-brown-600-rgb), 0.12);
305
+ --color-secondary-hover: rgba(var(--color-brown-600-rgb), 0.2);
306
+ --color-secondary-active: rgba(var(--color-brown-600-rgb), 0.25);
307
+ --color-border: rgba(var(--color-brown-600-rgb), 0.2);
308
+ --color-btn-primary-text: var(--color-cream-50);
309
+ --color-card-border: rgba(var(--color-brown-600-rgb), 0.12);
310
+ --color-card-border-inner: rgba(var(--color-brown-600-rgb), 0.12);
311
+ --color-error: var(--color-red-500);
312
+ --color-success: var(--color-teal-500);
313
+ --color-warning: var(--color-orange-500);
314
+ --color-info: var(--color-slate-500);
315
+ --color-focus-ring: rgba(var(--color-teal-500-rgb), 0.4);
316
+
317
+ /* RGB versions for light mode */
318
+ --color-success-rgb: var(--color-teal-500-rgb);
319
+ --color-error-rgb: var(--color-red-500-rgb);
320
+ --color-warning-rgb: var(--color-orange-500-rgb);
321
+ --color-info-rgb: var(--color-slate-500-rgb);
322
  }
323
 
324
  /* Base styles */
325
  html {
326
+ font-size: var(--font-size-base);
327
+ font-family: var(--font-family-base);
328
+ line-height: var(--line-height-normal);
329
+ color: var(--color-text);
330
+ background-color: var(--color-background);
331
+ -webkit-font-smoothing: antialiased;
332
+ box-sizing: border-box;
333
  }
334
 
335
  body {
336
+ margin: 0;
337
+ padding: 0;
338
  }
339
 
340
  *,
341
  *::before,
342
  *::after {
343
+ box-sizing: inherit;
344
  }
345
 
346
  /* Typography */
 
350
  h4,
351
  h5,
352
  h6 {
353
+ margin: 0;
354
+ font-weight: var(--font-weight-semibold);
355
+ line-height: var(--line-height-tight);
356
+ color: var(--color-text);
357
+ letter-spacing: var(--letter-spacing-tight);
358
  }
359
 
360
  h1 {
361
+ font-size: var(--font-size-4xl);
362
  }
363
+
364
  h2 {
365
+ font-size: var(--font-size-3xl);
366
  }
367
+
368
  h3 {
369
+ font-size: var(--font-size-2xl);
370
  }
371
+
372
  h4 {
373
+ font-size: var(--font-size-xl);
374
  }
375
+
376
  h5 {
377
+ font-size: var(--font-size-lg);
378
  }
379
+
380
  h6 {
381
+ font-size: var(--font-size-md);
382
  }
383
 
384
  p {
385
+ margin: 0 0 var(--space-16) 0;
386
  }
387
 
388
  a {
389
+ color: var(--color-primary);
390
+ text-decoration: none;
391
+ transition: color var(--duration-fast) var(--ease-standard);
392
  }
393
 
394
  a:hover {
395
+ color: var(--color-primary-hover);
396
  }
397
 
398
  code,
399
  pre {
400
+ font-family: var(--font-family-mono);
401
+ font-size: calc(var(--font-size-base) * 0.95);
402
+ background-color: var(--color-secondary);
403
+ border-radius: var(--radius-sm);
404
  }
405
 
406
  code {
407
+ padding: var(--space-1) var(--space-4);
408
  }
409
 
410
  pre {
411
+ padding: var(--space-16);
412
+ margin: var(--space-16) 0;
413
+ overflow: auto;
414
+ border: 1px solid var(--color-border);
415
  }
416
 
417
  pre code {
418
+ background: none;
419
+ padding: 0;
420
  }
421
 
422
  /* Buttons */
423
  .btn {
424
+ display: inline-flex;
425
+ align-items: center;
426
+ justify-content: center;
427
+ padding: var(--space-8) var(--space-16);
428
+ border-radius: var(--radius-base);
429
+ font-size: var(--font-size-base);
430
+ font-weight: 500;
431
+ line-height: 1.5;
432
+ cursor: pointer;
433
+ transition: all var(--duration-normal) var(--ease-standard);
434
+ border: none;
435
+ text-decoration: none;
436
+ position: relative;
437
  }
438
 
439
  .btn:focus-visible {
440
+ outline: none;
441
+ box-shadow: var(--focus-ring);
442
  }
443
 
444
  .btn--primary {
445
+ background: var(--color-primary);
446
+ color: var(--color-btn-primary-text);
447
  }
448
 
449
  .btn--primary:hover {
450
+ background: var(--color-primary-hover);
451
  }
452
 
453
  .btn--primary:active {
454
+ background: var(--color-primary-active);
455
  }
456
 
457
  .btn--secondary {
458
+ background: var(--color-secondary);
459
+ color: var(--color-text);
460
  }
461
 
462
  .btn--secondary:hover {
463
+ background: var(--color-secondary-hover);
464
  }
465
 
466
  .btn--secondary:active {
467
+ background: var(--color-secondary-active);
468
  }
469
 
470
  .btn--outline {
471
+ background: transparent;
472
+ border: 1px solid var(--color-border);
473
+ color: var(--color-text);
474
  }
475
 
476
  .btn--outline:hover {
477
+ background: var(--color-secondary);
478
  }
479
 
480
  .btn--sm {
481
+ padding: var(--space-4) var(--space-12);
482
+ font-size: var(--font-size-sm);
483
+ border-radius: var(--radius-sm);
484
  }
485
 
486
  .btn--lg {
487
+ padding: var(--space-10) var(--space-20);
488
+ font-size: var(--font-size-lg);
489
+ border-radius: var(--radius-md);
490
  }
491
 
492
  .btn--full-width {
493
+ width: 100%;
494
  }
495
 
496
  .btn:disabled {
497
+ opacity: 0.5;
498
+ cursor: not-allowed;
499
  }
500
 
501
  /* Form elements */
502
  .form-control {
503
+ display: block;
504
+ width: 100%;
505
+ padding: var(--space-8) var(--space-12);
506
+ font-size: var(--font-size-md);
507
+ line-height: 1.5;
508
+ color: var(--color-text);
509
+ background-color: var(--color-surface);
510
+ border: 1px solid var(--color-border);
511
+ border-radius: var(--radius-base);
512
+ transition: border-color var(--duration-fast) var(--ease-standard),
513
+ box-shadow var(--duration-fast) var(--ease-standard);
514
  }
515
 
516
  textarea.form-control {
517
+ font-family: var(--font-family-base);
518
+ font-size: var(--font-size-base);
519
  }
520
 
521
  select.form-control {
522
+ padding: var(--space-8) var(--space-12);
523
+ -webkit-appearance: none;
524
+ -moz-appearance: none;
525
+ appearance: none;
526
+ background-image: var(--select-caret-light);
527
+ background-repeat: no-repeat;
528
+ background-position: right var(--space-12) center;
529
+ background-size: 16px;
530
+ padding-right: var(--space-32);
531
  }
532
 
533
  /* Add a dark mode specific caret */
534
  @media (prefers-color-scheme: dark) {
535
+ select.form-control {
536
+ background-image: var(--select-caret-dark);
537
+ }
538
  }
539
 
540
  /* Also handle data-color-scheme */
541
  [data-color-scheme="dark"] select.form-control {
542
+ background-image: var(--select-caret-dark);
543
  }
544
 
545
  [data-color-scheme="light"] select.form-control {
546
+ background-image: var(--select-caret-light);
547
  }
548
 
549
  .form-control:focus {
550
+ border-color: var(--color-primary);
551
+ outline: var(--focus-outline);
552
  }
553
 
554
  .form-label {
555
+ display: block;
556
+ margin-bottom: var(--space-8);
557
+ font-weight: var(--font-weight-medium);
558
+ font-size: var(--font-size-sm);
559
  }
560
 
561
  .form-group {
562
+ margin-bottom: var(--space-16);
563
  }
564
 
565
  /* Card component */
566
  .card {
567
+ background-color: var(--color-surface);
568
+ border-radius: var(--radius-lg);
569
+ border: 1px solid var(--color-card-border);
570
+ box-shadow: var(--shadow-sm);
571
+ overflow: hidden;
572
+ transition: box-shadow var(--duration-normal) var(--ease-standard);
573
  }
574
 
575
  .card:hover {
576
+ box-shadow: var(--shadow-md);
577
  }
578
 
579
  .card__body {
580
+ padding: var(--space-16);
581
  }
582
 
583
  .card__header,
584
  .card__footer {
585
+ padding: var(--space-16);
586
+ border-bottom: 1px solid var(--color-card-border-inner);
587
  }
588
 
589
  /* Status indicators - simplified with CSS variables */
590
  .status {
591
+ display: inline-flex;
592
+ align-items: center;
593
+ padding: var(--space-6) var(--space-12);
594
+ border-radius: var(--radius-full);
595
+ font-weight: var(--font-weight-medium);
596
+ font-size: var(--font-size-sm);
597
  }
598
 
599
  .status--success {
600
+ background-color: rgba(var(--color-success-rgb, 33, 128, 141),
601
+ var(--status-bg-opacity));
602
+ color: var(--color-success);
603
+ border: 1px solid rgba(var(--color-success-rgb, 33, 128, 141), var(--status-border-opacity));
 
 
 
604
  }
605
 
606
  .status--error {
607
+ background-color: rgba(var(--color-error-rgb, 192, 21, 47),
608
+ var(--status-bg-opacity));
609
+ color: var(--color-error);
610
+ border: 1px solid rgba(var(--color-error-rgb, 192, 21, 47), var(--status-border-opacity));
 
 
 
611
  }
612
 
613
  .status--warning {
614
+ background-color: rgba(var(--color-warning-rgb, 168, 75, 47),
615
+ var(--status-bg-opacity));
616
+ color: var(--color-warning);
617
+ border: 1px solid rgba(var(--color-warning-rgb, 168, 75, 47), var(--status-border-opacity));
 
 
 
618
  }
619
 
620
  .status--info {
621
+ background-color: rgba(var(--color-info-rgb, 98, 108, 113),
622
+ var(--status-bg-opacity));
623
+ color: var(--color-info);
624
+ border: 1px solid rgba(var(--color-info-rgb, 98, 108, 113), var(--status-border-opacity));
 
 
 
625
  }
626
 
627
  /* Container layout */
628
  .container {
629
+ width: 100%;
630
+ margin-right: auto;
631
+ margin-left: auto;
632
+ padding-right: var(--space-16);
633
+ padding-left: var(--space-16);
634
  }
635
 
636
  @media (min-width: 640px) {
637
+ .container {
638
+ max-width: var(--container-sm);
639
+ }
640
  }
641
+
642
  @media (min-width: 768px) {
643
+ .container {
644
+ max-width: var(--container-md);
645
+ }
646
  }
647
+
648
  @media (min-width: 1024px) {
649
+ .container {
650
+ max-width: var(--container-lg);
651
+ }
652
  }
653
+
654
  @media (min-width: 1280px) {
655
+ .container {
656
+ max-width: var(--container-xl);
657
+ }
658
  }
659
 
660
  /* Utility classes */
661
  .flex {
662
+ display: flex;
663
  }
664
+
665
  .flex-col {
666
+ flex-direction: column;
667
  }
668
+
669
  .items-center {
670
+ align-items: center;
671
  }
672
+
673
  .justify-center {
674
+ justify-content: center;
675
  }
676
+
677
  .justify-between {
678
+ justify-content: space-between;
679
  }
680
+
681
  .gap-4 {
682
+ gap: var(--space-4);
683
  }
684
+
685
  .gap-8 {
686
+ gap: var(--space-8);
687
  }
688
+
689
  .gap-16 {
690
+ gap: var(--space-16);
691
  }
692
 
693
  .m-0 {
694
+ margin: 0;
695
  }
696
+
697
  .mt-8 {
698
+ margin-top: var(--space-8);
699
  }
700
+
701
  .mb-8 {
702
+ margin-bottom: var(--space-8);
703
  }
704
+
705
  .mx-8 {
706
+ margin-left: var(--space-8);
707
+ margin-right: var(--space-8);
708
  }
709
+
710
  .my-8 {
711
+ margin-top: var(--space-8);
712
+ margin-bottom: var(--space-8);
713
  }
714
 
715
  .p-0 {
716
+ padding: 0;
717
  }
718
+
719
  .py-8 {
720
+ padding-top: var(--space-8);
721
+ padding-bottom: var(--space-8);
722
  }
723
+
724
  .px-8 {
725
+ padding-left: var(--space-8);
726
+ padding-right: var(--space-8);
727
  }
728
+
729
  .py-16 {
730
+ padding-top: var(--space-16);
731
+ padding-bottom: var(--space-16);
732
  }
733
+
734
  .px-16 {
735
+ padding-left: var(--space-16);
736
+ padding-right: var(--space-16);
737
  }
738
 
739
  .block {
740
+ display: block;
741
  }
742
+
743
  .hidden {
744
+ display: none;
745
  }
746
 
747
  /* Accessibility */
748
  .sr-only {
749
+ position: absolute;
750
+ width: 1px;
751
+ height: 1px;
752
+ padding: 0;
753
+ margin: -1px;
754
+ overflow: hidden;
755
+ clip: rect(0, 0, 0, 0);
756
+ white-space: nowrap;
757
+ border-width: 0;
758
  }
759
 
760
  :focus-visible {
761
+ outline: var(--focus-outline);
762
+ outline-offset: 2px;
763
  }
764
 
765
  /* Dark mode specifics */
766
  [data-color-scheme="dark"] .btn--outline {
767
+ border: 1px solid var(--color-border-secondary);
768
  }
769
 
770
  @font-face {
771
+ font-family: 'FKGroteskNeue';
772
+ src: url('https://r2cdn.perplexity.ai/fonts/FKGroteskNeue.woff2') format('woff2');
 
773
  }
774
 
775
  /* END PERPLEXITY DESIGN SYSTEM */
 
1370
  background: #0f3460;
1371
  outline: none;
1372
  -webkit-appearance: none;
1373
+ appearance: none;
1374
  }
1375
 
1376
  .slider::-webkit-slider-thumb {
 
1689
  flex-wrap: wrap;
1690
  gap: 0.5rem;
1691
  }
1692
+
1693
  .subject-tab {
1694
  font-size: 0.8rem;
1695
  padding: 0.5rem 1rem;
1696
  }
1697
+
1698
  .main-container {
1699
  flex-direction: column;
1700
  }
 
1765
  padding: 30px;
1766
  margin: 40px 0;
1767
  border-left: 5px solid #64ffda;
1768
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
1769
  }
1770
 
1771
  .example-problem {
1772
+ background: rgba(255, 255, 255, 0.08);
1773
  padding: 25px;
1774
  border-radius: 10px;
1775
  margin-bottom: 30px;
 
1788
  gap: 20px;
1789
  margin: 25px 0;
1790
  padding: 25px;
1791
+ background: rgba(0, 0, 0, 0.3);
1792
  border-radius: 10px;
1793
  border-left: 4px solid #4a90e2;
1794
  transition: all 0.3s ease;
1795
  }
1796
 
1797
  .solution-step:hover {
1798
+ background: rgba(0, 0, 0, 0.4);
1799
  border-left-width: 6px;
1800
  transform: translateX(5px);
1801
  }
 
1820
  }
1821
 
1822
  .step-work {
1823
+ background: rgba(0, 0, 0, 0.4);
1824
  padding: 18px;
1825
  border-radius: 8px;
1826
  margin: 12px 0;
 
1848
  width: 100%;
1849
  border-collapse: collapse;
1850
  margin: 15px 0;
1851
+ background: rgba(0, 0, 0, 0.2);
1852
  border-radius: 8px;
1853
  overflow: hidden;
1854
  }
 
1856
  .calculation-table th,
1857
  .calculation-table td {
1858
  padding: 12px;
1859
+ border: 1px solid rgba(255, 255, 255, 0.1);
1860
  text-align: center;
1861
  }
1862
 
 
1877
  margin-top: 30px;
1878
  text-align: center;
1879
  font-size: 18px;
1880
+ box-shadow: 0 4px 12px rgba(81, 207, 102, 0.3);
1881
  }
1882
 
1883
  .answer-highlight {
 
1886
  color: white;
1887
  display: block;
1888
  margin-top: 12px;
1889
+ text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
1890
  }
1891
 
1892
  .verification {
 
1951
  }
1952
 
1953
  .practice-answers {
1954
+ background: rgba(0, 0, 0, 0.3);
1955
  padding: 20px;
1956
  border-radius: 8px;
1957
  margin-top: 15px;
 
1969
 
1970
  /* Animations */
1971
  @keyframes pulse {
1972
+
1973
+ 0%,
1974
+ 100% {
1975
  opacity: 1;
1976
  }
1977
+
1978
  50% {
1979
  opacity: 0.5;
1980
  }
 
2004
  /* Smooth Transitions */
2005
  * {
2006
  transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease;
2007
+ }
2008
+
2009
+ /* ========== SEARCH FUNCTIONALITY ========== */
2010
+ .search-container {
2011
+ position: relative;
2012
+ margin: 0 1.5rem;
2013
+ flex-shrink: 0;
2014
+ }
2015
+
2016
+ .search-input {
2017
+ width: 280px;
2018
+ padding: 0.7rem 1rem 0.7rem 1rem;
2019
+ background: rgba(255, 255, 255, 0.08);
2020
+ border: 1px solid rgba(100, 255, 218, 0.2);
2021
+ border-radius: 10px;
2022
+ color: #e1e1e1;
2023
+ font-size: 0.9rem;
2024
+ outline: none;
2025
+ transition: all 0.3s ease;
2026
+ backdrop-filter: blur(10px);
2027
+ }
2028
+
2029
+ .search-input::placeholder {
2030
+ color: rgba(255, 255, 255, 0.5);
2031
+ }
2032
+
2033
+ .search-input:focus {
2034
+ background: rgba(255, 255, 255, 0.12);
2035
+ border-color: rgba(100, 255, 218, 0.5);
2036
+ box-shadow: 0 0 20px rgba(100, 255, 218, 0.15);
2037
+ width: 320px;
2038
+ }
2039
+
2040
+ .search-results {
2041
+ position: absolute;
2042
+ top: 100%;
2043
+ left: 0;
2044
+ right: 0;
2045
+ background: rgba(26, 26, 46, 0.98);
2046
+ border: 1px solid rgba(100, 255, 218, 0.2);
2047
+ border-radius: 10px;
2048
+ margin-top: 0.5rem;
2049
+ max-height: 400px;
2050
+ overflow-y: auto;
2051
+ z-index: 1000;
2052
+ display: none;
2053
+ backdrop-filter: blur(20px);
2054
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
2055
+ }
2056
+
2057
+ .search-results.active {
2058
+ display: block;
2059
+ animation: slideDown 0.2s ease;
2060
+ }
2061
+
2062
+ @keyframes slideDown {
2063
+ from {
2064
+ opacity: 0;
2065
+ transform: translateY(-10px);
2066
+ }
2067
+
2068
+ to {
2069
+ opacity: 1;
2070
+ transform: translateY(0);
2071
+ }
2072
+ }
2073
+
2074
+ .search-result-item {
2075
+ padding: 0.8rem 1rem;
2076
+ cursor: pointer;
2077
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
2078
+ transition: all 0.2s ease;
2079
+ }
2080
+
2081
+ .search-result-item:hover {
2082
+ background: rgba(100, 255, 218, 0.1);
2083
+ }
2084
+
2085
+ .search-result-item:last-child {
2086
+ border-bottom: none;
2087
+ }
2088
+
2089
+ .search-result-title {
2090
+ color: #64ffda;
2091
+ font-weight: 600;
2092
+ font-size: 0.95rem;
2093
+ margin-bottom: 0.2rem;
2094
+ }
2095
+
2096
+ .search-result-subject {
2097
+ color: rgba(255, 255, 255, 0.5);
2098
+ font-size: 0.8rem;
2099
+ text-transform: uppercase;
2100
+ letter-spacing: 0.5px;
2101
+ }
2102
+
2103
+ .search-highlight {
2104
+ background: rgba(100, 255, 218, 0.3);
2105
+ border-radius: 2px;
2106
+ padding: 0 2px;
2107
+ }
2108
+
2109
+ .no-results {
2110
+ padding: 1.5rem;
2111
+ text-align: center;
2112
+ color: rgba(255, 255, 255, 0.5);
2113
+ }
2114
+
2115
+ /* Topic completion checkbox */
2116
+ .topic-complete-btn {
2117
+ display: flex;
2118
+ align-items: center;
2119
+ gap: 0.5rem;
2120
+ padding: 0.5rem 1rem;
2121
+ background: rgba(100, 255, 218, 0.1);
2122
+ border: 1px solid rgba(100, 255, 218, 0.3);
2123
+ border-radius: 8px;
2124
+ color: #64ffda;
2125
+ font-size: 0.85rem;
2126
+ cursor: pointer;
2127
+ transition: all 0.3s ease;
2128
+ margin-top: 1rem;
2129
+ }
2130
+
2131
+ .topic-complete-btn:hover {
2132
+ background: rgba(100, 255, 218, 0.2);
2133
+ transform: translateY(-2px);
2134
+ }
2135
+
2136
+ .topic-complete-btn.completed {
2137
+ background: rgba(81, 207, 102, 0.2);
2138
+ border-color: #51cf66;
2139
+ color: #51cf66;
2140
+ }
2141
+
2142
+ .topic-complete-btn .check-icon {
2143
+ width: 18px;
2144
+ height: 18px;
2145
+ border: 2px solid currentColor;
2146
+ border-radius: 4px;
2147
+ display: flex;
2148
+ align-items: center;
2149
+ justify-content: center;
2150
+ font-size: 12px;
2151
+ }
2152
+
2153
+ .topic-complete-btn.completed .check-icon::after {
2154
+ content: '✓';
2155
+ }
2156
+
2157
+ /* Sidebar topic completed state */
2158
+ .topic-link.completed {
2159
+ color: #51cf66 !important;
2160
+ position: relative;
2161
+ }
2162
+
2163
+ .topic-link.completed::before {
2164
+ content: '✓ ';
2165
+ font-weight: bold;
2166
+ }
2167
+
2168
+ /* ========== MICRO-ANIMATIONS ========== */
2169
+ @keyframes fadeInUp {
2170
+ from {
2171
+ opacity: 0;
2172
+ transform: translateY(20px);
2173
+ }
2174
+
2175
+ to {
2176
+ opacity: 1;
2177
+ transform: translateY(0);
2178
+ }
2179
+ }
2180
+
2181
+ @keyframes shimmer {
2182
+ 0% {
2183
+ background-position: -200% 0;
2184
+ }
2185
+
2186
+ 100% {
2187
+ background-position: 200% 0;
2188
+ }
2189
+ }
2190
+
2191
+ @keyframes glow {
2192
+
2193
+ 0%,
2194
+ 100% {
2195
+ box-shadow: 0 0 5px rgba(100, 255, 218, 0.3);
2196
+ }
2197
+
2198
+ 50% {
2199
+ box-shadow: 0 0 20px rgba(100, 255, 218, 0.6);
2200
+ }
2201
+ }
2202
+
2203
+ .topic-section {
2204
+ animation: fadeInUp 0.5s ease forwards;
2205
+ }
2206
+
2207
+ .content-card {
2208
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
2209
+ }
2210
+
2211
+ .content-card:hover {
2212
+ transform: translateY(-3px);
2213
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
2214
+ }
2215
+
2216
+ .callout-box {
2217
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
2218
+ }
2219
+
2220
+ .callout-box:hover {
2221
+ transform: translateX(5px);
2222
+ }
2223
+
2224
+ /* Button hover effects */
2225
+ .btn-primary,
2226
+ .btn-secondary,
2227
+ .subject-tab {
2228
+ position: relative;
2229
+ overflow: hidden;
2230
+ }
2231
+
2232
+ .btn-primary::after,
2233
+ .btn-secondary::after {
2234
+ content: '';
2235
+ position: absolute;
2236
+ top: 50%;
2237
+ left: 50%;
2238
+ width: 0;
2239
+ height: 0;
2240
+ background: rgba(255, 255, 255, 0.2);
2241
+ border-radius: 50%;
2242
+ transform: translate(-50%, -50%);
2243
+ transition: width 0.6s ease, height 0.6s ease;
2244
+ }
2245
+
2246
+ .btn-primary:hover::after,
2247
+ .btn-secondary:hover::after {
2248
+ width: 300px;
2249
+ height: 300px;
2250
+ }
2251
+
2252
+ /* ========== GLASSMORPHISM EFFECTS ========== */
2253
+ .formula-card {
2254
+ background: rgba(255, 255, 255, 0.03) !important;
2255
+ backdrop-filter: blur(10px);
2256
+ border: 1px solid rgba(100, 255, 218, 0.1) !important;
2257
+ }
2258
+
2259
+ .summary-card {
2260
+ background: linear-gradient(135deg, rgba(100, 255, 218, 0.05), rgba(74, 144, 226, 0.05)) !important;
2261
+ backdrop-filter: blur(10px);
2262
+ border: 1px solid rgba(100, 255, 218, 0.2) !important;
2263
+ }
2264
+
2265
+ /* ========== IMPROVED TYPOGRAPHY ========== */
2266
+ body {
2267
+ font-family: 'Inter', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
2268
+ }
2269
+
2270
+ .formula-card,
2271
+ code,
2272
+ pre,
2273
+ .formula-main {
2274
+ font-family: 'JetBrains Mono', 'Consolas', monospace !important;
2275
+ }
2276
+
2277
+ /* MathJax styling */
2278
+ .MathJax {
2279
+ font-size: 1.1em !important;
2280
+ }
2281
+
2282
+ mjx-container[jax="CHTML"] {
2283
+ margin: 0.5em 0 !important;
2284
+ }
2285
+
2286
+ /* ========== INTERACTIVE ELEMENT IMPROVEMENTS ========== */
2287
+ canvas {
2288
+ border-radius: 12px;
2289
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
2290
+ transition: box-shadow 0.3s ease;
2291
+ }
2292
+
2293
+ canvas:hover {
2294
+ box-shadow: 0 8px 30px rgba(100, 255, 218, 0.2);
2295
+ }
2296
+
2297
+ .controls {
2298
+ display: flex;
2299
+ gap: 1rem;
2300
+ flex-wrap: wrap;
2301
+ align-items: center;
2302
+ padding: 1rem;
2303
+ background: rgba(0, 0, 0, 0.2);
2304
+ border-radius: 10px;
2305
+ margin-top: 1rem;
2306
+ }
2307
+
2308
+ .slider-group {
2309
+ display: flex;
2310
+ flex-direction: column;
2311
+ gap: 0.3rem;
2312
+ }
2313
+
2314
+ .slider-group label {
2315
+ font-size: 0.85rem;
2316
+ color: rgba(255, 255, 255, 0.7);
2317
+ }
2318
+
2319
+ input[type="range"] {
2320
+ -webkit-appearance: none;
2321
+ appearance: none;
2322
+ width: 150px;
2323
+ height: 6px;
2324
+ background: rgba(255, 255, 255, 0.1);
2325
+ border-radius: 3px;
2326
+ outline: none;
2327
+ }
2328
+
2329
+ input[type="range"]::-webkit-slider-thumb {
2330
+ -webkit-appearance: none;
2331
+ width: 16px;
2332
+ height: 16px;
2333
+ background: linear-gradient(135deg, #4a90e2, #64ffda);
2334
+ border-radius: 50%;
2335
+ cursor: pointer;
2336
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
2337
+ transition: transform 0.2s ease;
2338
+ }
2339
+
2340
+ input[type="range"]::-webkit-slider-thumb:hover {
2341
+ transform: scale(1.2);
2342
+ }
2343
+
2344
+ /* ========== RESPONSIVE ADJUSTMENTS ========== */
2345
+ @media (max-width: 1200px) {
2346
+ .search-container {
2347
+ order: 3;
2348
+ margin: 0.5rem 0;
2349
+ width: 100%;
2350
+ }
2351
+
2352
+ .search-input {
2353
+ width: 100%;
2354
+ }
2355
+
2356
+ .search-input:focus {
2357
+ width: 100%;
2358
+ }
2359
+ }
2360
+
2361
+ @media (max-width: 768px) {
2362
+ .search-container {
2363
+ display: none;
2364
+ }
2365
  }