yonigozlan HF Staff commited on
Commit
3119164
·
1 Parent(s): 8c45c35

fix truncated cards at the extremities, improve style

Browse files
Files changed (1) hide show
  1. app.py +141 -70
app.py CHANGED
@@ -638,7 +638,7 @@ def create_timeline_template():
638
  }
639
 
640
  .header h1 {
641
- font-size: 1.5rem;
642
  font-weight: 700;
643
  color: #2d3748;
644
  margin-bottom: 0.1rem;
@@ -647,7 +647,7 @@ def create_timeline_template():
647
 
648
  .header p {
649
  color: #666;
650
- font-size: 0.8rem;
651
  margin: 0;
652
  transition: color 0.3s ease;
653
  }
@@ -814,7 +814,7 @@ def create_timeline_template():
814
  .input-group label {
815
  font-weight: 500;
816
  color: #4a5568;
817
- font-size: 0.7rem;
818
  transition: color 0.3s ease;
819
  }
820
 
@@ -824,10 +824,10 @@ def create_timeline_template():
824
  }
825
 
826
  .input-group input {
827
- padding: 0.3rem 0.6rem;
828
  border: 1px solid #e2e8f0;
829
  border-radius: 5px;
830
- font-size: 0.7rem;
831
  transition: all 0.2s ease;
832
  background: white;
833
  }
@@ -898,10 +898,10 @@ def create_timeline_template():
898
  }
899
 
