OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      4a4be5dae958
DATE      2025-07-29
SUBJECT   sim: update all
FILES     7 CHANGED
HASH      4a4be5dae958
DATE      2025-07-29
SUBJECT   sim: update all
FILES     7 CHANGED
 

diff --git a/flake.lock b/flake.lock
index d15d620..aacffa5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -86,11 +86,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1753685245,
-        "narHash": "sha256-nFSuXwbWYevQlZc/P9qRGWT1JfiYbbuOIxx08PtS1Ug=",
+        "lastModified": 1753762429,
+        "narHash": "sha256-vAp1f/m7JFEstwf2ve0HsfuvTkiPD2mJWeCZ+5rDQ1E=",
         "owner": "ooks-io",
         "repo": "mop",
-        "rev": "6156bf245b8a1663badb5c1663372ada3f8757c2",
+        "rev": "3d85fa980d1fe8cde01384a790b8b5b093f44906",
         "type": "github"
       },
       "original": {
@@ -103,11 +103,11 @@
     "wowsims-upstream": {
       "flake": false,
       "locked": {
-        "lastModified": 1753683048,
-        "narHash": "sha256-nIeQRXq++k0nvTo11s948s5C5Ur/Hrh7aR8ENK+P6d8=",
+        "lastModified": 1753730192,
+        "narHash": "sha256-5PP87yeofSm/WIoxuLniJMvQfxLrb97E+h/Z2kQRDs0=",
         "owner": "wowsims",
         "repo": "mop",
-        "rev": "06153aa432c961a62ca8255eed5eb1ee1e7114ca",
+        "rev": "99ee7a6a9db6acb96e684459e68abaad90c0b608",
         "type": "github"
       },
       "original": {

diff --git a/flake.lock b/flake.lock
index d15d620..aacffa5 100644
--- a/flake.lock
+++ b/flake.lock
@@ -86,11 +86,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1753685245,
-        "narHash": "sha256-nFSuXwbWYevQlZc/
P9qRGWT1JfiYbbuOIxx08PtS1Ug=",
+        "lastModified": 1753762429,
+        "narHash": "sha256-vAp1f/m7JFEstwf2
ve0HsfuvTkiPD2mJWeCZ+5rDQ1E=",
         "owner": "ooks-io",
         "repo": "mop",
-        "rev": "6156bf245b8a1663badb5c16633
72ada3f8757c2",
+        "rev": "3d85fa980d1fe8cde01384a790b
8b5b093f44906",
         "type": "github"
       },
       "original": {
@@ -103,11 +103,11 @@
     "wowsims-upstream": {
       "flake": false,
       "locked": {
-        "lastModified": 1753683048,
-        "narHash": "sha256-nIeQRXq++k0nvTo1
1s948s5C5Ur/Hrh7aR8ENK+P6d8=",
+        "lastModified": 1753730192,
+        "narHash": "sha256-5PP87yeofSm/WIox
uLniJMvQfxLrb97E+h/Z2kQRDs0=",
         "owner": "wowsims",
         "repo": "mop",
-        "rev": "06153aa432c961a62ca8255eed5
eb1ee1e7114ca",
+        "rev": "99ee7a6a9db6acb96e684459e68
abaad90c0b608",
         "type": "github"
       },
       "original": {
 

diff --git a/web/.astro/settings.json b/web/.astro/settings.json
index 214ce60..0e40bfc 100644
--- a/web/.astro/settings.json
+++ b/web/.astro/settings.json
@@ -1,5 +1,5 @@
 {
-	"_variables": {
-		"lastUpdateCheck": 1753092924601
-	}
-}
\ No newline at end of file
+  "_variables": {
+    "lastUpdateCheck": 1753092924601
+  }
+}

diff --git a/web/.astro/settings.json b/web/
.astro/settings.json
index 214ce60..0e40bfc 100644
--- a/web/.astro/settings.json
+++ b/web/.astro/settings.json
@@ -1,5 +1,5 @@
 {
-	"_variables": {
-		"lastUpdateCheck": 1753092924601
-	}
-}
\ No newline at end of file
+  "_variables": {
+    "lastUpdateCheck": 1753092924601
+  }
+}
 

diff --git a/web/.astro/types.d.ts b/web/.astro/types.d.ts
index f964fe0..03d7cc4 100644
--- a/web/.astro/types.d.ts
+++ b/web/.astro/types.d.ts
@@ -1 +1,2 @@
 /// <reference types="astro/client" />
+/// <reference path="content.d.ts" />
\ No newline at end of file

diff --git a/web/.astro/types.d.ts b/web/.as
tro/types.d.ts
index f964fe0..03d7cc4 100644
--- a/web/.astro/types.d.ts
+++ b/web/.astro/types.d.ts
@@ -1 +1,2 @@
 /// <reference types="astro/client" />
+/// <reference path="content.d.ts" />
\ No newline at end of file
 

diff --git a/web/astro.config.mjs b/web/astro.config.mjs
index 8dae6ea..7914983 100644
--- a/web/astro.config.mjs
+++ b/web/astro.config.mjs
@@ -1,7 +1,7 @@
 // @ts-check
-import { defineConfig } from 'astro/config';
+import { defineConfig } from "astro/config";
 
 // https://astro.build/config
 export default defineConfig({
-  site: 'https://wowsimstats.com',
+  site: "https://wowsimstats.com",
 });

diff --git a/web/astro.config.mjs b/web/astr
o.config.mjs
index 8dae6ea..7914983 100644
--- a/web/astro.config.mjs
+++ b/web/astro.config.mjs
@@ -1,7 +1,7 @@
 // @ts-check
-import { defineConfig } from 'astro/config'
;
+import { defineConfig } from "astro/config"
;
 
 // https://astro.build/config
 export default defineConfig({
-  site: 'https://wowsimstats.com',
+  site: "https://wowsimstats.com",
 });
 

diff --git a/web/src/components/Callout.astro b/web/src/components/Callout.astro
index cd6e375..7de7613 100644
--- a/web/src/components/Callout.astro
+++ b/web/src/components/Callout.astro
@@ -104,4 +104,4 @@ const config = typeConfig[type];
   .callout-content strong {
     color: var(--typeColor);
   }
-</style>
\ No newline at end of file
+</style>

diff --git a/web/src/components/Callout.astr
o b/web/src/components/Callout.astro
index cd6e375..7de7613 100644
--- a/web/src/components/Callout.astro
+++ b/web/src/components/Callout.astro
@@ -104,4 +104,4 @@ const config = typeConfi
g[type];
   .callout-content strong {
     color: var(--typeColor);
   }
-</style>
\ No newline at end of file
+</style>
 

diff --git a/web/src/components/ItemDatabase.astro b/web/src/components/ItemDataba
se.astro
index 79364d2..7e40bd8 100644
--- a/web/src/components/ItemDatabase.astro
+++ b/web/src/components/ItemDatabase.astro
@@ -76,30 +76,30 @@ const statNames = {
 };
 
 // Global functions for item database functionality
-window.ItemDatabase = {
+(window as any).ItemDatabase = {
   // Helper function to format reforging
-  formatReforge: (reforgeId) => {
+  formatReforge: (reforgeId: any) => {
     if (!reforgeId || reforgeId === 0) return '';
-    const reforge = reforgeData[reforgeId.toString()];
+    const reforge = reforgeData[reforgeId.toString() as keyof typeof reforgeData]
;
     if (!reforge) return ` [R:${reforgeId}]`;
     
-    const fromStat = statNames[reforge.s1] || reforge.s1;
-    const toStat = statNames[reforge.s2] || reforge.s2;
+    const fromStat = statNames[reforge.s1 as keyof typeof statNames] || reforge.s
1;
+    const toStat = statNames[reforge.s2 as keyof typeof statNames] || reforge.s2;
     return ` [${fromStat} ? ${toStat}]`;
   },
 
   // Generate Wowhead URL for items
-  getWowheadUrl: (itemId) => {
+  getWowheadUrl: (itemId: any) => {
     return `https://www.wowhead.com/mop-classic/item=${itemId}`;
   },
 
   // Format equipment item with all details - uses enriched data from backend
-  formatEquipmentItem: (item) => {
+  formatEquipmentItem: (item: any) => {
     if (!item || !item.id) return null;
     
     // Use enriched name if available, otherwise fallback to ID
     const itemName = item.name || `Item ${item.id}`;
-    const wowheadUrl = window.ItemDatabase.getWowheadUrl(item.id);
+    const wowheadUrl = (window as any).ItemDatabase.getWowheadUrl(item.id);
     const iconUrl = item.icon ? `https://wow.zamimg.com/images/wow/icons/large/${
item.icon}.jpg` : null;
     
     // Build details array with specific layout structure
@@ -118,17 +118,17 @@ window.ItemDatabase = {
       details.push(`<div class="item-reforge">Reforged ${item.reforging.descripti
on}</div>`);
     } else if (item.reforging && typeof item.reforging === 'number') {
       // Fallback to manual reforge lookup if not enriched
-      const reforge = reforgeData[item.reforging.toString()];
+      const reforge = reforgeData[item.reforging.toString() as keyof typeof refor
geData];
       if (reforge) {
-        const fromStat = statNames[reforge.s1] || reforge.s1;
-        const toStat = statNames[reforge.s2] || reforge.s2;
+        const fromStat = statNames[reforge.s1 as keyof typeof statNames] || refor
ge.s1;
+        const toStat = statNames[reforge.s2 as keyof typeof statNames] || reforge
.s2;
         details.push(`<div class="item-reforge">Reforged ${fromStat} ? ${toStat}<
/div>`);
       }
     }
     
     // Gems - each gem on separate line with icon and white stats
     if (item.gems && item.gems.length > 0) {
-      item.gems.forEach(gem => {
+      item.gems.forEach((gem: any) => {
         if (gem && gem.name) {
           const gemIcon = gem.icon ? `<img src="https://wow.zamimg.com/images/wow
/icons/small/${gem.icon}.jpg" alt="${gem.name}" class="gem-icon-inline" />` : '';
           const gemStats = gem.stats && gem.stats.length > 0 ? gem.stats.join(', 
') : gem.name;
@@ -163,12 +163,12 @@ window.ItemDatabase = {
   },
 
   // Format gem information
-  formatGems: (gems) => {
+  formatGems: (gems: any) => {
     if (!gems || gems.length === 0) return '';
-    const activeGems = gems.filter(g => g && g !== 0);
+    const activeGems = gems.filter((g: any) => g && g !== 0);
     if (activeGems.length === 0) return '';
     
-    return activeGems.map(gemId => `Gem ${gemId}`).join(', ');
+    return activeGems.map((gemId: any) => `Gem ${gemId}`).join(', ');
   }
 };
 </script>
\ No newline at end of file

diff --git a/web/src/components/ItemDatabase
.astro b/web/src/components/ItemDatabase.ast
ro
index 79364d2..7e40bd8 100644
--- a/web/src/components/ItemDatabase.astro
+++ b/web/src/components/ItemDatabase.astro
@@ -76,30 +76,30 @@ const statNames = {
 };
 
 // Global functions for item database funct
ionality
-window.ItemDatabase = {
+(window as any).ItemDatabase = {
   // Helper function to format reforging
-  formatReforge: (reforgeId) => {
+  formatReforge: (reforgeId: any) => {
     if (!reforgeId || reforgeId === 0) retu
rn '';
-    const reforge = reforgeData[reforgeId.t
oString()];
+    const reforge = reforgeData[reforgeId.t
oString() as keyof typeof reforgeData];
     if (!reforge) return ` [R:${reforgeId}]
`;
     
-    const fromStat = statNames[reforge.s1] 
|| reforge.s1;
-    const toStat = statNames[reforge.s2] ||
 reforge.s2;
+    const fromStat = statNames[reforge.s1 a
s keyof typeof statNames] || reforge.s1;
+    const toStat = statNames[reforge.s2 as 
keyof typeof statNames] || reforge.s2;
     return ` [${fromStat} ? ${toStat}]`;
   },
 
   // Generate Wowhead URL for items
-  getWowheadUrl: (itemId) => {
+  getWowheadUrl: (itemId: any) => {
     return `https://www.wowhead.com/mop-cla
ssic/item=${itemId}`;
   },
 
   // Format equipment item with all details
 - uses enriched data from backend
-  formatEquipmentItem: (item) => {
+  formatEquipmentItem: (item: any) => {
     if (!item || !item.id) return null;
     
     // Use enriched name if available, othe
rwise fallback to ID
     const itemName = item.name || `Item ${i
tem.id}`;
-    const wowheadUrl = window.ItemDatabase.
getWowheadUrl(item.id);
+    const wowheadUrl = (window as any).Item
Database.getWowheadUrl(item.id);
     const iconUrl = item.icon ? `https://wo
w.zamimg.com/images/wow/icons/large/${item.i
con}.jpg` : null;
     
     // Build details array with specific la
yout structure
@@ -118,17 +118,17 @@ window.ItemDatabase = 
{
       details.push(`<div class="item-reforg
e">Reforged ${item.reforging.description}</d
iv>`);
     } else if (item.reforging && typeof ite
m.reforging === 'number') {
       // Fallback to manual reforge lookup 
if not enriched
-      const reforge = reforgeData[item.refo
rging.toString()];
+      const reforge = reforgeData[item.refo
rging.toString() as keyof typeof reforgeData
];
       if (reforge) {
-        const fromStat = statNames[reforge.
s1] || reforge.s1;
-        const toStat = statNames[reforge.s2
] || reforge.s2;
+        const fromStat = statNames[reforge.
s1 as keyof typeof statNames] || reforge.s1;
+        const toStat = statNames[reforge.s2
 as keyof typeof statNames] || reforge.s2;
         details.push(`<div class="item-refo
rge">Reforged ${fromStat} ? ${toStat}</div>`
);
       }
     }
     
     // Gems - each gem on separate line wit
h icon and white stats
     if (item.gems && item.gems.length > 0) 
{
-      item.gems.forEach(gem => {
+      item.gems.forEach((gem: any) => {
         if (gem && gem.name) {
           const gemIcon = gem.icon ? `<img 
src="https://wow.zamimg.com/images/wow/icons
/small/${gem.icon}.jpg" alt="${gem.name}" cl
ass="gem-icon-inline" />` : '';
           const gemStats = gem.stats && gem
.stats.length > 0 ? gem.stats.join(', ') : g
em.name;
@@ -163,12 +163,12 @@ window.ItemDatabase = 
{
   },
 
   // Format gem information
-  formatGems: (gems) => {
+  formatGems: (gems: any) => {
     if (!gems || gems.length === 0) return 
'';
-    const activeGems = gems.filter(g => g &
& g !== 0);
+    const activeGems = gems.filter((g: any)
 => g && g !== 0);
     if (activeGems.length === 0) return '';
     
-    return activeGems.map(gemId => `Gem ${g
emId}`).join(', ');
+    return activeGems.map((gemId: any) => `
Gem ${gemId}`).join(', ');
   }
 };
 </script>
\ No newline at end of file
 

diff --git a/web/src/layouts/ChartLayout.astro b/web/src/layouts/ChartLayout.astro
index c376218..c2c6292 100644
--- a/web/src/layouts/ChartLayout.astro
+++ b/web/src/layouts/ChartLayout.astro
@@ -269,6 +269,21 @@ const isFixedClassSpec = fixedClass && fixedSpec;
         flex: 1;
       }
       
+      .trinket-icon {
+        width: 20px;
+        height: 20px;
+        border-radius: 3px;
+        margin-right: 8px;
+        vertical-align: middle;
+        border: 1px solid rgba(255,255,255,0.2);
+      }
+      
+      .trinket-ilvl {
+        font-weight: 600;
+        color: var(--text-primary);
+        vertical-align: middle;
+      }
+      
       .chart-bar-container {
         display: flex;
         align-items: center;
@@ -771,6 +786,34 @@ const isFixedClassSpec = fixedClass && fixedSpec;
       
       <div id="metadata-container"></div>
       
+      <!-- Trinket comparison callout -->
+      <div id="trinket-callout" class="hidden">
+        <Callout type="note">
+          <p>
+            <em>Trinket rankings are still a work-in-progress.</em>
+          </p>
+          <p><strong>Current Implementation</strong>:</p>
+          <p>
+            We take the selected phase's bis profile gearset, remove the trinkets
, and simulate to get the <strong>baseline</strong> result. We then generate a gea
rset for each trinket and compare the results against the baseline. 
+          </p>
+          <p>
+            This implementation comes with some caveats that can scew the results
:
+          </p>
+          <ul>
+            <li>We <strong>don't</strong> re-reforge the gearset for every trinke
t</li>
+            <li>Ideally every trinket would come with it's own optimized gearset<
/li>
+            <li>This is simulating the trinkets <strong>individually</strong>, no
t in pairs</li>
+            <li>Like all other simulations, keep in mind WoWSims is still in <str
ong>alpha</strong> so not all spec/trinket implementations are not complete</li>
+          </ul>
+          <p>
+            With all that in mind, you should only consider this list as a <em>ro
ugh approximation</em> of the value of each individual trinket.
+          </p>
+          <p>
+            In the future we will reforge the gearset for each trinket to get a m
ore accurate value.
+          </p>
+        </Callout>
+      </div>
+      
       <div class="controls">
         <h3 class="controls-title">
           {isRankingsMode ? "Simulation Options" : (isFixedClassSpec ? "Simulatio
n Options" : "Comparison Options")}
@@ -781,7 +824,7 @@ const isFixedClassSpec = fixedClass && fixedSpec;
               <label for="comparisonType">Compare:</label>
               <select id="comparisonType">
                 <option value="race">Races</option>
-                <option value="trinket" disabled>Trinkets (Coming Soon)</option>
+                <option value="trinket">Trinkets</option>
               </select>
             </div>
           )}
@@ -852,6 +895,7 @@ const isFixedClassSpec = fixedClass && fixedSpec;
           <div class="control-group">
             <label for="sortBy">Sort By:</label>
             <select id="sortBy">
+              <option value="percent">Average Percentage Increase</option>
               <option value="dps">Average DPS</option>
               <option value="max">Max DPS</option>
               <option value="min">Min DPS</option>
@@ -955,9 +999,9 @@ const formatRace = (race) => {
   return race.replace('Race', '').replace(/([A-Z])/g, ' $1').trim();
 };
 
-const formatClass = (className) => {
-  return className.replace('Class', '').replace(/([A-Z])/g, ' $1').trim();
-};
+// const formatClass = (className) => {
+//   return className.replace('Class', '').replace(/([A-Z])/g, ' $1').trim();
+// };
 
 const formatTalents = (talents) => {
   if (!talents || !talents.talents || !Array.isArray(talents.talents)) {
@@ -1132,7 +1176,7 @@ const formatEquipmentSummary = (items) => {
 
 
 // Generate loadout dropdown HTML
-const generateLoadoutDropdown = (loadout, chartData) => {
+const generateLoadoutDropdown = (loadout, _chartData) => {
   const sections = formatLoadout(loadout);
   if (!sections || sections.length === 0) return '';
   
@@ -1349,6 +1393,12 @@ class UnifiedChart {
         comparisonSelect.value = comparisonType;
       }
     }
+    
+    // Initialize phase options for comparison modes
+    if (!this.isRankingsMode) {
+      this.updatePhaseOptions();
+      this.updateTrinketCallout();
+    }
   }
 
   bindEvents() {
@@ -1365,7 +1415,12 @@ class UnifiedChart {
 
       const dataSelects = document.querySelectorAll('#spec, #comparisonType');
       dataSelects.forEach(select => {
-        select.addEventListener('change', () => this.loadData());
+        select.addEventListener('change', () => {
+          if (select.id === 'comparisonType') {
+            this.updateSortOptions();
+          }
+          this.loadData();
+        });
       });
     }
     
@@ -1428,6 +1483,71 @@ class UnifiedChart {
     return comparisonSelect ? comparisonSelect.value : 'race';
   }
 
+  updateSortOptions() {
+    const sortBySelect = document.getElementById('sortBy');
+    if (!sortBySelect) return;
+    
+    const currentComparisonType = this.getCurrentComparisonType();
+    const percentOption = sortBySelect.querySelector('option[value="percent"]');
+    
+    if (percentOption) {
+      // Show percentage option only for trinket comparisons
+      percentOption.style.display = currentComparisonType === 'trinket' ? '' : 'n
one';
+      
+      if (currentComparisonType === 'trinket') {
+        // Default to percentage for trinket comparisons
+        if (sortBySelect.value === 'dps' || !sortBySelect.value) {
+          sortBySelect.value = 'percent';
+        }
+      } else {
+        // If percentage is selected but not available, switch to DPS
+        if (sortBySelect.value === 'percent') {
+          sortBySelect.value = 'dps';
+        }
+      }
+    }
+    
+    // Update phase options for trinket comparisons
+    this.updatePhaseOptions();
+    
+    // Update trinket callout visibility
+    this.updateTrinketCallout();
+  }
+
+  updateTrinketCallout() {
+    const trinketCallout = document.getElementById('trinket-callout');
+    if (!trinketCallout) return;
+    
+    const currentComparisonType = this.getCurrentComparisonType();
+    
+    if (currentComparisonType === 'trinket') {
+      trinketCallout.classList.remove('hidden');
+    } else {
+      trinketCallout.classList.add('hidden');
+    }
+  }
+
+  updatePhaseOptions() {
+    const phaseSelect = document.getElementById('phase');
+    if (!phaseSelect) return;
+    
+    // const currentComparisonType = this.getCurrentComparisonType();
+    const preRaidOption = phaseSelect.querySelector('option[value="preRaid"]');
+    
+    if (preRaidOption) {
+      if (!this.isRankingsMode) {
+        // Hide preRaid option for all comparison modes and force p1
+        preRaidOption.style.display = 'none';
+        if (phaseSelect.value === 'preRaid') {
+          phaseSelect.value = 'p1';
+        }
+      } else {
+        // Show preRaid option for rankings mode
+        preRaidOption.style.display = '';
+      }
+    }
+  }
+
   getFileName() {
     const targetCount = document.getElementById('targetCount').value;
     const encounterType = document.getElementById('encounterType').value;
@@ -1450,7 +1570,14 @@ class UnifiedChart {
     } else {
       const currentClass = this.getCurrentClass();
       const currentSpec = this.getCurrentSpec();
-      return `/data/comparison/${currentClass}/${currentSpec}/`;
+      const currentComparisonType = this.getCurrentComparisonType();
+      
+      // Handle different comparison type paths
+      if (currentComparisonType === 'trinket') {
+        return `/data/comparison/trinkets/${currentClass}/${currentSpec}/`;
+      } else {
+        return `/data/comparison/race/${currentClass}/${currentSpec}/`;
+      }
     }
   }
 
@@ -1471,6 +1598,9 @@ class UnifiedChart {
         return;
       }
     }
+    
+    // Update sort options based on comparison type
+    this.updateSortOptions();
 
     const loadingEl = document.getElementById('loading');
     const errorEl = document.getElementById('error');
@@ -1570,6 +1700,8 @@ class UnifiedChart {
     // Sort based on selected metric
     if (sortBy === 'stdev') {
       chartItems.sort((a, b) => a.value - b.value); // Lower stdev is better
+    } else if (sortBy === 'percent') {
+      chartItems.sort((a, b) => (b.percentIncrease || 0) - (a.percentIncrease || 
0)); // Higher percentage is better
     } else {
       chartItems.sort((a, b) => b.value - a.value); // Higher values are better
     }
@@ -1758,6 +1890,7 @@ class UnifiedChart {
             const displayValue = Math.round(item.value).toLocaleString();
             const avgDps = Math.round(item.dps).toLocaleString();
             const tooltip = sortBy === 'dps' ? displayValue : `${displayValue} (A
vg: ${avgDps})`;
+            const chartDisplayValue = displayValue; // Rankings always show raw v
alues
             
             const barColor = classColors[item.className] || '#666';
             const dropdownContent = item.loadout ? generateLoadoutDropdown(item.l
oadout, this.currentData) : '';
@@ -1774,7 +1907,7 @@ class UnifiedChart {
                   <div class="chart-bar-track">
                     <div class="chart-bar" style="width: ${barWidth}%; background
-color: ${barColor};"></div>
                   </div>
-                  <span class="chart-value" title="${tooltip}">${displayValue}</s
pan>
+                  <span class="chart-value" title="${tooltip}">${chartDisplayValu
e}</span>
                 </div>
               </div>
               ${dropdownContent}
@@ -1793,8 +1926,30 @@ class UnifiedChart {
     const chartItems = [];
     
     for (const [itemName, itemData] of Object.entries(this.currentData.results)) 
{
+      let label = itemName.replace(/_/g, ' ');
+      let iconData = null;
+      
+      // For trinket comparisons, extract icon and ilvl from equipment
+      if (currentComparisonType === 'trinket' && itemData.loadout && itemData.loa
dout.equipment) {
+        const trinket1 = itemData.loadout.equipment.items[12]; // Trinket slot 1
+        const trinket2 = itemData.loadout.equipment.items[13]; // Trinket slot 2
+        
+        // Find the trinket that's not empty (baseline has no trinkets)
+        const trinket = trinket1 && trinket1.id ? trinket1 : (trinket2 && trinket
2.id ? trinket2 : null);
+        
+        if (trinket) {
+          const ilvl = trinket.stats && trinket.stats.ilvl ? trinket.stats.ilvl :
 '';
+          iconData = {
+            icon: trinket.icon,
+            ilvl: ilvl,
+            name: trinket.name
+          };
+          label = ilvl ? `${ilvl}` : trinket.name; // Show just ilvl if available
+        }
+      }
+      
       chartItems.push({
-        label: itemName.replace(/_/g, ' '),
+        label: label,
         dps: itemData.dps,
         max: itemData.max,
         min: itemData.min,
@@ -1802,7 +1957,29 @@ class UnifiedChart {
         value: itemData[sortBy] || itemData.dps,
         category: itemName,
         rawName: itemName,
-        loadout: itemData.loadout || null
+        loadout: itemData.loadout || null,
+        iconData: iconData
+      });
+    }
+
+    // Find baseline for percentage calculations (only for trinket comparisons)
+    const baseline = this.currentData.results.baseline;
+    const baselineDps = baseline ? baseline.dps : null;
+    
+    // Add percentage and DPS increase calculations to chart items
+    if (baselineDps && currentComparisonType === 'trinket') {
+      chartItems.forEach(item => {
+        if (item.rawName === 'baseline') {
+          item.percentIncrease = 0;
+          item.dpsIncrease = 0;
+        } else {
+          // Calculate increases based on sort type (compare like-to-like)
+          const baselineValue = baseline[sortBy] || baseline.dps;
+          const itemValue = item[sortBy] || item.dps;
+          
+          item.percentIncrease = ((itemValue - baselineValue) / baselineValue * 1
00);
+          item.dpsIncrease = itemValue - baselineValue;
+        }
       });
     }
 
@@ -1815,7 +1992,8 @@ class UnifiedChart {
       dps: `${comparisonLabel} DPS Rankings (Average)`,
       max: `${comparisonLabel} DPS Rankings (Maximum)`,
       min: `${comparisonLabel} DPS Rankings (Minimum)`, 
-      stdev: `${comparisonLabel} DPS Consistency Rankings (Low StdDev = More Cons
istent)`
+      stdev: `${comparisonLabel} DPS Consistency Rankings (Low StdDev = More Cons
istent)`,
+      percent: `${comparisonLabel} Performance Rankings (% Increase)`
     };
 
     // TODO: abstract
@@ -1828,25 +2006,52 @@ class UnifiedChart {
             
             const displayValue = Math.round(item.value).toLocaleString();
             const avgDps = Math.round(item.dps).toLocaleString();
-            const tooltip = sortBy === 'dps' ? displayValue : `${displayValue} (A
vg: ${avgDps})`;
             
-            const barColor = currentComparisonType === 'race' ? 
-              (classColors[this.getCurrentClass()] || '#666') : '#666';
+            // Format tooltip and display value based on sort type and comparison
 type
+            let tooltip, chartDisplayValue;
+            if (currentComparisonType === 'trinket' && item.percentIncrease !== u
ndefined) {
+              const percentDisplay = item.percentIncrease === 0 ? 'Baseline' : `+
${item.percentIncrease.toFixed(1)}%`;
+              const dpsIncreaseDisplay = item.dpsIncrease === 0 ? 'Baseline' : `+
${Math.round(item.dpsIncrease).toLocaleString()}`;
+              
+              if (sortBy === 'percent') {
+                // When sorting by percentage, show percentage as main value
+                chartDisplayValue = percentDisplay;
+                tooltip = `${percentDisplay} (${avgDps} DPS)`;
+              } else if (sortBy === 'stdev') {
+                // For consistency sort, show actual standard deviation
+                chartDisplayValue = Math.round(item.stdev).toLocaleString();
+                tooltip = `${chartDisplayValue} StdDev (${avgDps} DPS avg, lower 
is more consistent)`;
+              } else {
+                // For DPS-based sorts, show DPS increase from baseline
+                chartDisplayValue = dpsIncreaseDisplay;
+                tooltip = sortBy === 'dps' ? `${displayValue} DPS (${dpsIncreaseD
isplay} vs baseline)` : `${displayValue} (Avg: ${avgDps} DPS, ${dpsIncreaseDisplay
} vs baseline)`;
+              }
+            } else {
+              chartDisplayValue = displayValue;
+              tooltip = sortBy === 'dps' ? displayValue : `${displayValue} (Avg: 
${avgDps})`;
+            }
+            
+            const barColor = classColors[this.getCurrentClass()] || '#666';
             const dropdownContent = item.loadout ? generateLoadoutDropdown(item.l
oadout, this.currentData) : '';
             
+            // Create the label content with icon if trinket
+            const labelContent = item.iconData ? 
+              `<img src="https://wow.zamimg.com/images/wow/icons/small/${item.ico
nData.icon}.jpg" alt="${item.iconData.name}" class="trinket-icon" title="${item.ic
onData.name}" /><span class="trinket-ilvl">${item.label}</span>` :
+              `<span class="chart-label">${item.label}</span>`;
+            
             return `
             <div class="chart-item-wrapper">
               <div class="chart-item" onclick="toggleChartItem(this.parentElement
)">
                 <div class="chart-labels">
                   <span class="chart-rank">#${index + 1}</span>
-                  <span class="chart-label">${item.label}</span>
+                  ${labelContent}
                   ${item.loadout ? '<span class="chart-expand-icon">?</span>' : '
'}
                 </div>
                 <div class="chart-bar-container">
                   <div class="chart-bar-track">
                     <div class="chart-bar" style="width: ${barWidth}%; background
-color: ${barColor};"></div>
                   </div>
-                  <span class="chart-value" title="${tooltip}">${displayValue}</s
pan>
+                  <span class="chart-value" title="${tooltip}">${chartDisplayValu
e}</span>
                 </div>
               </div>
               ${dropdownContent}

diff --git a/web/src/layouts/ChartLayout.ast
ro b/web/src/layouts/ChartLayout.astro
index c376218..c2c6292 100644
--- a/web/src/layouts/ChartLayout.astro
+++ b/web/src/layouts/ChartLayout.astro
@@ -269,6 +269,21 @@ const isFixedClassSpec 
= fixedClass && fixedSpec;
         flex: 1;
       }
       
+      .trinket-icon {
+        width: 20px;
+        height: 20px;
+        border-radius: 3px;
+        margin-right: 8px;
+        vertical-align: middle;
+        border: 1px solid rgba(255,255,255,
0.2);
+      }
+      
+      .trinket-ilvl {
+        font-weight: 600;
+        color: var(--text-primary);
+        vertical-align: middle;
+      }
+      
       .chart-bar-container {
         display: flex;
         align-items: center;
@@ -771,6 +786,34 @@ const isFixedClassSpec 
= fixedClass && fixedSpec;
       
       <div id="metadata-container"></div>
       
+      <!-- Trinket comparison callout -->
+      <div id="trinket-callout" class="hidd
en">
+        <Callout type="note">
+          <p>
+            <em>Trinket rankings are still 
a work-in-progress.</em>
+          </p>
+          <p><strong>Current Implementation
</strong>:</p>
+          <p>
+            We take the selected phase's bi
s profile gearset, remove the trinkets, and 
simulate to get the <strong>baseline</strong
> result. We then generate a gearset for eac
h trinket and compare the results against th
e baseline. 
+          </p>
+          <p>
+            This implementation comes with 
some caveats that can scew the results:
+          </p>
+          <ul>
+            <li>We <strong>don't</strong> r
e-reforge the gearset for every trinket</li>
+            <li>Ideally every trinket would
 come with it's own optimized gearset</li>
+            <li>This is simulating the trin
kets <strong>individually</strong>, not in p
airs</li>
+            <li>Like all other simulations,
 keep in mind WoWSims is still in <strong>al
pha</strong> so not all spec/trinket impleme
ntations are not complete</li>
+          </ul>
+          <p>
+            With all that in mind, you shou
ld only consider this list as a <em>rough ap
proximation</em> of the value of each indivi
dual trinket.
+          </p>
+          <p>
+            In the future we will reforge t
he gearset for each trinket to get a more ac
curate value.
+          </p>
+        </Callout>
+      </div>
+      
       <div class="controls">
         <h3 class="controls-title">
           {isRankingsMode ? "Simulation Opt
ions" : (isFixedClassSpec ? "Simulation Opti
ons" : "Comparison Options")}
@@ -781,7 +824,7 @@ const isFixedClassSpec =
 fixedClass && fixedSpec;
               <label for="comparisonType">C
ompare:</label>
               <select id="comparisonType">
                 <option value="race">Races<
/option>
-                <option value="trinket" dis
abled>Trinkets (Coming Soon)</option>
+                <option value="trinket">Tri
nkets</option>
               </select>
             </div>
           )}
@@ -852,6 +895,7 @@ const isFixedClassSpec =
 fixedClass && fixedSpec;
           <div class="control-group">
             <label for="sortBy">Sort By:</l
abel>
             <select id="sortBy">
+              <option value="percent">Avera
ge Percentage Increase</option>
               <option value="dps">Average D
PS</option>
               <option value="max">Max DPS</
option>
               <option value="min">Min DPS</
option>
@@ -955,9 +999,9 @@ const formatRace = (race
) => {
   return race.replace('Race', '').replace(/
([A-Z])/g, ' $1').trim();
 };
 
-const formatClass = (className) => {
-  return className.replace('Class', '').rep
lace(/([A-Z])/g, ' $1').trim();
-};
+// const formatClass = (className) => {
+//   return className.replace('Class', '').
replace(/([A-Z])/g, ' $1').trim();
+// };
 
 const formatTalents = (talents) => {
   if (!talents || !talents.talents || !Arra
y.isArray(talents.talents)) {
@@ -1132,7 +1176,7 @@ const formatEquipmentS
ummary = (items) => {
 
 
 // Generate loadout dropdown HTML
-const generateLoadoutDropdown = (loadout, c
hartData) => {
+const generateLoadoutDropdown = (loadout, _
chartData) => {
   const sections = formatLoadout(loadout);
   if (!sections || sections.length === 0) r
eturn '';
   
@@ -1349,6 +1393,12 @@ class UnifiedChart {
         comparisonSelect.value = comparison
Type;
       }
     }
+    
+    // Initialize phase options for compari
son modes
+    if (!this.isRankingsMode) {
+      this.updatePhaseOptions();
+      this.updateTrinketCallout();
+    }
   }
 
   bindEvents() {
@@ -1365,7 +1415,12 @@ class UnifiedChart {
 
       const dataSelects = document.querySel
ectorAll('#spec, #comparisonType');
       dataSelects.forEach(select => {
-        select.addEventListener('change', (
) => this.loadData());
+        select.addEventListener('change', (
) => {
+          if (select.id === 'comparisonType
') {
+            this.updateSortOptions();
+          }
+          this.loadData();
+        });
       });
     }
     
@@ -1428,6 +1483,71 @@ class UnifiedChart {
     return comparisonSelect ? comparisonSel
ect.value : 'race';
   }
 
+  updateSortOptions() {
+    const sortBySelect = document.getElemen
tById('sortBy');
+    if (!sortBySelect) return;
+    
+    const currentComparisonType = this.getC
urrentComparisonType();
+    const percentOption = sortBySelect.quer
ySelector('option[value="percent"]');
+    
+    if (percentOption) {
+      // Show percentage option only for tr
inket comparisons
+      percentOption.style.display = current
ComparisonType === 'trinket' ? '' : 'none';
+      
+      if (currentComparisonType === 'trinke
t') {
+        // Default to percentage for trinke
t comparisons
+        if (sortBySelect.value === 'dps' ||
 !sortBySelect.value) {
+          sortBySelect.value = 'percent';
+        }
+      } else {
+        // If percentage is selected but no
t available, switch to DPS
+        if (sortBySelect.value === 'percent
') {
+          sortBySelect.value = 'dps';
+        }
+      }
+    }
+    
+    // Update phase options for trinket com
parisons
+    this.updatePhaseOptions();
+    
+    // Update trinket callout visibility
+    this.updateTrinketCallout();
+  }
+
+  updateTrinketCallout() {
+    const trinketCallout = document.getElem
entById('trinket-callout');
+    if (!trinketCallout) return;
+    
+    const currentComparisonType = this.getC
urrentComparisonType();
+    
+    if (currentComparisonType === 'trinket'
) {
+      trinketCallout.classList.remove('hidd
en');
+    } else {
+      trinketCallout.classList.add('hidden'
);
+    }
+  }
+
+  updatePhaseOptions() {
+    const phaseSelect = document.getElement
ById('phase');
+    if (!phaseSelect) return;
+    
+    // const currentComparisonType = this.g
etCurrentComparisonType();
+    const preRaidOption = phaseSelect.query
Selector('option[value="preRaid"]');
+    
+    if (preRaidOption) {
+      if (!this.isRankingsMode) {
+        // Hide preRaid option for all comp
arison modes and force p1
+        preRaidOption.style.display = 'none
';
+        if (phaseSelect.value === 'preRaid'
) {
+          phaseSelect.value = 'p1';
+        }
+      } else {
+        // Show preRaid option for rankings
 mode
+        preRaidOption.style.display = '';
+      }
+    }
+  }
+
   getFileName() {
     const targetCount = document.getElement
ById('targetCount').value;
     const encounterType = document.getEleme
ntById('encounterType').value;
@@ -1450,7 +1570,14 @@ class UnifiedChart {
     } else {
       const currentClass = this.getCurrentC
lass();
       const currentSpec = this.getCurrentSp
ec();
-      return `/data/comparison/${currentCla
ss}/${currentSpec}/`;
+      const currentComparisonType = this.ge
tCurrentComparisonType();
+      
+      // Handle different comparison type p
aths
+      if (currentComparisonType === 'trinke
t') {
+        return `/data/comparison/trinkets/$
{currentClass}/${currentSpec}/`;
+      } else {
+        return `/data/comparison/race/${cur
rentClass}/${currentSpec}/`;
+      }
     }
   }
 
@@ -1471,6 +1598,9 @@ class UnifiedChart {
         return;
       }
     }
+    
+    // Update sort options based on compari
son type
+    this.updateSortOptions();
 
     const loadingEl = document.getElementBy
Id('loading');
     const errorEl = document.getElementById
('error');
@@ -1570,6 +1700,8 @@ class UnifiedChart {
     // Sort based on selected metric
     if (sortBy === 'stdev') {
       chartItems.sort((a, b) => a.value - b
.value); // Lower stdev is better
+    } else if (sortBy === 'percent') {
+      chartItems.sort((a, b) => (b.percentI
ncrease || 0) - (a.percentIncrease || 0)); /
/ Higher percentage is better
     } else {
       chartItems.sort((a, b) => b.value - a
.value); // Higher values are better
     }
@@ -1758,6 +1890,7 @@ class UnifiedChart {
             const displayValue = Math.round
(item.value).toLocaleString();
             const avgDps = Math.round(item.
dps).toLocaleString();
             const tooltip = sortBy === 'dps
' ? displayValue : `${displayValue} (Avg: ${
avgDps})`;
+            const chartDisplayValue = displ
ayValue; // Rankings always show raw values
             
             const barColor = classColors[it
em.className] || '#666';
             const dropdownContent = item.lo
adout ? generateLoadoutDropdown(item.loadout
, this.currentData) : '';
@@ -1774,7 +1907,7 @@ class UnifiedChart {
                   <div class="chart-bar-tra
ck">
                     <div class="chart-bar" 
style="width: ${barWidth}%; background-color
: ${barColor};"></div>
                   </div>
-                  <span class="chart-value"
 title="${tooltip}">${displayValue}</span>
+                  <span class="chart-value"
 title="${tooltip}">${chartDisplayValue}</sp
an>
                 </div>
               </div>
               ${dropdownContent}
@@ -1793,8 +1926,30 @@ class UnifiedChart {
     const chartItems = [];
     
     for (const [itemName, itemData] of Obje
ct.entries(this.currentData.results)) {
+      let label = itemName.replace(/_/g, ' 
');
+      let iconData = null;
+      
+      // For trinket comparisons, extract i
con and ilvl from equipment
+      if (currentComparisonType === 'trinke
t' && itemData.loadout && itemData.loadout.e
quipment) {
+        const trinket1 = itemData.loadout.e
quipment.items[12]; // Trinket slot 1
+        const trinket2 = itemData.loadout.e
quipment.items[13]; // Trinket slot 2
+        
+        // Find the trinket that's not empt
y (baseline has no trinkets)
+        const trinket = trinket1 && trinket
1.id ? trinket1 : (trinket2 && trinket2.id ?
 trinket2 : null);
+        
+        if (trinket) {
+          const ilvl = trinket.stats && tri
nket.stats.ilvl ? trinket.stats.ilvl : '';
+          iconData = {
+            icon: trinket.icon,
+            ilvl: ilvl,
+            name: trinket.name
+          };
+          label = ilvl ? `${ilvl}` : trinke
t.name; // Show just ilvl if available
+        }
+      }
+      
       chartItems.push({
-        label: itemName.replace(/_/g, ' '),
+        label: label,
         dps: itemData.dps,
         max: itemData.max,
         min: itemData.min,
@@ -1802,7 +1957,29 @@ class UnifiedChart {
         value: itemData[sortBy] || itemData
.dps,
         category: itemName,
         rawName: itemName,
-        loadout: itemData.loadout || null
+        loadout: itemData.loadout || null,
+        iconData: iconData
+      });
+    }
+
+    // Find baseline for percentage calcula
tions (only for trinket comparisons)
+    const baseline = this.currentData.resul
ts.baseline;
+    const baselineDps = baseline ? baseline
.dps : null;
+    
+    // Add percentage and DPS increase calc
ulations to chart items
+    if (baselineDps && currentComparisonTyp
e === 'trinket') {
+      chartItems.forEach(item => {
+        if (item.rawName === 'baseline') {
+          item.percentIncrease = 0;
+          item.dpsIncrease = 0;
+        } else {
+          // Calculate increases based on s
ort type (compare like-to-like)
+          const baselineValue = baseline[so
rtBy] || baseline.dps;
+          const itemValue = item[sortBy] ||
 item.dps;
+          
+          item.percentIncrease = ((itemValu
e - baselineValue) / baselineValue * 100);
+          item.dpsIncrease = itemValue - ba
selineValue;
+        }
       });
     }
 
@@ -1815,7 +1992,8 @@ class UnifiedChart {
       dps: `${comparisonLabel} DPS Rankings
 (Average)`,
       max: `${comparisonLabel} DPS Rankings
 (Maximum)`,
       min: `${comparisonLabel} DPS Rankings
 (Minimum)`, 
-      stdev: `${comparisonLabel} DPS Consis
tency Rankings (Low StdDev = More Consistent
)`
+      stdev: `${comparisonLabel} DPS Consis
tency Rankings (Low StdDev = More Consistent
)`,
+      percent: `${comparisonLabel} Performa
nce Rankings (% Increase)`
     };
 
     // TODO: abstract
@@ -1828,25 +2006,52 @@ class UnifiedChart {
             
             const displayValue = Math.round
(item.value).toLocaleString();
             const avgDps = Math.round(item.
dps).toLocaleString();
-            const tooltip = sortBy === 'dps
' ? displayValue : `${displayValue} (Avg: ${
avgDps})`;
             
-            const barColor = currentCompari
sonType === 'race' ? 
-              (classColors[this.getCurrentC
lass()] || '#666') : '#666';
+            // Format tooltip and display v
alue based on sort type and comparison type
+            let tooltip, chartDisplayValue;
+            if (currentComparisonType === '
trinket' && item.percentIncrease !== undefin
ed) {
+              const percentDisplay = item.p
ercentIncrease === 0 ? 'Baseline' : `+${item
.percentIncrease.toFixed(1)}%`;
+              const dpsIncreaseDisplay = it
em.dpsIncrease === 0 ? 'Baseline' : `+${Math
.round(item.dpsIncrease).toLocaleString()}`;
+              
+              if (sortBy === 'percent') {
+                // When sorting by percenta
ge, show percentage as main value
+                chartDisplayValue = percent
Display;
+                tooltip = `${percentDisplay
} (${avgDps} DPS)`;
+              } else if (sortBy === 'stdev'
) {
+                // For consistency sort, sh
ow actual standard deviation
+                chartDisplayValue = Math.ro
und(item.stdev).toLocaleString();
+                tooltip = `${chartDisplayVa
lue} StdDev (${avgDps} DPS avg, lower is mor
e consistent)`;
+              } else {
+                // For DPS-based sorts, sho
w DPS increase from baseline
+                chartDisplayValue = dpsIncr
easeDisplay;
+                tooltip = sortBy === 'dps' 
? `${displayValue} DPS (${dpsIncreaseDisplay
} vs baseline)` : `${displayValue} (Avg: ${a
vgDps} DPS, ${dpsIncreaseDisplay} vs baselin
e)`;
+              }
+            } else {
+              chartDisplayValue = displayVa
lue;
+              tooltip = sortBy === 'dps' ? 
displayValue : `${displayValue} (Avg: ${avgD
ps})`;
+            }
+            
+            const barColor = classColors[th
is.getCurrentClass()] || '#666';
             const dropdownContent = item.lo
adout ? generateLoadoutDropdown(item.loadout
, this.currentData) : '';
             
+            // Create the label content wit
h icon if trinket
+            const labelContent = item.iconD
ata ? 
+              `<img src="https://wow.zamimg
.com/images/wow/icons/small/${item.iconData.
icon}.jpg" alt="${item.iconData.name}" class
="trinket-icon" title="${item.iconData.name}
" /><span class="trinket-ilvl">${item.label}
</span>` :
+              `<span class="chart-label">${
item.label}</span>`;
+            
             return `
             <div class="chart-item-wrapper"
>
               <div class="chart-item" oncli
ck="toggleChartItem(this.parentElement)">
                 <div class="chart-labels">
                   <span class="chart-rank">
#${index + 1}</span>
-                  <span class="chart-label"
>${item.label}</span>
+                  ${labelContent}
                   ${item.loadout ? '<span c
lass="chart-expand-icon">?</span>' : ''}
                 </div>
                 <div class="chart-bar-conta
iner">
                   <div class="chart-bar-tra
ck">
                     <div class="chart-bar" 
style="width: ${barWidth}%; background-color
: ${barColor};"></div>
                   </div>
-                  <span class="chart-value"
 title="${tooltip}">${displayValue}</span>
+                  <span class="chart-value"
 title="${tooltip}">${chartDisplayValue}</sp
an>
                 </div>
               </div>
               ${dropdownContent}
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET