Blast radius UI: Support for navigating from keep file to rule details

Bug: b/483560331
Change-Id: I8ce9cc0308752c648cdc004553fd4c19484cba73
diff --git a/src/blastradius/web/main.js b/src/blastradius/web/main.js
index a8d2356..ad7b9f6 100644
--- a/src/blastradius/web/main.js
+++ b/src/blastradius/web/main.js
@@ -360,6 +360,7 @@
 
     const div = document.createElement('div');
     div.className = 'rule-item';
+    div.dataset.ruleId = rule.id;
     const classes = rule.blastRadius.classBlastRadius?.length || 0;
     const methods = rule.blastRadius.methodBlastRadius?.length || 0;
     const fields = rule.blastRadius.fieldBlastRadius?.length || 0;
@@ -433,7 +434,7 @@
                     <div class="banner banner-warning">
                         <strong>Redundant Rule:</strong> This rule is fully subsumed by ${isSameFile ? 'another rule in the same file' : 'a rule in a different file'}:
                         <div style="margin-top: 0.5rem;">
-                            <div class="subsumed-by-item">
+                            <div class="subsumed-by-item" onclick="navigateToRule('${id}')">
                                 <div style="font-weight: 500;">${r ? escapeHtml(r.source) : `Rule ID: ${id}`}</div>
                                 <div style="font-size: 0.8rem; color: #666; margin-top: 2px;">Origin: ${escapeHtml(originStr)}</div>
                             </div>
@@ -462,17 +463,24 @@
 }
 
 function renderFileDetail(file) {
+  const usedRules = file.rules.filter(r => r.totalRadius > 0).sort((a, b) => b.totalRadius - a.totalRadius);
+  const unusedRules = file.rules.filter(r => r.totalRadius === 0).sort((a, b) => a.source.localeCompare(b.source));
+
   let html = `
         <div class="card">
             <h2>File Details</h2>
             <p><strong>File:</strong> ${escapeHtml(file.name)}</p>
             <p><strong>Blast radius:</strong> ${file.totalRadius} items kept across ${file.rules.length} rules.</p>
         </div>
+    `;
+
+  if (usedRules.length > 0) {
+    html += `
         <div class="card">
             <h2>Keep Rules in this File</h2>
             <div class="item-list">
-                ${file.rules.sort((a,b) => b.totalRadius - a.totalRadius).map(r => `
-                    <div style="padding: 1rem; border-bottom: 1px solid #eee;">
+                ${usedRules.map(r => `
+                    <div class="rule-item" onclick="navigateToRule('${r.id}')">
                         <div class="rule-name">${escapeHtml(r.source)}</div>
                         <div class="rule-stats">Blast radius: ${r.totalRadius}</div>
                     </div>
@@ -480,6 +488,23 @@
             </div>
         </div>
     `;
+  }
+
+  if (unusedRules.length > 0) {
+    html += `
+        <div class="card">
+            <h2>Unused Keep Rules in this File</h2>
+            <div class="item-list">
+                ${unusedRules.map(r => `
+                    <div class="rule-item" onclick="navigateToRule('${r.id}')">
+                        <div class="rule-name">${escapeHtml(r.source)}</div>
+                    </div>
+                `).join('')}
+            </div>
+        </div>
+    `;
+  }
+
   mainContent.innerHTML = html;
 }
 
@@ -557,7 +582,19 @@
   renderRuleList();
 }
 
-function switchView(view) {
+function navigateToRule(ruleId) {
+  const rule = tables.rules.get(Number(ruleId));
+  if (!rule) return;
+
+  switchView('rules', true);
+  const element = ruleList.querySelector(`[data-rule-id="${rule.id}"]`);
+  if (element) {
+    selectRule(rule, element);
+    element.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
+  }
+}
+
+function switchView(view, preserveDetail = false) {
   currentView = view;
   document.querySelectorAll('.tab-button').forEach(btn => {
     btn.classList.toggle('active', btn.getAttribute('onclick').includes(`'${view}'`));
@@ -569,7 +606,9 @@
   if (containerData) {
     if (view === 'rules') {
       filterRules("");
-      renderStatsOverview();
+      if (!preserveDetail) {
+        renderStatsOverview();
+      }
     } else if (view === 'unused') {
       filterUnusedRules("");
       mainContent.innerHTML = '';
diff --git a/src/blastradius/web/style.css b/src/blastradius/web/style.css
index 940b0db..887b27f 100644
--- a/src/blastradius/web/style.css
+++ b/src/blastradius/web/style.css
@@ -209,6 +209,12 @@
     border-radius: 2px;
     font-family: monospace;
     word-break: break-all;
+    cursor: pointer;
+    transition: background-color 0.2s;
+}
+
+.subsumed-by-item:hover {
+    background-color: rgba(255, 255, 255, 0.8);
 }
 
 .show-more-btn {