900
  .btn-small {
901
- padding: 0.4rem 0.6rem;
902
- font-size: 0.65rem;
903
  margin-top: 0;
904
- height: 32px;
905
  display: flex;
906
  align-items: center;
907
  justify-content: center;
@@ -910,14 +910,14 @@ def create_timeline_template():
910
  .modality-checkbox {
911
  display: flex;
912
  align-items: center;
913
- gap: 0.3rem;
914
- padding: 0.3rem 0.5rem;
915
  background: rgba(255, 255, 255, 0.9);
916
  border: 1px solid rgba(0, 0, 0, 0.1);
917
  border-radius: 8px;
918
  cursor: pointer;
919
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
920
- font-size: 0.65rem;
921
  font-weight: 500;
922
  position: relative;
923
  overflow: hidden;
@@ -967,8 +967,8 @@ def create_timeline_template():
967
 
968
  .modality-checkbox input[type="checkbox"] {
969
  appearance: none;
970
- width: 16px;
971
- height: 16px;
972
  border: 2px solid var(--modality-color, #8B5CF6);
973
  border-radius: 4px;
974
  position: relative;
@@ -1025,14 +1025,14 @@ def create_timeline_template():
1025
  .task-checkbox {
1026
  display: flex;
1027
  align-items: center;
1028
- gap: 0.3rem;
1029
- padding: 0.3rem 0.5rem;
1030
  background: rgba(255, 255, 255, 0.9);
1031
  border: 1px solid rgba(0, 0, 0, 0.1);
1032
  border-radius: 8px;
1033
  cursor: pointer;
1034
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1035
- font-size: 0.65rem;
1036
  font-weight: 500;
1037
  position: relative;
1038
  overflow: hidden;
@@ -1082,8 +1082,8 @@ def create_timeline_template():
1082
 
1083
  .task-checkbox input[type="checkbox"] {
1084
  appearance: none;
1085
- width: 16px;
1086
- height: 16px;
1087
  border: 2px solid var(--task-color, #6366f1);
1088
  border-radius: 4px;
1089
  background: white;
@@ -1301,20 +1301,20 @@ def create_timeline_template():
1301
  top: 15px;
1302
  right: 15px;
1303
  display: flex;
1304
- gap: 8px;
1305
  z-index: 20;
1306
  }
1307
 
1308
  .zoom-btn {
1309
- width: 36px;
1310
- height: 36px;
1311
  background: rgba(255, 255, 255, 0.9);
1312
- border-radius: 8px;
1313
  display: flex;
1314
  align-items: center;
1315
  justify-content: center;
1316
  cursor: pointer;
1317
- font-size: 1.1rem;
1318
  font-weight: 600;
1319
  color: #d97706;
1320
  border: 2px solid rgba(217, 119, 6, 0.2);
@@ -1351,12 +1351,17 @@ def create_timeline_template():
1351
  .zoom-indicator {
1352
  background: rgba(255, 255, 255, 0.95);
1353
  border-radius: 6px;
1354
- padding: 4px 8px;
1355
- font-size: 0.75rem;
1356
  color: #d97706;
1357
  font-weight: 500;
1358
  border: 1px solid rgba(217, 119, 6, 0.2);
1359
  transition: all 0.3s ease;
 
 
 
 
 
1360
  }
1361
 
1362
  /* Dark mode zoom indicator */
@@ -1490,8 +1495,8 @@ def create_timeline_template():
1490
  }
1491
 
1492
  .timeline-dot {
1493
- width: 18px;
1494
- height: 18px;
1495
  border-radius: 50%;
1496
  background: white;
1497
  border: 3px solid;
@@ -1511,12 +1516,12 @@ def create_timeline_template():
1511
 
1512
  .timeline-label {
1513
  position: absolute;
1514
- min-width: 100px;
1515
- max-width: 160px;
1516
- padding: 0.4rem 0.6rem;
1517
  background: white;
1518
  border-radius: 6px;
1519
- font-size: 0.75rem;
1520
  font-weight: 500;
1521
  text-align: center;
1522
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
@@ -1555,9 +1560,9 @@ def create_timeline_template():
1555
 
1556
  /* Expanded card styles */
1557
  .timeline-label.expanded {
1558
- max-width: 500px !important;
1559
- min-width: 400px !important;
1560
- width: 500px !important;
1561
  min-height: 250px !important;
1562
  text-align: left;
1563
  z-index: 9999 !important; /* Much higher than all other elements */
@@ -1584,7 +1589,7 @@ def create_timeline_template():
1584
 
1585
  .timeline-label .model-title {
1586
  font-weight: 600;
1587
- font-size: 0.85rem;
1588
  margin-bottom: 0.5rem;
1589
  color: #1f2937;
1590
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
@@ -1602,10 +1607,9 @@ def create_timeline_template():
1602
  }
1603
 
1604
  .timeline-label .model-description {
1605
- font-size: 0.7rem;
1606
  line-height: 1.4;
1607
  color: #4b5563;
1608
- margin-bottom: 0.5rem;
1609
  max-height: 0;
1610
  overflow: hidden;
1611
  transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), color 0.3s ease;
@@ -1618,8 +1622,8 @@ def create_timeline_template():
1618
  }
1619
 
1620
  .timeline-label.expanded .model-description {
1621
- max-height: 200px !important;
1622
- height: 200px !important;
1623
  position: relative;
1624
  overflow: hidden; /* Hide overflow for blur effect */
1625
  }
@@ -1631,21 +1635,23 @@ def create_timeline_template():
1631
  }
1632
 
1633
  .timeline-label.expanded .description-content {
1634
- max-height: 200px !important;
1635
- height: 200px !important;
 
 
1636
  overflow-y: auto;
1637
  padding-right: 8px; /* Space for scrollbar */
1638
  }
1639
 
1640
  .timeline-label .description-fade {
1641
- position: absolute;
1642
- bottom: -3px; /* Move down 3px to cover text artifact */
1643
  left: 0;
1644
- right: 0;
1645
- height: 43px; /* Increase height to compensate */
1646
  background: linear-gradient(to bottom,
1647
  rgba(255, 255, 255, 0) 0%,
1648
  rgba(255, 255, 255, 0.9) 50%,
 
1649
  rgba(255, 255, 255, 1) 100%);
1650
  opacity: 0;
1651
  transition: opacity 0.3s ease;
@@ -1658,6 +1664,7 @@ def create_timeline_template():
1658
  background: linear-gradient(to bottom,
1659
  rgba(55, 65, 81, 0) 0%,
1660
  rgba(55, 65, 81, 0.9) 50%,
 
1661
  rgba(55, 65, 81, 1) 100%);
1662
  }
1663
 
@@ -1682,9 +1689,9 @@ def create_timeline_template():
1682
  color: #f9fafb;
1683
  }
1684
 
1685
- .timeline-label .model-description h1 { font-size: 0.85rem; }
1686
- .timeline-label .model-description h2 { font-size: 0.8rem; }
1687
- .timeline-label .model-description h3 { font-size: 0.75rem; }
1688
 
1689
  .timeline-label .model-description p {
1690
  margin: 0.5em 0;
@@ -1695,7 +1702,7 @@ def create_timeline_template():
1695
  padding: 0.1em 0.3em;
1696
  border-radius: 3px;
1697
  font-family: 'Consolas', 'Monaco', monospace;
1698
- font-size: 0.65rem;
1699
  transition: all 0.3s ease;
1700
  }
1701
 
@@ -1745,14 +1752,14 @@ def create_timeline_template():
1745
  display: none;
1746
  text-decoration: none;
1747
  color: var(--modality-color, #8B5CF6);
1748
- font-size: 0.7rem;
1749
  font-weight: 600;
1750
  padding: 0.3rem 0.6rem;
1751
  border: 1px solid var(--modality-color, #8B5CF6);
1752
  border-radius: 4px;
1753
  background: rgba(255, 255, 255, 0.8);
1754
  transition: all 0.2s ease;
1755
- margin-top: 0.5rem;
1756
  text-align: center;
1757
  }
1758
 
@@ -1807,7 +1814,7 @@ def create_timeline_template():
1807
  }
1808
 
1809
  .tasks-label {
1810
- font-size: 0.7rem;
1811
  font-weight: 600;
1812
  color: #4a5568;
1813
  margin-bottom: 0.3rem;
@@ -1827,10 +1834,10 @@ def create_timeline_template():
1827
 
1828
  .task-badge {
1829
  display: inline-block;
1830
- padding: 0.2rem 0.4rem;
1831
  color: white;
1832
  border-radius: 4px;
1833
- font-size: 0.6rem;
1834
  font-weight: 500;
1835
  opacity: 0.9;
1836
  transition: all 0.2s ease;
@@ -1904,7 +1911,7 @@ def create_timeline_template():
1904
  }
1905
 
1906
  .timeline-date {
1907
- font-size: 0.65rem;
1908
  color: #9ca3af;
1909
  margin-top: 0.3rem;
1910
  font-weight: 500;
@@ -1944,7 +1951,7 @@ def create_timeline_template():
1944
  }
1945
 
1946
  .date-input-group label {
1947
- font-size: 0.7rem;
1948
  color: #4a5568;
1949
  font-weight: 600;
1950
  white-space: nowrap;
@@ -1957,13 +1964,13 @@ def create_timeline_template():
1957
  }
1958
 
1959
  .date-input-group input {
1960
- padding: 0.4rem 0.6rem;
1961
  border: 2px solid #e2e8f0;
1962
  border-radius: 8px;
1963
- font-size: 0.75rem;
1964
  background: white;
1965
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1966
- min-width: 130px;
1967
  text-align: center;
1968
  font-weight: 500;
1969
  color: #374151;
@@ -2034,7 +2041,7 @@ def create_timeline_template():
2034
 
2035
  .stat-label {
2036
  color: #666;
2037
- font-size: 0.7rem;
2038
  margin-top: 0.15rem;
2039
  transition: color 0.3s ease;
2040
  }
@@ -2194,6 +2201,7 @@ def create_timeline_template():
2194
  let zoomLevel = 1.0; // 1.0 = 100%, 0.5 = 50%, 2.0 = 200%
2195
  const minZoom = 0.3; // Minimum zoom (30%)
2196
  const maxZoom = 3.0; // Maximum zoom (300%)
 
2197
 
2198
  // Theme management
2199
  let currentTheme = 'light';
@@ -2525,6 +2533,9 @@ def create_timeline_template():
2525
  setTimeout(() => {
2526
  otherLabel.style.top = '';
2527
  otherLabel.style.bottom = '';
 
 
 
2528
  otherLabel.parentElement.style.zIndex = '';
2529
  }, 50);
2530
  }, 50);
@@ -2548,8 +2559,6 @@ def create_timeline_template():
2548
  const storedTasks = label.dataset.tasks ? JSON.parse(label.dataset.tasks) : [];
2549
  const formattedDescription = markdownToHtml(label.dataset.description);
2550
 
2551
-
2552
-
2553
  // Format tasks for display with task-specific colors
2554
  const tasksHtml = storedTasks && storedTasks.length > 0 ?
2555
  `<div class="model-tasks">
@@ -2557,7 +2566,8 @@ def create_timeline_template():
2557
  <div class="tasks-list">
2558
  ${storedTasks.map(task => {
2559
  const taskColor = getTaskColor(task);
2560
- return `<span class="task-badge" style="background-color: ${taskColor}">${task.replace(/-/g, ' ').replace(/\\b\\w/g, l => l.toUpperCase())}</span>`;
 
2561
  }).join('')}
2562
  </div>
2563
  </div>` :
@@ -2573,9 +2583,9 @@ def create_timeline_template():
2573
  <div class="description-fade"></div>
2574
  </div>
2575
  <a href="${label.dataset.learnMoreUrl}"
2576
- target="_blank"
2577
- class="learn-more"
2578
- onclick="event.stopPropagation()">
2579
  Learn More →
2580
  </a>
2581
  `;
@@ -2586,7 +2596,48 @@ def create_timeline_template():
2586
  // Ensure this timeline item is on top
2587
  item.style.zIndex = '10000';
2588
 
2589
- // Set positioning to keep the correct edge fixed (using pre-expansion position)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2590
  if (isAboveAxis) {
2591
  // Keep top edge fixed - set top position
2592
  label.style.bottom = 'auto';
@@ -2612,9 +2663,13 @@ def create_timeline_template():
2612
  setTimeout(() => {
2613
  label.style.top = '';
2614
  label.style.bottom = '';
2615
- item.style.zIndex = ''; // Reset z-index too
2616
- }, 50); // Small delay to ensure content is set
2617
- }, 50); // Small delay to start transition
 
 
 
 
2618
  }
2619
  });
2620
 
@@ -3018,6 +3073,11 @@ def create_timeline_template():
3018
  const taskFilters = document.getElementById('taskFilters');
3019
  taskFilters.innerHTML = '';
3020
 
 
 
 
 
 
3021
  data.tasks.forEach(task => {
3022
  const checkboxContainer = document.createElement('div');
3023
  checkboxContainer.className = 'task-checkbox';
@@ -3132,6 +3192,16 @@ def create_timeline_template():
3132
  return taskColors[taskKey] || "#6b7280"; // Default gray for unmapped tasks
3133
  }
3134
 
 
 
 
 
 
 
 
 
 
 
3135
  async function loadModalities() {
3136
  try {
3137
  const response = await fetch('/api/modalities');
@@ -3238,6 +3308,7 @@ def create_timeline_template():
3238
  setTimeout(() => {
3239
  label.style.top = '';
3240
  label.style.bottom = '';
 
3241
  label.parentElement.style.zIndex = '';
3242
  }, 50);
3243
  }, 50);
 
638
  }
639
 
640
  .header h1 {
641
+ font-size: 1.6rem;
642
  font-weight: 700;
643
  color: #2d3748;
644
  margin-bottom: 0.1rem;
 
647
 
648
  .header p {
649
  color: #666;
650
+ font-size: 0.85rem;
651
  margin: 0;
652
  transition: color 0.3s ease;
653
  }
 
814
  .input-group label {
815
  font-weight: 500;
816
  color: #4a5568;
817
+ font-size: 0.75rem;
818
  transition: color 0.3s ease;
819
  }
820
 
 
824
  }
825
 
826
  .input-group input {
827
+ padding: 0.35rem 0.65rem;
828
  border: 1px solid #e2e8f0;
829
  border-radius: 5px;
830
+ font-size: 0.75rem;
831
  transition: all 0.2s ease;
832
  background: white;
833
  }
 
898
  }
899
 
900
  .btn-small {
901
+ padding: 0.45rem 0.65rem;
902
+ font-size: 0.7rem;
903
  margin-top: 0;
904
+ height: 34px;
905
  display: flex;
906
  align-items: center;
907
  justify-content: center;
 
910
  .modality-checkbox {
911
  display: flex;
912
  align-items: center;
913
+ gap: 0.35rem;
914
+ padding: 0.35rem 0.55rem;
915
  background: rgba(255, 255, 255, 0.9);
916
  border: 1px solid rgba(0, 0, 0, 0.1);
917
  border-radius: 8px;
918
  cursor: pointer;
919
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
920
+ font-size: 0.7rem;
921
  font-weight: 500;
922
  position: relative;
923
  overflow: hidden;
 
967
 
968
  .modality-checkbox input[type="checkbox"] {
969
  appearance: none;
970
+ width: 17px;
971
+ height: 17px;
972
  border: 2px solid var(--modality-color, #8B5CF6);
973
  border-radius: 4px;
974
  position: relative;
 
1025
  .task-checkbox {
1026
  display: flex;
1027
  align-items: center;
1028
+ gap: 0.35rem;
1029
+ padding: 0.35rem 0.55rem;
1030
  background: rgba(255, 255, 255, 0.9);
1031
  border: 1px solid rgba(0, 0, 0, 0.1);
1032
  border-radius: 8px;
1033
  cursor: pointer;
1034
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1035
+ font-size: 0.7rem;
1036
  font-weight: 500;
1037
  position: relative;
1038
  overflow: hidden;
 
1082
 
1083
  .task-checkbox input[type="checkbox"] {
1084
  appearance: none;
1085
+ width: 17px;
1086
+ height: 17px;
1087
  border: 2px solid var(--task-color, #6366f1);
1088
  border-radius: 4px;
1089
  background: white;
 
1301
  top: 15px;
1302
  right: 15px;
1303
  display: flex;
1304
+ gap: 6px;
1305
  z-index: 20;
1306
  }
1307
 
1308
  .zoom-btn {
1309
+ width: 28px;
1310
+ height: 28px;
1311
  background: rgba(255, 255, 255, 0.9);
1312
+ border-radius: 6px;
1313
  display: flex;
1314
  align-items: center;
1315
  justify-content: center;
1316
  cursor: pointer;
1317
+ font-size: 0.9rem;
1318
  font-weight: 600;
1319
  color: #d97706;
1320
  border: 2px solid rgba(217, 119, 6, 0.2);
 
1351
  .zoom-indicator {
1352
  background: rgba(255, 255, 255, 0.95);
1353
  border-radius: 6px;
1354
+ padding: 2px 6px;
1355
+ font-size: 0.7rem;
1356
  color: #d97706;
1357
  font-weight: 500;
1358
  border: 1px solid rgba(217, 119, 6, 0.2);
1359
  transition: all 0.3s ease;
1360
+ display: flex;
1361
+ align-items: center;
1362
+ justify-content: center;
1363
+ min-width: 40px;
1364
+ height: 28px;
1365
  }
1366
 
1367
  /* Dark mode zoom indicator */
 
1495
  }
1496
 
1497
  .timeline-dot {
1498
+ width: 19px;
1499
+ height: 19px;
1500
  border-radius: 50%;
1501
  background: white;
1502
  border: 3px solid;
 
1516
 
1517
  .timeline-label {
1518
  position: absolute;
1519
+ min-width: 110px;
1520
+ max-width: 170px;
1521
+ padding: 0.5rem 0.7rem;
1522
  background: white;
1523
  border-radius: 6px;
1524
+ font-size: 0.85rem;
1525
  font-weight: 500;
1526
  text-align: center;
1527
  box-shadow: 0 3px 12px rgba(0, 0, 0, 0.12);
 
1560
 
1561
  /* Expanded card styles */
1562
  .timeline-label.expanded {
1563
+ max-width: 550px !important;
1564
+ min-width: 450px !important;
1565
+ width: 550px !important;
1566
  min-height: 250px !important;
1567
  text-align: left;
1568
  z-index: 9999 !important; /* Much higher than all other elements */
 
1589
 
1590
  .timeline-label .model-title {
1591
  font-weight: 600;
1592
+ font-size: 0.95rem;
1593
  margin-bottom: 0.5rem;
1594
  color: #1f2937;
1595
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
 
1607
  }
1608
 
1609
  .timeline-label .model-description {
1610
+ font-size: 0.8rem;
1611
  line-height: 1.4;
1612
  color: #4b5563;
 
1613
  max-height: 0;
1614
  overflow: hidden;
1615
  transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1), color 0.3s ease;
 
1622
  }
1623
 
1624
  .timeline-label.expanded .model-description {
1625
+ max-height: 175px !important;
1626
+ height: 175px !important;
1627
  position: relative;
1628
  overflow: hidden; /* Hide overflow for blur effect */
1629
  }
 
1635
  }
1636
 
1637
  .timeline-label.expanded .description-content {
1638
+ position: relative;
1639
+ bottom: 2px;
1640
+ max-height: 175px !important;
1641
+ height: 175px !important;
1642
  overflow-y: auto;
1643
  padding-right: 8px; /* Space for scrollbar */
1644
  }
1645
 
1646
  .timeline-label .description-fade {
1647
+ position: sticky;
1648
+ bottom: 0px; /* Position at the very bottom of the container */
1649
  left: 0;
1650
+ height: 15px;
 
1651
  background: linear-gradient(to bottom,
1652
  rgba(255, 255, 255, 0) 0%,
1653
  rgba(255, 255, 255, 0.9) 50%,
1654
+ rgba(255, 255, 255, 1) 95%);
1655
  rgba(255, 255, 255, 1) 100%);
1656
  opacity: 0;
1657
  transition: opacity 0.3s ease;
 
1664
  background: linear-gradient(to bottom,
1665
  rgba(55, 65, 81, 0) 0%,
1666
  rgba(55, 65, 81, 0.9) 50%,
1667
+ rgba(55, 65, 81, 1) 95%);
1668
  rgba(55, 65, 81, 1) 100%);
1669
  }
1670
 
 
1689
  color: #f9fafb;
1690
  }
1691
 
1692
+ .timeline-label .model-description h1 { font-size: 0.9rem; }
1693
+ .timeline-label .model-description h2 { font-size: 0.85rem; }
1694
+ .timeline-label .model-description h3 { font-size: 0.8rem; }
1695
 
1696
  .timeline-label .model-description p {
1697
  margin: 0.5em 0;
 
1702
  padding: 0.1em 0.3em;
1703
  border-radius: 3px;
1704
  font-family: 'Consolas', 'Monaco', monospace;
1705
+ font-size: 0.7rem;
1706
  transition: all 0.3s ease;
1707
  }
1708
 
 
1752
  display: none;
1753
  text-decoration: none;
1754
  color: var(--modality-color, #8B5CF6);
1755
+ font-size: 0.8rem;
1756
  font-weight: 600;
1757
  padding: 0.3rem 0.6rem;
1758
  border: 1px solid var(--modality-color, #8B5CF6);
1759
  border-radius: 4px;
1760
  background: rgba(255, 255, 255, 0.8);
1761
  transition: all 0.2s ease;
1762
+ margin-top: 0.2rem;
1763
  text-align: center;
1764
  }
1765
 
 
1814
  }
1815
 
1816
  .tasks-label {
1817
+ font-size: 0.8rem;
1818
  font-weight: 600;
1819
  color: #4a5568;
1820
  margin-bottom: 0.3rem;
 
1834
 
1835
  .task-badge {
1836
  display: inline-block;
1837
+ padding: 0.25rem 0.45rem;
1838
  color: white;
1839
  border-radius: 4px;
1840
+ font-size: 0.7rem;
1841
  font-weight: 500;
1842
  opacity: 0.9;
1843
  transition: all 0.2s ease;
 
1911
  }
1912
 
1913
  .timeline-date {
1914
+ font-size: 0.75rem;
1915
  color: #9ca3af;
1916
  margin-top: 0.3rem;
1917
  font-weight: 500;
 
1951
  }
1952
 
1953
  .date-input-group label {
1954
+ font-size: 0.75rem;
1955
  color: #4a5568;
1956
  font-weight: 600;
1957
  white-space: nowrap;
 
1964
  }
1965
 
1966
  .date-input-group input {
1967
+ padding: 0.45rem 0.65rem;
1968
  border: 2px solid #e2e8f0;
1969
  border-radius: 8px;
1970
+ font-size: 0.8rem;
1971
  background: white;
1972
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
1973
+ min-width: 135px;
1974
  text-align: center;
1975
  font-weight: 500;
1976
  color: #374151;
 
2041
 
2042
  .stat-label {
2043
  color: #666;
2044
+ font-size: 0.75rem;
2045
  margin-top: 0.15rem;
2046
  transition: color 0.3s ease;
2047
  }
 
2201
  let zoomLevel = 1.0; // 1.0 = 100%, 0.5 = 50%, 2.0 = 200%
2202
  const minZoom = 0.3; // Minimum zoom (30%)
2203
  const maxZoom = 3.0; // Maximum zoom (300%)
2204
+ let taskData = {}; // Store task data for easy lookup
2205
 
2206
  // Theme management
2207
  let currentTheme = 'light';
 
2533
  setTimeout(() => {
2534
  otherLabel.style.top = '';
2535
  otherLabel.style.bottom = '';
2536
+ otherLabel.style.left = '';
2537
+ otherLabel.style.right = '';
2538
+ otherLabel.style.transform = 'translateX(-50%)';
2539
  otherLabel.parentElement.style.zIndex = '';
2540
  }, 50);
2541
  }, 50);
 
2559
  const storedTasks = label.dataset.tasks ? JSON.parse(label.dataset.tasks) : [];
2560
  const formattedDescription = markdownToHtml(label.dataset.description);
2561
 
 
 
2562
  // Format tasks for display with task-specific colors
2563
  const tasksHtml = storedTasks && storedTasks.length > 0 ?
2564
  `<div class="model-tasks">
 
2566
  <div class="tasks-list">
2567
  ${storedTasks.map(task => {
2568
  const taskColor = getTaskColor(task);
2569
+ const taskName = getTaskDisplayName(task);
2570
+ return `<span class="task-badge" style="background-color: ${taskColor}">${taskName}</span>`;
2571
  }).join('')}
2572
  </div>
2573
  </div>` :
 
2583
  <div class="description-fade"></div>
2584
  </div>
2585
  <a href="${label.dataset.learnMoreUrl}"
2586
+ target="_blank"
2587
+ class="learn-more"
2588
+ onclick="event.stopPropagation()">
2589
  Learn More →
2590
  </a>
2591
  `;
 
2596
  // Ensure this timeline item is on top
2597
  item.style.zIndex = '10000';
2598
 
2599
+ // Calculate timeline container bounds
2600
+ const timelineContainer = document.querySelector('.timeline-container');
2601
+ const containerBounds = timelineContainer.getBoundingClientRect();
2602
+ const timelineItemRect = item.getBoundingClientRect();
2603
+
2604
+ // Get expanded card dimensions (it's now rendered with expanded content)
2605
+ const expandedRect = label.getBoundingClientRect();
2606
+ const cardWidth = 550; // Known width from CSS
2607
+ const cardHalfWidth = cardWidth / 2;
2608
+
2609
+ // Calculate timeline item's center position relative to container
2610
+ const itemCenterX = timelineItemRect.left + (timelineItemRect.width / 2) - containerBounds.left;
2611
+
2612
+ // Calculate default centered position bounds
2613
+ const defaultLeft = itemCenterX - cardHalfWidth;
2614
+ const defaultRight = itemCenterX + cardHalfWidth;
2615
+
2616
+ // Container padding to ensure cards don't touch edges
2617
+ const padding = 20;
2618
+ const containerWidth = containerBounds.width;
2619
+
2620
+ // Determine optimal positioning
2621
+ let finalTransform = 'translateX(-50%)'; // Default centered
2622
+ let finalLeft = '';
2623
+ let finalRight = '';
2624
+
2625
+ if (defaultLeft < padding) {
2626
+ // Card would extend beyond left edge - align to left with padding
2627
+ finalTransform = 'translateX(-5%)';
2628
+ finalLeft = '';
2629
+ } else if (defaultRight > (containerWidth - padding)) {
2630
+ // Card would extend beyond right edge - align to right with padding
2631
+ finalTransform = 'translateX(-95%)';
2632
+ finalRight = '';
2633
+ }
2634
+
2635
+ // Apply positioning adjustments
2636
+ label.style.transform = finalTransform;
2637
+ // if (finalLeft) label.style.left = finalLeft;
2638
+ // if (finalRight) label.style.right = finalRight;
2639
+
2640
+ // Set vertical positioning to keep the correct edge fixed
2641
  if (isAboveAxis) {
2642
  // Keep top edge fixed - set top position
2643
  label.style.bottom = 'auto';
 
2663
  setTimeout(() => {
2664
  label.style.top = '';
2665
  label.style.bottom = '';
2666
+ label.style.left = '';
2667
+ label.style.right = '';
2668
+ // Reset transform: depends on current position
2669
+ label.style.transform = 'translateX(-50%)';
2670
+ item.style.zIndex = '';
2671
+ }, 50);
2672
+ }, 50);
2673
  }
2674
  });
2675
 
 
3073
  const taskFilters = document.getElementById('taskFilters');
3074
  taskFilters.innerHTML = '';
3075
 
3076
+ // Store task data for easy lookup
3077
+ data.tasks.forEach(task => {
3078
+ taskData[task.key] = { name: task.name, color: task.color };
3079
+ });
3080
+
3081
  data.tasks.forEach(task => {
3082
  const checkboxContainer = document.createElement('div');
3083
  checkboxContainer.className = 'task-checkbox';
 
3192
  return taskColors[taskKey] || "#6b7280"; // Default gray for unmapped tasks
3193
  }
3194
 
3195
+ // Function to get task display name from loaded task data
3196
+ function getTaskDisplayName(taskKey) {
3197
+ // Use stored task data if available
3198
+ if (taskData[taskKey]) {
3199
+ return taskData[taskKey].name;
3200
+ }
3201
+ // Fallback to formatted key if not found
3202
+ return taskKey.replace(/-/g, ' ').replace(/\b\\w/g, l => l.toUpperCase());
3203
+ }
3204
+
3205
  async function loadModalities() {
3206
  try {
3207
  const response = await fetch('/api/modalities');
 
3308
  setTimeout(() => {
3309
  label.style.top = '';
3310
  label.style.bottom = '';
3311
+ label.style.transform = 'translateX(-50%)';
3312
  label.parentElement.style.zIndex = '';
3313
  }, 50);
3314
  }, 50);