added on local at 2026-07-12 22:38:38
| 1 | <div class="page-head"> | |
| 2 | <span class="page-eyebrow"><span class="dot"></span> Portfolio drift</span> | |
| 3 | <h1 class="page-title">Same file changed across multiple sites</h1> | |
| 4 | <p class="page-subtitle">Groups changes by basename (last path segment) and flags any file that drifted in 2+ monitored sites in the current window. <strong style="color:var(--accent-warn-hi)">Divergent</strong> = each site's copy has a different SHA. <strong style="color:var(--accent-ok)">Synchronized</strong> = everyone has the same content — usually an intentional broadcast you can confirm landed.</p> | |
| 5 | </div> | |
| 6 | ||
| 7 | <div style="display:flex;gap:14px;align-items:center;margin-bottom:12px"> | |
| 8 | <div>$picker_html</div> | |
| 9 | <div class="dim" style="font-size:12.5px">Window: <strong>$range_label</strong></div> | |
| 10 | </div> | |
| 11 | ||
| 12 | <div class="kpi-row" style="margin-bottom:16px"> | |
| 13 | <div class="kpi-tile"> | |
| 14 | <div class="kpi-label">Files touching 2+ sites</div> | |
| 15 | <div class="kpi-value">$group_count</div> | |
| 16 | </div> | |
| 17 | <div class="kpi-tile"> | |
| 18 | <div class="kpi-label">Divergent</div> | |
| 19 | <div class="kpi-value" style="color:var(--accent-warn-hi)">$divergent_count</div> | |
| 20 | </div> | |
| 21 | <div class="kpi-tile"> | |
| 22 | <div class="kpi-label">Synchronized</div> | |
| 23 | <div class="kpi-value" style="color:var(--accent-ok)">$sync_count</div> | |
| 24 | </div> | |
| 25 | </div> | |
| 26 | ||
| 27 | [if:!$has_groups] | |
| 28 | <div class="module glow-teal"> | |
| 29 | <div class="module-body"> | |
| 30 | <p class="dim" style="margin:0;font-size:13.5px">No cross-site basename overlap in this window. Widen the range to catch slower propagation.</p> | |
| 31 | </div> | |
| 32 | </div> | |
| 33 | [/if] | |
| 34 | ||
| 35 | [if:$has_groups] | |
| 36 | <style> | |
| 37 | .pd-table { width:100%;border-collapse:collapse } | |
| 38 | .pd-table th { text-align:left;font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:.06em;font-weight:600;padding:8px 10px;border-bottom:1px solid var(--border) } | |
| 39 | .pd-table td { padding:10px;vertical-align:top;border-bottom:1px solid var(--border) } | |
| 40 | .pd-table .basename { font-family:var(--mono);font-size:13px;color:var(--text);font-weight:600 } | |
| 41 | .pd-sites { display:flex;flex-direction:column;gap:6px } | |
| 42 | .pd-site { display:flex;align-items:center;gap:8px;font-size:12px;padding:4px 8px;background:var(--panel-2);border-radius:6px } | |
| 43 | .pd-site .sn { color:var(--text);font-weight:500 } | |
| 44 | .pd-site .srv { color:var(--text-dim);font-size:11.5px } | |
| 45 | .pd-site .sha { font-family:var(--mono);font-size:11px;color:var(--text-dim) } | |
| 46 | .pd-site .path { font-family:var(--mono);font-size:11px;color:var(--text-muted);flex:1;text-align:right;padding-left:12px } | |
| 47 | .pd-site .ts { color:var(--text-muted);font-size:11px;min-width:90px;text-align:right } | |
| 48 | </style> | |
| 49 | ||
| 50 | <div class="module"> | |
| 51 | <div class="module-head"><div class="left"> | |
| 52 | <div class="module-title">Cross-site file changes</div> | |
| 53 | <span class="module-subtitle">Ordered: divergent first (the ones you probably need to look at), then synchronized, both newest-first.</span> | |
| 54 | </div></div> | |
| 55 | <div class="module-body tight"> | |
| 56 | <table class="pd-table"> | |
| 57 | <thead><tr> | |
| 58 | <th style="width:280px">Basename</th> | |
| 59 | <th style="width:120px">Status</th> | |
| 60 | <th style="width:80px">Sites</th> | |
| 61 | <th>Per-site latest</th> | |
| 62 | </tr></thead> | |
| 63 | <tbody> | |
| 64 | [loop:@groups] | |
| 65 | <tr> | |
| 66 | <td class="basename">$basename</td> | |
| 67 | <td> | |
| 68 | <span class="pill $status_pill">$status_label</span> | |
| 69 | [if:$distinct_blobs]<span class="dim" style="display:block;font-size:11px;margin-top:3px">$distinct_blobs distinct blob[if:$distinct_blobs]s[/if]</span>[/if] | |
| 70 | </td> | |
| 71 | <td class="mono">$site_count</td> | |
| 72 | <td> | |
| 73 | <div class="pd-sites"> | |
| 74 | [loop:@per_site] | |
| 75 | <div class="pd-site"> | |
| 76 | <span class="sn">$scan_name</span> | |
| 77 | <span class="srv">$server_name</span> | |
| 78 | <span class="pill $status_pill" style="font-size:10px;padding:1px 6px">$status</span> | |
| 79 | <span class="sha">$blob_short</span> | |
| 80 | <span class="path" title="$full_path">$full_path_h</span> | |
| 81 | <span class="ts"><span class="ts" data-ts="$ts_epoch" data-fmt="relative"></span></span> | |
| 82 | <a href="$file_url" class="tr-chip" style="padding:2px 8px;font-size:11px">diff</a> | |
| 83 | </div> | |
| 84 | [/loop] | |
| 85 | </div> | |
| 86 | </td> | |
| 87 | </tr> | |
| 88 | [/loop] | |
| 89 | </tbody> | |
| 90 | </table> | |
| 91 | </div> | |
| 92 | </div> | |
| 93 | [/if] |