OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      495a035fe2bc
DATE      2025-07-20
SUBJECT   web: remove client load
FILES     4 CHANGED
HASH      495a035fe2bc
DATE      2025-07-20
SUBJECT   web: remove client load
FILES     4 CHANGED
 

diff --git a/web/public/js/dynamic-rankings.js b/web/public/js/dynamic-rankings.js
new file mode 100644
index 0000000..3fae0c8
--- /dev/null
+++ b/web/public/js/dynamic-rankings.js
@@ -0,0 +1,218 @@
+console.log('Dynamic rankings script loaded!');
+
+// WoW class colors for consistency
+const classColors = {
+  death_knight: '#C41E3A',
+  druid: '#FF7C0A', 
+  hunter: '#AAD372',
+  mage: '#3FC7EB',
+  monk: '#00FF98',
+  paladin: '#F48CBA',
+  priest: '#FFFFFF',
+  rogue: '#FFF468',
+  shaman: '#0070DD',
+  warlock: '#8788EE',
+  warrior: '#C69B6D'
+};
+
+class DynamicRankings {
+  constructor() {
+    this.currentData = null;
+    this.bindEvents();
+    this.loadInitialData();
+  }
+
+  bindEvents() {
+    const selects = document.querySelectorAll('#targetCount, #encounterType, #dur
ation, #phase');
+    selects.forEach(select => {
+      select.addEventListener('change', () => this.loadData());
+    });
+  }
+
+  async loadInitialData() {
+    await this.loadData();
+  }
+
+  getFileName() {
+    const targetCount = document.getElementById('targetCount').value;
+    const encounterType = document.getElementById('encounterType').value;
+    const duration = document.getElementById('duration').value;
+    const phase = document.getElementById('phase').value;
+    
+    return `dps_${phase}_${encounterType}_${targetCount}_${duration}.json`;
+  }
+
+  async loadData() {
+    const loadingEl = document.getElementById('loading');
+    const errorEl = document.getElementById('error');
+    const metadataContainer = document.getElementById('metadata-container');
+    const chartContainer = document.getElementById('chart-container');
+
+    if (!loadingEl || !errorEl || !metadataContainer || !chartContainer) {
+      console.error('Required elements not found');
+      return;
+    }
+
+    const scrollY = window.scrollY;
+
+    metadataContainer.style.opacity = '0.5';
+    chartContainer.style.opacity = '0.5';
+    loadingEl.classList.remove('hidden');
+    errorEl.classList.add('hidden');
+
+    try {
+      const fileName = this.getFileName();
+      console.log('Loading:', fileName);
+      const response = await fetch(`/data/${fileName}`);
+      
+      if (!response.ok) {
+        throw new Error(`Failed to load ${fileName}: ${response.statusText}`);
+      }
+
+      this.currentData = await response.json();
+      console.log('Data loaded successfully');
+      
+      loadingEl.classList.add('hidden');
+      
+      this.renderMetadata();
+      this.renderChart();
+      
+      metadataContainer.style.opacity = '1';
+      chartContainer.style.opacity = '1';
+      
+      requestAnimationFrame(() => {
+        window.scrollTo(0, scrollY);
+      });
+      
+    } catch (error) {
+      console.error('Error loading data:', error);
+      loadingEl.classList.add('hidden');
+      errorEl.textContent = `Error loading simulation data: ${error.message}`;
+      errorEl.classList.remove('hidden');
+      
+      metadataContainer.innerHTML = '';
+      chartContainer.innerHTML = '';
+      metadataContainer.style.opacity = '1';
+      chartContainer.style.opacity = '1';
+    }
+  }
+
+  renderMetadata() {
+    if (!this.currentData) return;
+
+    const container = document.getElementById('metadata-container');
+    const metadata = this.currentData.metadata;
+    
+    const simulationDate = new Date(metadata.timestamp).toLocaleString('en-US', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric',
+      hour: '2-digit',
+      minute: '2-digit',
+      timeZoneName: 'short'
+    });
+
+    const formatDuration = (seconds) => {
+      const minutes = Math.floor(seconds / 60);
+      const remainingSeconds = seconds % 60;
+      return minutes > 0 ? `${minutes}m ${remainingSeconds}s` : `${remainingSecon
ds}s`;
+    };
+
+    const formatRaidBuffs = (buffs) => {
+      const activeBuffs = Object.entries(buffs)
+        .filter(([_, value]) => value !== false && value !== 0)
+        .map(([key, value]) => {
+          const readable = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => st
r.toUpperCase());
+          return typeof value === 'number' && value > 1 ? `${readable} (${value})
` : readable;
+        });
+      return activeBuffs.join(', ');
+    };
+
+    container.innerHTML = `
+      <div class="simulation-metadata">
+        <h3 class="metadata-title">Simulation Details</h3>
+        <div class="metadata-grid">
+          <div class="metadata-item">
+            <span class="metadata-label">Iterations</span>
+            <span class="metadata-value">${metadata.iterations.toLocaleString()}<
/span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Specs Tested</span>
+            <span class="metadata-value">${metadata.specCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Encounter Duration</span>
+            <span class="metadata-value">${formatDuration(metadata.encounterDurat
ion)}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Duration Variation</span>
+            <span class="metadata-value">?${metadata.encounterVariation}s</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Target Count</span>
+            <span class="metadata-value">${metadata.targetCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Date Simulated</span>
+            <span class="metadata-value">${simulationDate}</span>
+          </div>
+          <div class="metadata-item metadata-item-wide">
+            <span class="metadata-label">Active Raid Buffs</span>
+            <span class="metadata-value metadata-value-wrap">${formatRaidBuffs(me
tadata.raidBuffs)}</span>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  renderChart() {
+    if (!this.currentData) return;
+
+    const container = document.getElementById('chart-container');
+    
+    const chartItems = [];
+    
+    for (const [className, classSpecs] of Object.entries(this.currentData.results
)) {
+      for (const [specName, specData] of Object.entries(classSpecs)) {
+        chartItems.push({
+          label: specName,
+          sublabel: className,
+          value: specData.dps,
+          category: className
+        });
+      }
+    }
+
+    chartItems.sort((a, b) => b.value - a.value);
+
+    const maxDps = Math.max(...chartItems.map(item => item.value));
+    
+    container.innerHTML = `
+      <div class="chart-container">
+        <h2 class="chart-title">DPS Rankings</h2>
+        <div class="chart-bars">
+          ${chartItems.map((item, index) => `
+            <div class="chart-item">
+              <div class="chart-labels">
+                <span class="chart-rank">#${index + 1}</span>
+                <span class="chart-label">${item.label}</span>
+                <span class="chart-sublabel">${item.sublabel}</span>
+              </div>
+              <div class="chart-bar-container">
+                <div class="chart-bar" style="width: ${(item.value / maxDps) * 10
0}%; background-color: ${classColors[item.category] || '#666'};">
+                </div>
+                <span class="chart-value">${Math.round(item.value).toLocaleString
()}</span>
+              </div>
+            </div>
+          `).join('')}
+        </div>
+      </div>
+    `;
+  }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+  console.log('DOM loaded, initializing DynamicRankings...');
+  new DynamicRankings();
+});
\ No newline at end of file

diff --git a/web/public/js/dynamic-rankings.
js b/web/public/js/dynamic-rankings.js
new file mode 100644
index 0000000..3fae0c8
--- /dev/null
+++ b/web/public/js/dynamic-rankings.js
@@ -0,0 +1,218 @@
+console.log('Dynamic rankings script loaded
!');
+
+// WoW class colors for consistency
+const classColors = {
+  death_knight: '#C41E3A',
+  druid: '#FF7C0A', 
+  hunter: '#AAD372',
+  mage: '#3FC7EB',
+  monk: '#00FF98',
+  paladin: '#F48CBA',
+  priest: '#FFFFFF',
+  rogue: '#FFF468',
+  shaman: '#0070DD',
+  warlock: '#8788EE',
+  warrior: '#C69B6D'
+};
+
+class DynamicRankings {
+  constructor() {
+    this.currentData = null;
+    this.bindEvents();
+    this.loadInitialData();
+  }
+
+  bindEvents() {
+    const selects = document.querySelectorA
ll('#targetCount, #encounterType, #duration,
 #phase');
+    selects.forEach(select => {
+      select.addEventListener('change', () 
=> this.loadData());
+    });
+  }
+
+  async loadInitialData() {
+    await this.loadData();
+  }
+
+  getFileName() {
+    const targetCount = document.getElement
ById('targetCount').value;
+    const encounterType = document.getEleme
ntById('encounterType').value;
+    const duration = document.getElementByI
d('duration').value;
+    const phase = document.getElementById('
phase').value;
+    
+    return `dps_${phase}_${encounterType}_$
{targetCount}_${duration}.json`;
+  }
+
+  async loadData() {
+    const loadingEl = document.getElementBy
Id('loading');
+    const errorEl = document.getElementById
('error');
+    const metadataContainer = document.getE
lementById('metadata-container');
+    const chartContainer = document.getElem
entById('chart-container');
+
+    if (!loadingEl || !errorEl || !metadata
Container || !chartContainer) {
+      console.error('Required elements not 
found');
+      return;
+    }
+
+    const scrollY = window.scrollY;
+
+    metadataContainer.style.opacity = '0.5'
;
+    chartContainer.style.opacity = '0.5';
+    loadingEl.classList.remove('hidden');
+    errorEl.classList.add('hidden');
+
+    try {
+      const fileName = this.getFileName();
+      console.log('Loading:', fileName);
+      const response = await fetch(`/data/$
{fileName}`);
+      
+      if (!response.ok) {
+        throw new Error(`Failed to load ${f
ileName}: ${response.statusText}`);
+      }
+
+      this.currentData = await response.jso
n();
+      console.log('Data loaded successfully
');
+      
+      loadingEl.classList.add('hidden');
+      
+      this.renderMetadata();
+      this.renderChart();
+      
+      metadataContainer.style.opacity = '1'
;
+      chartContainer.style.opacity = '1';
+      
+      requestAnimationFrame(() => {
+        window.scrollTo(0, scrollY);
+      });
+      
+    } catch (error) {
+      console.error('Error loading data:', 
error);
+      loadingEl.classList.add('hidden');
+      errorEl.textContent = `Error loading 
simulation data: ${error.message}`;
+      errorEl.classList.remove('hidden');
+      
+      metadataContainer.innerHTML = '';
+      chartContainer.innerHTML = '';
+      metadataContainer.style.opacity = '1'
;
+      chartContainer.style.opacity = '1';
+    }
+  }
+
+  renderMetadata() {
+    if (!this.currentData) return;
+
+    const container = document.getElementBy
Id('metadata-container');
+    const metadata = this.currentData.metad
ata;
+    
+    const simulationDate = new Date(metadat
a.timestamp).toLocaleString('en-US', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric',
+      hour: '2-digit',
+      minute: '2-digit',
+      timeZoneName: 'short'
+    });
+
+    const formatDuration = (seconds) => {
+      const minutes = Math.floor(seconds / 
60);
+      const remainingSeconds = seconds % 60
;
+      return minutes > 0 ? `${minutes}m ${r
emainingSeconds}s` : `${remainingSeconds}s`;
+    };
+
+    const formatRaidBuffs = (buffs) => {
+      const activeBuffs = Object.entries(bu
ffs)
+        .filter(([_, value]) => value !== f
alse && value !== 0)
+        .map(([key, value]) => {
+          const readable = key.replace(/([A
-Z])/g, ' $1').replace(/^./, str => str.toUp
perCase());
+          return typeof value === 'number' 
&& value > 1 ? `${readable} (${value})` : re
adable;
+        });
+      return activeBuffs.join(', ');
+    };
+
+    container.innerHTML = `
+      <div class="simulation-metadata">
+        <h3 class="metadata-title">Simulati
on Details</h3>
+        <div class="metadata-grid">
+          <div class="metadata-item">
+            <span class="metadata-label">It
erations</span>
+            <span class="metadata-value">${
metadata.iterations.toLocaleString()}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Sp
ecs Tested</span>
+            <span class="metadata-value">${
metadata.specCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">En
counter Duration</span>
+            <span class="metadata-value">${
formatDuration(metadata.encounterDuration)}<
/span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Du
ration Variation</span>
+            <span class="metadata-value">?$
{metadata.encounterVariation}s</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Ta
rget Count</span>
+            <span class="metadata-value">${
metadata.targetCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Da
te Simulated</span>
+            <span class="metadata-value">${
simulationDate}</span>
+          </div>
+          <div class="metadata-item metadat
a-item-wide">
+            <span class="metadata-label">Ac
tive Raid Buffs</span>
+            <span class="metadata-value met
adata-value-wrap">${formatRaidBuffs(metadata
.raidBuffs)}</span>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  renderChart() {
+    if (!this.currentData) return;
+
+    const container = document.getElementBy
Id('chart-container');
+    
+    const chartItems = [];
+    
+    for (const [className, classSpecs] of O
bject.entries(this.currentData.results)) {
+      for (const [specName, specData] of Ob
ject.entries(classSpecs)) {
+        chartItems.push({
+          label: specName,
+          sublabel: className,
+          value: specData.dps,
+          category: className
+        });
+      }
+    }
+
+    chartItems.sort((a, b) => b.value - a.v
alue);
+
+    const maxDps = Math.max(...chartItems.m
ap(item => item.value));
+    
+    container.innerHTML = `
+      <div class="chart-container">
+        <h2 class="chart-title">DPS Ranking
s</h2>
+        <div class="chart-bars">
+          ${chartItems.map((item, index) =>
 `
+            <div class="chart-item">
+              <div class="chart-labels">
+                <span class="chart-rank">#$
{index + 1}</span>
+                <span class="chart-label">$
{item.label}</span>
+                <span class="chart-sublabel
">${item.sublabel}</span>
+              </div>
+              <div class="chart-bar-contain
er">
+                <div class="chart-bar" styl
e="width: ${(item.value / maxDps) * 100}%; b
ackground-color: ${classColors[item.category
] || '#666'};">
+                </div>
+                <span class="chart-value">$
{Math.round(item.value).toLocaleString()}</s
pan>
+              </div>
+            </div>
+          `).join('')}
+        </div>
+      </div>
+    `;
+  }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded
', () => {
+  console.log('DOM loaded, initializing Dyn
amicRankings...');
+  new DynamicRankings();
+});
\ No newline at end of file
 

diff --git a/web/src/components/DynamicRankings.astro b/web/src/components/Dynamic
Rankings.astro
index 707ba63..dd97988 100644
--- a/web/src/components/DynamicRankings.astro
+++ b/web/src/components/DynamicRankings.astro
@@ -1,5 +1,5 @@
 ---
-// This component will be client-side rendered for interactivity
+export interface Props {}
 ---
 
 <div class="rankings-container">

diff --git a/web/src/components/DynamicRanki
ngs.astro b/web/src/components/DynamicRankin
gs.astro
index 707ba63..dd97988 100644
--- a/web/src/components/DynamicRankings.ast
ro
+++ b/web/src/components/DynamicRankings.ast
ro
@@ -1,5 +1,5 @@
 ---
-// This component will be client-side rende
red for interactivity
+export interface Props {}
 ---
 
 <div class="rankings-container">
 

diff --git a/web/src/layouts/DynamicRankingsLayout.astro b/web/src/layouts/Dynamic
RankingsLayout.astro
new file mode 100644
index 0000000..77f1da5
--- /dev/null
+++ b/web/src/layouts/DynamicRankingsLayout.astro
@@ -0,0 +1,541 @@
+---
+export interface Props {
+  title: string;
+  description?: string;
+}
+
+const { title, description } = Astro.props;
+---
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+    <meta name="viewport" content="width=device-width" />
+    <title>{title} - WoW MoP Rankings</title>
+    <style>
+      body {
+        font-family: Arial, sans-serif;
+        margin: 0;
+        padding: 0;
+        background-color: #282828;
+        color: #ffffff;
+      }
+      
+      .page-header {
+        text-align: center;
+        padding: 30px 20px;
+        background-color: #3a3a3a;
+        border-bottom: 1px solid #4a4a4a;
+      }
+      
+      .page-title {
+        color: #ea6962;
+        margin: 0 0 10px 0;
+        font-size: 2.5em;
+        font-weight: 700;
+      }
+      
+      .page-description {
+        color: #aaaaaa;
+        font-size: 1.1em;
+        margin: 0;
+      }
+
+      .rankings-container {
+        max-width: 1200px;
+        margin: 0 auto;
+        padding: 20px;
+      }
+      
+      .controls {
+        margin-bottom: 30px;
+        padding: 20px;
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .controls-title {
+        color: #ea6962;
+        margin: 0 0 15px 0;
+        font-size: 1.2em;
+        font-weight: 600;
+      }
+      
+      .controls-grid {
+        display: flex;
+        gap: 20px;
+        flex-wrap: wrap;
+      }
+      
+      .control-group {
+        display: flex;
+        flex-direction: column;
+        gap: 5px;
+      }
+      
+      .control-group label {
+        font-size: 0.9em;
+        color: #a9a9a9;
+        font-weight: 500;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+      }
+      
+      .control-group select {
+        padding: 8px 12px;
+        background-color: #2a2a2a;
+        border: 1px solid #555;
+        border-radius: 4px;
+        color: #ffffff;
+        font-size: 0.9em;
+        min-width: 120px;
+      }
+      
+      .control-group select:focus {
+        outline: none;
+        border-color: #ea6962;
+      }
+      
+      .loading, .error {
+        text-align: center;
+        padding: 20px;
+        margin: 20px 0;
+        position: fixed;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        background-color: #2a2a2a;
+        border-radius: 8px;
+        z-index: 1000;
+        min-width: 200px;
+      }
+      
+      .loading {
+        color: #a9a9a9;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .error {
+        color: #ea6962;
+        background-color: #3a2a2a;
+        border: 1px solid #ea6962;
+      }
+      
+      #metadata-container {
+        min-height: 120px;
+        transition: opacity 0.2s ease;
+      }
+      
+      #chart-container {
+        min-height: 400px;
+        transition: opacity 0.2s ease;
+      }
+      
+      .hidden {
+        display: none;
+      }
+    </style>
+    <style is:global>
+      .simulation-metadata {
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        padding: 20px;
+        margin-bottom: 30px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .metadata-title {
+        color: #ea6962;
+        margin: 0 0 15px 0;
+        font-size: 1.2em;
+        font-weight: 600;
+      }
+      
+      .metadata-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+        gap: 15px;
+      }
+      
+      .metadata-item {
+        display: flex;
+        flex-direction: column;
+        gap: 4px;
+      }
+      
+      .metadata-item-wide {
+        grid-column: 1 / -1;
+      }
+      
+      .metadata-label {
+        font-size: 0.85em;
+        color: #a9a9a9;
+        font-weight: 500;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+      }
+      
+      .metadata-value {
+        font-size: 1em;
+        color: #ffffff;
+        font-weight: 600;
+      }
+      
+      .metadata-value-wrap {
+        white-space: normal;
+        word-wrap: break-word;
+        line-height: 1.4;
+      }
+
+      .chart-container {
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        padding: 20px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .chart-title {
+        color: #ea6962;
+        margin: 0 0 20px 0;
+        font-size: 1.4em;
+        font-weight: 600;
+      }
+      
+      .chart-bars {
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+      }
+      
+      .chart-item {
+        display: flex;
+        align-items: center;
+        gap: 15px;
+        padding: 8px 0;
+      }
+      
+      .chart-labels {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        min-width: 200px;
+        flex-shrink: 0;
+      }
+      
+      .chart-rank {
+        font-size: 0.9em;
+        color: #a9a9a9;
+        font-weight: 600;
+        min-width: 30px;
+      }
+      
+      .chart-label {
+        font-weight: 600;
+        color: #ffffff;
+        text-transform: capitalize;
+      }
+      
+      .chart-sublabel {
+        font-size: 0.85em;
+        color: #a9a9a9;
+        text-transform: capitalize;
+      }
+      
+      .chart-bar-container {
+        display: flex;
+        align-items: center;
+        flex: 1;
+        gap: 10px;
+      }
+      
+      .chart-bar {
+        height: 20px;
+        border-radius: 3px;
+        min-width: 2px;
+        transition: width 0.3s ease;
+      }
+      
+      .chart-value {
+        font-weight: 600;
+        color: #ffffff;
+        font-size: 0.9em;
+        min-width: 60px;
+        text-align: right;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="page-header">
+      <h1 class="page-title">{title}</h1>
+      {description && <p class="page-description">{description}</p>}
+    </div>
+    
+    <div class="rankings-container">
+      <div id="metadata-container"></div>
+      
+      <div class="controls">
+        <h3 class="controls-title">Simulation Options</h3>
+        <div class="controls-grid">
+          <div class="control-group">
+            <label for="targetCount">Target Count:</label>
+            <select id="targetCount">
+              <option value="single">Single Target</option>
+              <option value="three">3 Targets</option>
+              <option value="cleave">Cleave (2)</option>
+              <option value="ten">10 Targets</option>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="encounterType">Encounter:</label>
+            <select id="encounterType">
+              <option value="raid">Raid</option>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="duration">Duration:</label>
+            <select id="duration">
+              <option value="long">Long</option>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="phase">Phase:</label>
+            <select id="phase">
+              <option value="p1">Phase 1</option>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div id="loading" class="loading hidden">Loading simulation data...</div>
+      <div id="error" class="error hidden"></div>
+      
+      <div id="chart-container"></div>
+    </div>
+
+    <script is:inline>
+console.log('Dynamic rankings script loaded!');
+
+// WoW class colors for consistency
+const classColors = {
+  death_knight: '#C41E3A',
+  druid: '#FF7C0A', 
+  hunter: '#AAD372',
+  mage: '#3FC7EB',
+  monk: '#00FF98',
+  paladin: '#F48CBA',
+  priest: '#FFFFFF',
+  rogue: '#FFF468',
+  shaman: '#0070DD',
+  warlock: '#8788EE',
+  warrior: '#C69B6D'
+};
+
+class DynamicRankings {
+  constructor() {
+    this.currentData = null;
+    this.bindEvents();
+    this.loadInitialData();
+  }
+
+  bindEvents() {
+    const selects = document.querySelectorAll('#targetCount, #encounterType, #dur
ation, #phase');
+    selects.forEach(select => {
+      select.addEventListener('change', () => this.loadData());
+    });
+  }
+
+  async loadInitialData() {
+    await this.loadData();
+  }
+
+  getFileName() {
+    const targetCount = document.getElementById('targetCount').value;
+    const encounterType = document.getElementById('encounterType').value;
+    const duration = document.getElementById('duration').value;
+    const phase = document.getElementById('phase').value;
+    
+    return `dps_${phase}_${encounterType}_${targetCount}_${duration}.json`;
+  }
+
+  async loadData() {
+    const loadingEl = document.getElementById('loading');
+    const errorEl = document.getElementById('error');
+    const metadataContainer = document.getElementById('metadata-container');
+    const chartContainer = document.getElementById('chart-container');
+
+    if (!loadingEl || !errorEl || !metadataContainer || !chartContainer) {
+      console.error('Required elements not found');
+      return;
+    }
+
+    const scrollY = window.scrollY;
+
+    metadataContainer.style.opacity = '0.5';
+    chartContainer.style.opacity = '0.5';
+    loadingEl.classList.remove('hidden');
+    errorEl.classList.add('hidden');
+
+    try {
+      const fileName = this.getFileName();
+      console.log('Loading:', fileName);
+      const response = await fetch(`/data/${fileName}`);
+      
+      if (!response.ok) {
+        throw new Error(`Failed to load ${fileName}: ${response.statusText}`);
+      }
+
+      this.currentData = await response.json();
+      console.log('Data loaded successfully');
+      
+      loadingEl.classList.add('hidden');
+      
+      this.renderMetadata();
+      this.renderChart();
+      
+      metadataContainer.style.opacity = '1';
+      chartContainer.style.opacity = '1';
+      
+      requestAnimationFrame(() => {
+        window.scrollTo(0, scrollY);
+      });
+      
+    } catch (error) {
+      console.error('Error loading data:', error);
+      loadingEl.classList.add('hidden');
+      errorEl.textContent = `Error loading simulation data: ${error.message}`;
+      errorEl.classList.remove('hidden');
+      
+      metadataContainer.innerHTML = '';
+      chartContainer.innerHTML = '';
+      metadataContainer.style.opacity = '1';
+      chartContainer.style.opacity = '1';
+    }
+  }
+
+  renderMetadata() {
+    if (!this.currentData) return;
+
+    const container = document.getElementById('metadata-container');
+    const metadata = this.currentData.metadata;
+    
+    const simulationDate = new Date(metadata.timestamp).toLocaleString('en-US', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric',
+      hour: '2-digit',
+      minute: '2-digit',
+      timeZoneName: 'short'
+    });
+
+    const formatDuration = (seconds) => {
+      const minutes = Math.floor(seconds / 60);
+      const remainingSeconds = seconds % 60;
+      return minutes > 0 ? `${minutes}m ${remainingSeconds}s` : `${remainingSecon
ds}s`;
+    };
+
+    const formatRaidBuffs = (buffs) => {
+      const activeBuffs = Object.entries(buffs)
+        .filter(([_, value]) => value !== false && value !== 0)
+        .map(([key, value]) => {
+          const readable = key.replace(/([A-Z])/g, ' $1').replace(/^./, str => st
r.toUpperCase());
+          return typeof value === 'number' && value > 1 ? `${readable} (${value})
` : readable;
+        });
+      return activeBuffs.join(', ');
+    };
+
+    container.innerHTML = `
+      <div class="simulation-metadata">
+        <h3 class="metadata-title">Simulation Details</h3>
+        <div class="metadata-grid">
+          <div class="metadata-item">
+            <span class="metadata-label">Iterations</span>
+            <span class="metadata-value">${metadata.iterations.toLocaleString()}<
/span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Specs Tested</span>
+            <span class="metadata-value">${metadata.specCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Encounter Duration</span>
+            <span class="metadata-value">${formatDuration(metadata.encounterDurat
ion)}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Duration Variation</span>
+            <span class="metadata-value">?${metadata.encounterVariation}s</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Target Count</span>
+            <span class="metadata-value">${metadata.targetCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Date Simulated</span>
+            <span class="metadata-value">${simulationDate}</span>
+          </div>
+          <div class="metadata-item metadata-item-wide">
+            <span class="metadata-label">Active Raid Buffs</span>
+            <span class="metadata-value metadata-value-wrap">${formatRaidBuffs(me
tadata.raidBuffs)}</span>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  renderChart() {
+    if (!this.currentData) return;
+
+    const container = document.getElementById('chart-container');
+    
+    const chartItems = [];
+    
+    for (const [className, classSpecs] of Object.entries(this.currentData.results
)) {
+      for (const [specName, specData] of Object.entries(classSpecs)) {
+        chartItems.push({
+          label: specName,
+          sublabel: className,
+          value: specData.dps,
+          category: className
+        });
+      }
+    }
+
+    chartItems.sort((a, b) => b.value - a.value);
+
+    const maxDps = Math.max(...chartItems.map(item => item.value));
+    
+    container.innerHTML = `
+      <div class="chart-container">
+        <h2 class="chart-title">DPS Rankings</h2>
+        <div class="chart-bars">
+          ${chartItems.map((item, index) => `
+            <div class="chart-item">
+              <div class="chart-labels">
+                <span class="chart-rank">#${index + 1}</span>
+                <span class="chart-label">${item.label}</span>
+                <span class="chart-sublabel">${item.sublabel}</span>
+              </div>
+              <div class="chart-bar-container">
+                <div class="chart-bar" style="width: ${(item.value / maxDps) * 10
0}%; background-color: ${classColors[item.category] || '#666'};">
+                </div>
+                <span class="chart-value">${Math.round(item.value).toLocaleString
()}</span>
+              </div>
+            </div>
+          `).join('')}
+        </div>
+      </div>
+    `;
+  }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded', () => {
+  console.log('DOM loaded, initializing DynamicRankings...');
+  new DynamicRankings();
+});
+    </script>
+  </body>
+</html>
\ No newline at end of file

diff --git a/web/src/layouts/DynamicRankings
Layout.astro b/web/src/layouts/DynamicRankin
gsLayout.astro
new file mode 100644
index 0000000..77f1da5
--- /dev/null
+++ b/web/src/layouts/DynamicRankingsLayout.
astro
@@ -0,0 +1,541 @@
+---
+export interface Props {
+  title: string;
+  description?: string;
+}
+
+const { title, description } = Astro.props;
+---
+
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <link rel="icon" type="image/svg+xml" h
ref="/favicon.svg" />
+    <meta name="viewport" content="width=de
vice-width" />
+    <title>{title} - WoW MoP Rankings</titl
e>
+    <style>
+      body {
+        font-family: Arial, sans-serif;
+        margin: 0;
+        padding: 0;
+        background-color: #282828;
+        color: #ffffff;
+      }
+      
+      .page-header {
+        text-align: center;
+        padding: 30px 20px;
+        background-color: #3a3a3a;
+        border-bottom: 1px solid #4a4a4a;
+      }
+      
+      .page-title {
+        color: #ea6962;
+        margin: 0 0 10px 0;
+        font-size: 2.5em;
+        font-weight: 700;
+      }
+      
+      .page-description {
+        color: #aaaaaa;
+        font-size: 1.1em;
+        margin: 0;
+      }
+
+      .rankings-container {
+        max-width: 1200px;
+        margin: 0 auto;
+        padding: 20px;
+      }
+      
+      .controls {
+        margin-bottom: 30px;
+        padding: 20px;
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .controls-title {
+        color: #ea6962;
+        margin: 0 0 15px 0;
+        font-size: 1.2em;
+        font-weight: 600;
+      }
+      
+      .controls-grid {
+        display: flex;
+        gap: 20px;
+        flex-wrap: wrap;
+      }
+      
+      .control-group {
+        display: flex;
+        flex-direction: column;
+        gap: 5px;
+      }
+      
+      .control-group label {
+        font-size: 0.9em;
+        color: #a9a9a9;
+        font-weight: 500;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+      }
+      
+      .control-group select {
+        padding: 8px 12px;
+        background-color: #2a2a2a;
+        border: 1px solid #555;
+        border-radius: 4px;
+        color: #ffffff;
+        font-size: 0.9em;
+        min-width: 120px;
+      }
+      
+      .control-group select:focus {
+        outline: none;
+        border-color: #ea6962;
+      }
+      
+      .loading, .error {
+        text-align: center;
+        padding: 20px;
+        margin: 20px 0;
+        position: fixed;
+        top: 50%;
+        left: 50%;
+        transform: translate(-50%, -50%);
+        background-color: #2a2a2a;
+        border-radius: 8px;
+        z-index: 1000;
+        min-width: 200px;
+      }
+      
+      .loading {
+        color: #a9a9a9;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .error {
+        color: #ea6962;
+        background-color: #3a2a2a;
+        border: 1px solid #ea6962;
+      }
+      
+      #metadata-container {
+        min-height: 120px;
+        transition: opacity 0.2s ease;
+      }
+      
+      #chart-container {
+        min-height: 400px;
+        transition: opacity 0.2s ease;
+      }
+      
+      .hidden {
+        display: none;
+      }
+    </style>
+    <style is:global>
+      .simulation-metadata {
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        padding: 20px;
+        margin-bottom: 30px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .metadata-title {
+        color: #ea6962;
+        margin: 0 0 15px 0;
+        font-size: 1.2em;
+        font-weight: 600;
+      }
+      
+      .metadata-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-
fit, minmax(200px, 1fr));
+        gap: 15px;
+      }
+      
+      .metadata-item {
+        display: flex;
+        flex-direction: column;
+        gap: 4px;
+      }
+      
+      .metadata-item-wide {
+        grid-column: 1 / -1;
+      }
+      
+      .metadata-label {
+        font-size: 0.85em;
+        color: #a9a9a9;
+        font-weight: 500;
+        text-transform: uppercase;
+        letter-spacing: 0.5px;
+      }
+      
+      .metadata-value {
+        font-size: 1em;
+        color: #ffffff;
+        font-weight: 600;
+      }
+      
+      .metadata-value-wrap {
+        white-space: normal;
+        word-wrap: break-word;
+        line-height: 1.4;
+      }
+
+      .chart-container {
+        background-color: #3a3a3a;
+        border-radius: 8px;
+        padding: 20px;
+        border: 1px solid #4a4a4a;
+      }
+      
+      .chart-title {
+        color: #ea6962;
+        margin: 0 0 20px 0;
+        font-size: 1.4em;
+        font-weight: 600;
+      }
+      
+      .chart-bars {
+        display: flex;
+        flex-direction: column;
+        gap: 8px;
+      }
+      
+      .chart-item {
+        display: flex;
+        align-items: center;
+        gap: 15px;
+        padding: 8px 0;
+      }
+      
+      .chart-labels {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+        min-width: 200px;
+        flex-shrink: 0;
+      }
+      
+      .chart-rank {
+        font-size: 0.9em;
+        color: #a9a9a9;
+        font-weight: 600;
+        min-width: 30px;
+      }
+      
+      .chart-label {
+        font-weight: 600;
+        color: #ffffff;
+        text-transform: capitalize;
+      }
+      
+      .chart-sublabel {
+        font-size: 0.85em;
+        color: #a9a9a9;
+        text-transform: capitalize;
+      }
+      
+      .chart-bar-container {
+        display: flex;
+        align-items: center;
+        flex: 1;
+        gap: 10px;
+      }
+      
+      .chart-bar {
+        height: 20px;
+        border-radius: 3px;
+        min-width: 2px;
+        transition: width 0.3s ease;
+      }
+      
+      .chart-value {
+        font-weight: 600;
+        color: #ffffff;
+        font-size: 0.9em;
+        min-width: 60px;
+        text-align: right;
+      }
+    </style>
+  </head>
+  <body>
+    <div class="page-header">
+      <h1 class="page-title">{title}</h1>
+      {description && <p class="page-descri
ption">{description}</p>}
+    </div>
+    
+    <div class="rankings-container">
+      <div id="metadata-container"></div>
+      
+      <div class="controls">
+        <h3 class="controls-title">Simulati
on Options</h3>
+        <div class="controls-grid">
+          <div class="control-group">
+            <label for="targetCount">Target
 Count:</label>
+            <select id="targetCount">
+              <option value="single">Single
 Target</option>
+              <option value="three">3 Targe
ts</option>
+              <option value="cleave">Cleave
 (2)</option>
+              <option value="ten">10 Target
s</option>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="encounterType">Enco
unter:</label>
+            <select id="encounterType">
+              <option value="raid">Raid</op
tion>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="duration">Duration:
</label>
+            <select id="duration">
+              <option value="long">Long</op
tion>
+            </select>
+          </div>
+          
+          <div class="control-group">
+            <label for="phase">Phase:</labe
l>
+            <select id="phase">
+              <option value="p1">Phase 1</o
ption>
+            </select>
+          </div>
+        </div>
+      </div>
+
+      <div id="loading" class="loading hidd
en">Loading simulation data...</div>
+      <div id="error" class="error hidden">
</div>
+      
+      <div id="chart-container"></div>
+    </div>
+
+    <script is:inline>
+console.log('Dynamic rankings script loaded
!');
+
+// WoW class colors for consistency
+const classColors = {
+  death_knight: '#C41E3A',
+  druid: '#FF7C0A', 
+  hunter: '#AAD372',
+  mage: '#3FC7EB',
+  monk: '#00FF98',
+  paladin: '#F48CBA',
+  priest: '#FFFFFF',
+  rogue: '#FFF468',
+  shaman: '#0070DD',
+  warlock: '#8788EE',
+  warrior: '#C69B6D'
+};
+
+class DynamicRankings {
+  constructor() {
+    this.currentData = null;
+    this.bindEvents();
+    this.loadInitialData();
+  }
+
+  bindEvents() {
+    const selects = document.querySelectorA
ll('#targetCount, #encounterType, #duration,
 #phase');
+    selects.forEach(select => {
+      select.addEventListener('change', () 
=> this.loadData());
+    });
+  }
+
+  async loadInitialData() {
+    await this.loadData();
+  }
+
+  getFileName() {
+    const targetCount = document.getElement
ById('targetCount').value;
+    const encounterType = document.getEleme
ntById('encounterType').value;
+    const duration = document.getElementByI
d('duration').value;
+    const phase = document.getElementById('
phase').value;
+    
+    return `dps_${phase}_${encounterType}_$
{targetCount}_${duration}.json`;
+  }
+
+  async loadData() {
+    const loadingEl = document.getElementBy
Id('loading');
+    const errorEl = document.getElementById
('error');
+    const metadataContainer = document.getE
lementById('metadata-container');
+    const chartContainer = document.getElem
entById('chart-container');
+
+    if (!loadingEl || !errorEl || !metadata
Container || !chartContainer) {
+      console.error('Required elements not 
found');
+      return;
+    }
+
+    const scrollY = window.scrollY;
+
+    metadataContainer.style.opacity = '0.5'
;
+    chartContainer.style.opacity = '0.5';
+    loadingEl.classList.remove('hidden');
+    errorEl.classList.add('hidden');
+
+    try {
+      const fileName = this.getFileName();
+      console.log('Loading:', fileName);
+      const response = await fetch(`/data/$
{fileName}`);
+      
+      if (!response.ok) {
+        throw new Error(`Failed to load ${f
ileName}: ${response.statusText}`);
+      }
+
+      this.currentData = await response.jso
n();
+      console.log('Data loaded successfully
');
+      
+      loadingEl.classList.add('hidden');
+      
+      this.renderMetadata();
+      this.renderChart();
+      
+      metadataContainer.style.opacity = '1'
;
+      chartContainer.style.opacity = '1';
+      
+      requestAnimationFrame(() => {
+        window.scrollTo(0, scrollY);
+      });
+      
+    } catch (error) {
+      console.error('Error loading data:', 
error);
+      loadingEl.classList.add('hidden');
+      errorEl.textContent = `Error loading 
simulation data: ${error.message}`;
+      errorEl.classList.remove('hidden');
+      
+      metadataContainer.innerHTML = '';
+      chartContainer.innerHTML = '';
+      metadataContainer.style.opacity = '1'
;
+      chartContainer.style.opacity = '1';
+    }
+  }
+
+  renderMetadata() {
+    if (!this.currentData) return;
+
+    const container = document.getElementBy
Id('metadata-container');
+    const metadata = this.currentData.metad
ata;
+    
+    const simulationDate = new Date(metadat
a.timestamp).toLocaleString('en-US', {
+      year: 'numeric',
+      month: 'long',
+      day: 'numeric',
+      hour: '2-digit',
+      minute: '2-digit',
+      timeZoneName: 'short'
+    });
+
+    const formatDuration = (seconds) => {
+      const minutes = Math.floor(seconds / 
60);
+      const remainingSeconds = seconds % 60
;
+      return minutes > 0 ? `${minutes}m ${r
emainingSeconds}s` : `${remainingSeconds}s`;
+    };
+
+    const formatRaidBuffs = (buffs) => {
+      const activeBuffs = Object.entries(bu
ffs)
+        .filter(([_, value]) => value !== f
alse && value !== 0)
+        .map(([key, value]) => {
+          const readable = key.replace(/([A
-Z])/g, ' $1').replace(/^./, str => str.toUp
perCase());
+          return typeof value === 'number' 
&& value > 1 ? `${readable} (${value})` : re
adable;
+        });
+      return activeBuffs.join(', ');
+    };
+
+    container.innerHTML = `
+      <div class="simulation-metadata">
+        <h3 class="metadata-title">Simulati
on Details</h3>
+        <div class="metadata-grid">
+          <div class="metadata-item">
+            <span class="metadata-label">It
erations</span>
+            <span class="metadata-value">${
metadata.iterations.toLocaleString()}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Sp
ecs Tested</span>
+            <span class="metadata-value">${
metadata.specCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">En
counter Duration</span>
+            <span class="metadata-value">${
formatDuration(metadata.encounterDuration)}<
/span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Du
ration Variation</span>
+            <span class="metadata-value">?$
{metadata.encounterVariation}s</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Ta
rget Count</span>
+            <span class="metadata-value">${
metadata.targetCount}</span>
+          </div>
+          <div class="metadata-item">
+            <span class="metadata-label">Da
te Simulated</span>
+            <span class="metadata-value">${
simulationDate}</span>
+          </div>
+          <div class="metadata-item metadat
a-item-wide">
+            <span class="metadata-label">Ac
tive Raid Buffs</span>
+            <span class="metadata-value met
adata-value-wrap">${formatRaidBuffs(metadata
.raidBuffs)}</span>
+          </div>
+        </div>
+      </div>
+    `;
+  }
+
+  renderChart() {
+    if (!this.currentData) return;
+
+    const container = document.getElementBy
Id('chart-container');
+    
+    const chartItems = [];
+    
+    for (const [className, classSpecs] of O
bject.entries(this.currentData.results)) {
+      for (const [specName, specData] of Ob
ject.entries(classSpecs)) {
+        chartItems.push({
+          label: specName,
+          sublabel: className,
+          value: specData.dps,
+          category: className
+        });
+      }
+    }
+
+    chartItems.sort((a, b) => b.value - a.v
alue);
+
+    const maxDps = Math.max(...chartItems.m
ap(item => item.value));
+    
+    container.innerHTML = `
+      <div class="chart-container">
+        <h2 class="chart-title">DPS Ranking
s</h2>
+        <div class="chart-bars">
+          ${chartItems.map((item, index) =>
 `
+            <div class="chart-item">
+              <div class="chart-labels">
+                <span class="chart-rank">#$
{index + 1}</span>
+                <span class="chart-label">$
{item.label}</span>
+                <span class="chart-sublabel
">${item.sublabel}</span>
+              </div>
+              <div class="chart-bar-contain
er">
+                <div class="chart-bar" styl
e="width: ${(item.value / maxDps) * 100}%; b
ackground-color: ${classColors[item.category
] || '#666'};">
+                </div>
+                <span class="chart-value">$
{Math.round(item.value).toLocaleString()}</s
pan>
+              </div>
+            </div>
+          `).join('')}
+        </div>
+      </div>
+    `;
+  }
+}
+
+// Initialize when DOM is loaded
+document.addEventListener('DOMContentLoaded
', () => {
+  console.log('DOM loaded, initializing Dyn
amicRankings...');
+  new DynamicRankings();
+});
+    </script>
+  </body>
+</html>
\ No newline at end of file
 

diff --git a/web/src/pages/index.astro b/web/src/pages/index.astro
index 3a5123a..8ec0fba 100644
--- a/web/src/pages/index.astro
+++ b/web/src/pages/index.astro
@@ -1,49 +1,8 @@
 ---
-import DynamicRankings from '../components/DynamicRankings.astro';
+import DynamicRankingsLayout from '../layouts/DynamicRankingsLayout.astro';
 ---
 
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
-    <meta name="viewport" content="width=device-width" />
-    <title>WoW MoP DPS Rankings</title>
-    <style>
-      body {
-        font-family: Arial, sans-serif;
-        margin: 0;
-        padding: 0;
-        background-color: #282828;
-        color: #ffffff;
-      }
-      
-      .page-header {
-        text-align: center;
-        padding: 30px 20px;
-        background-color: #3a3a3a;
-        border-bottom: 1px solid #4a4a4a;
-      }
-      
-      .page-title {
-        color: #ea6962;
-        margin: 0 0 10px 0;
-        font-size: 2.5em;
-        font-weight: 700;
-      }
-      
-      .page-description {
-        color: #aaaaaa;
-        font-size: 1.1em;
-        margin: 0;
-      }
-    </style>
-  </head>
-  <body>
-    <div class="page-header">
-      <h1 class="page-title">WoW MoP DPS Rankings</h1>
-      <p class="page-description">Interactive simulation results for all encounte
rs and target counts</p>
-    </div>
-    
-    <DynamicRankings client:load />
-  </body>
-</html>
+<DynamicRankingsLayout 
+  title="WoW MoP DPS Rankings"
+  description="Interactive simulation results for all encounters and target count
s"
+/>

diff --git a/web/src/pages/index.astro b/web
/src/pages/index.astro
index 3a5123a..8ec0fba 100644
--- a/web/src/pages/index.astro
+++ b/web/src/pages/index.astro
@@ -1,49 +1,8 @@
 ---
-import DynamicRankings from '../components/
DynamicRankings.astro';
+import DynamicRankingsLayout from '../layou
ts/DynamicRankingsLayout.astro';
 ---
 
-<html lang="en">
-  <head>
-    <meta charset="utf-8" />
-    <link rel="icon" type="image/svg+xml" h
ref="/favicon.svg" />
-    <meta name="viewport" content="width=de
vice-width" />
-    <title>WoW MoP DPS Rankings</title>
-    <style>
-      body {
-        font-family: Arial, sans-serif;
-        margin: 0;
-        padding: 0;
-        background-color: #282828;
-        color: #ffffff;
-      }
-      
-      .page-header {
-        text-align: center;
-        padding: 30px 20px;
-        background-color: #3a3a3a;
-        border-bottom: 1px solid #4a4a4a;
-      }
-      
-      .page-title {
-        color: #ea6962;
-        margin: 0 0 10px 0;
-        font-size: 2.5em;
-        font-weight: 700;
-      }
-      
-      .page-description {
-        color: #aaaaaa;
-        font-size: 1.1em;
-        margin: 0;
-      }
-    </style>
-  </head>
-  <body>
-    <div class="page-header">
-      <h1 class="page-title">WoW MoP DPS Ra
nkings</h1>
-      <p class="page-description">Interacti
ve simulation results for all encounters and
 target counts</p>
-    </div>
-    
-    <DynamicRankings client:load />
-  </body>
-</html>
+<DynamicRankingsLayout 
+  title="WoW MoP DPS Rankings"
+  description="Interactive simulation resul
ts for all encounters and target counts"
+/>
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET