<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Server Deployment Records</title>
<style>
:root{
--bg: #f4f6f8;
--panel: #ffffff;
--text: #0f172a;
--muted:#5b677a;
--border:#d7dee7;
--border2:#e6edf5;
--shadow: 0 18px 50px rgba(16,24,40,.08);
--rowHover:#f6f8fb;
--rowSelected:#eaf2ff;
--focus:#2563eb;
--goodBg:#ecfdf3;
--warnBg:#fffbeb;
--badBg:#fff1f2;
--goodText:#047857;
--warnText:#92400e;
--badText:#b91c1c;
--chipBg: rgba(2,6,23,.04);
--chipBorder: rgba(2,6,23,.09);
}
*{box-sizing:border-box}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
background: radial-gradient(1200px 700px at 20% 0%, #fbfdff 0%, var(--bg) 60%, #f2f4f7 100%);
color:var(--text);
display:flex;
align-items:flex-start;
justify-content:center;
padding:40px 24px;
min-height:100vh;
}
.stage{
width: 980px; /* natural component width for photography */
max-width: 100%;
}
.card{
background: var(--panel);
border: 1px solid var(--border2);
border-radius: 14px;
box-shadow: var(--shadow);
overflow:hidden;
}
.headerbar{
display:flex;
align-items:center;
justify-content:space-between;
padding:16px 16px 12px 16px;
border-bottom:1px solid var(--border2);
gap:16px;
}
.title{
display:flex; flex-direction:column; gap:4px;
min-width: 320px;
}
.title .h{
font-weight: 690;
letter-spacing: -0.02em;
font-size: 14px;
color:#0b1220;
}
.title .sub{
font-size: 12px;
color: var(--muted);
}
.controls{
display:flex;
align-items:center;
gap:10px;
color:var(--muted);
font-size:12px;
white-space:nowrap;
}
.pill{
display:inline-flex; align-items:center; gap:8px;
padding:8px 10px;
background: rgba(2,6,23,.03);
border:1px solid rgba(2,6,23,.06);
border-radius: 999px;
}
.dot{
width:9px; height:9px; border-radius: 50%;
background: #2563eb;
box-shadow: 0 0 0 3px rgba(37,99,235,.12);
}
.tableWrap{
padding:0;
}
table{
width:100%;
border-collapse:separate;
border-spacing:0;
table-layout: fixed;
}
thead th{
position:relative;
text-align:left;
font-size: 12px;
color:#334155;
font-weight: 650;
padding:12px 12px;
border-bottom:1px solid var(--border);
background: linear-gradient(#fbfcfe, #f7f9fc);
user-select:none;
}
thead th:first-child{padding-left:14px}
thead th:last-child{padding-right:14px}
.thBtn{
display:flex;
align-items:center;
gap:8px;
width:100%;
cursor:pointer;
}
.sortState{
margin-left:auto;
width:14px;height:14px;
flex:0 0 auto;
opacity: .95;
}
.sortState svg{display:block}
.activeSort{
color:#0f3a84;
}
.activeSort .chev{
fill:#0f3a84;
}
tbody td{
padding:12px 12px;
border-bottom:1px solid var(--border2);
font-size: 13px;
color:#0f172a;
vertical-align:middle;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
tbody tr td:first-child{padding-left:14px}
tbody tr td:last-child{padding-right:14px}
tbody tr:last-child td{
border-bottom:none;
}
tbody tr:hover td{
background: var(--rowHover);
}
tbody tr.selected td{
background: var(--rowSelected);
border-bottom-color: rgba(37,99,235,.20);
}
.statusCell{
display:flex;
align-items:center;
gap:10px;
min-width: 120px;
}
.badge{
display:inline-flex;
align-items:center;
gap:8px;
padding:6px 10px;
border-radius:999px;
border:1px solid var(--chipBorder);
background: var(--chipBg);
font-size: 12px;
font-weight: 700;
letter-spacing: .01em;
color:#0b1220;
}
.badge .sig{
width:9px; height:9px; border-radius: 50%;
flex:0 0 auto;
border:1px solid rgba(2,6,23,.12);
background:#475569;
}
/* Status styles that remain legible without color-only meaning */
.badge[data-status="Success"]{
background: var(--goodBg);
border-color: rgba(4,120,87,.25);
color: var(--goodText);
}
.badge[data-status="Success"] .sig{ background: var(--goodText); border-color: rgba(4,120,87,.35); }
.badge[data-status="In Progress"]{
background: var(--warnBg);
border-color: rgba(146,64,14,.25);
color: var(--warnText);
}
.badge[data-status="In Progress"] .sig{ background: var(--warnText); border-color: rgba(146,64,14,.35); }
.badge[data-status="Failed"]{
background: var(--badBg);
border-color: rgba(185,28,28,.25);
color: var(--badText);
}
.badge[data-status="Failed"] .sig{ background: var(--badText); border-color: rgba(185,28,28,.35); }
.badge .label{
position:relative;
display:inline-flex;
align-items:center;
gap:8px;
max-width: 160px;
overflow:hidden;
text-overflow:ellipsis;
}
/* Add non-color cues: prefix + symbol */
.badge[data-status="Success"] .label::before{ content:"✓"; font-weight:900; }
.badge[data-status="In Progress"] .label::before{ content:"⏳"; font-weight:900; }
.badge[data-status="Failed"] .label::before{ content:"✕"; font-weight:900; }
.col-service{width: 270px}
.col-region{width: 150px}
.col-deployer{width: 150px}
.col-duration{width: 130px}
.col-timestamp{width: 205px}
.mono{
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 12.5px;
color:#0b1220;
}
.muted{color:var(--muted)}
.footer{
padding:12px 16px 14px 16px;
display:flex;
align-items:center;
justify-content:space-between;
gap:16px;
color:var(--muted);
font-size:12px;
}
.kbd{
border:1px solid rgba(2,6,23,.12);
background: rgba(2,6,23,.03);
padding:4px 7px;
border-radius: 8px;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
color:#0f172a;
}
/* Make the hovered row state visible even without real pointer hover */
tbody tr.demoHover td{ background: var(--rowHover); }
/* Keep selection distinct from hover if both apply; selection wins */
tbody tr.selected.demoHover td{ background: var(--rowSelected); }
/* Subtle row edge highlight for selected */
tbody tr.selected{
box-shadow: inset 0 0 0 1px rgba(37,99,235,.18);
position:relative;
z-index:1;
}
</style>
</head>
<body>
<div class="stage">
<div class="card" role="region" aria-label="Server deployment records">
<div class="headerbar">
<div class="title">
<div class="h">Deployment Audit Log</div>
<div class="sub">Eight recent server deployments across services and regions.</div>
</div>
<div class="controls" aria-label="Status summary">
<span class="pill" title="Last refresh time">
<span class="dot" aria-hidden="true"></span>
Updated just now
</span>
</div>
</div>
<div class="tableWrap">
<table aria-label="Data table: server deployment records">
<colgroup>
<col style="width:160px">
<col class="col-service">
<col class="col-region">
<col class="col-deployer">
<col class="col-duration">
<col class="col-timestamp">
</colgroup>
<thead>
<tr>
<th scope="col">
<div class="thBtn" aria-label="Sort by status">
<span>Status</span>
</div>
</th>
<th scope="col" class="">
<div class="thBtn" aria-label="Sort by service name">
<span>Service name</span>
</div>
</th>
<th scope="col">
<div class="thBtn" aria-label="Sort by region">
<span>Region</span>
</div>
</th>
<th scope="col">
<div class="thBtn" aria-label="Sort by deployed by">
<span>Deployed by</span>
</div>
</th>
<th scope="col">
<div class="thBtn activeSort" aria-label="Sort by duration (descending)">
<span>Duration</span>
<span class="sortState" aria-hidden="true">
<!-- descending caret -->
<svg width="14" height="14" viewBox="0 0 14 14" role="img" aria-hidden="true">
<path class="chev" d="M7 3.2c.25 0 .48.11.62.3l3 4.05c.29.39.2.95-.19 1.24-.15.11-.34.17-.53.17H3.1c-.5 0-.9-.41-.9-.92 0-.2.06-.38.17-.53l3-4.05c.14-.19.37-.3.62-.3Z" fill="#0f3a84"/>
<path class="chev" d="M7 10.3c-.25 0-.48-.11-.62-.3l-3-4.05c-.29-.39-.2-.95.19-1.24.15-.11.34-.17.53-.17h6.2c.5 0 .9.41.9.92 0 .2-.06.38-.17.53l-3 4.05c-.14.19-.37.3-.62.3Z" fill="#0f3a84" opacity=".9" transform="translate(0,0) scale(0.0001)"/>
</svg>
</span>
<span style="position:absolute;left:-9999px;">Sorted descending</span>
</div>
</th>
<th scope="col">
<div class="thBtn" aria-label="Sort by timestamp">
<span>Timestamp</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="Success" title="Deployment completed successfully">
<span class="label">Success</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="api-gateway">api-gateway</td>
<td class="muted" title="us-east-1">us-east-1</td>
<td title="Maya Chen"><span class="mono">maya.chen</span></td>
<td class="mono" title="Duration">08m 42s</td>
<td title="Timestamp">2026-07-26 09:12:31</td>
</tr>
<tr class="demoHover">
<td>
<div class="statusCell">
<span class="badge" data-status="In Progress" title="Deployment still running">
<span class="label">In Progress</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="billing-worker">billing-worker</td>
<td class="muted" title="eu-west-1">eu-west-1</td>
<td title="Jordan Patel"><span class="mono">j.patel</span></td>
<td class="mono" title="Duration">03m 10s</td>
<td title="Timestamp">2026-07-26 09:15:04</td>
</tr>
<tr class="selected">
<td>
<div class="statusCell">
<span class="badge" data-status="Failed" title="Deployment failed; rollback may be available">
<span class="label">Failed</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="orders-service">orders-service</td>
<td class="muted" title="ap-southeast-2">ap-southeast-2</td>
<td title="Avery Nguyen"><span class="mono">avery.ng</span></td>
<td class="mono" title="Duration">12m 05s</td>
<td title="Timestamp">2026-07-26 09:16:42</td>
</tr>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="Success" title="Deployment completed successfully">
<span class="label">Success</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="auth-service">auth-service</td>
<td class="muted" title="us-west-2">us-west-2</td>
<td title="Sam Rivera"><span class="mono">s.rivera</span></td>
<td class="mono" title="Duration">06m 28s</td>
<td title="Timestamp">2026-07-26 09:13:57</td>
</tr>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="Success" title="Deployment completed successfully">
<span class="label">Success</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="search-indexer">search-indexer</td>
<td class="muted" title="eu-central-1">eu-central-1</td>
<td title="Kim Thompson"><span class="mono">kim.t</span></td>
<td class="mono" title="Duration">09m 11s</td>
<td title="Timestamp">2026-07-26 09:10:19</td>
</tr>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="In Progress" title="Deployment still running">
<span class="label">In Progress</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="notifications-api">notifications-api</td>
<td class="muted" title="sa-east-1">sa-east-1</td>
<td title="Noah Brooks"><span class="mono">n.brooks</span></td>
<td class="mono" title="Duration">02m 49s</td>
<td title="Timestamp">2026-07-26 09:17:25</td>
</tr>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="Failed" title="Deployment failed; check logs">
<span class="label">Failed</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="recommendation-engine">recommendation-engine</td>
<td class="muted" title="us-east-2">us-east-2</td>
<td title="Priya Iyer"><span class="mono">priya.iyer</span></td>
<td class="mono" title="Duration">10m 37s</td>
<td title="Timestamp">2026-07-26 09:09:46</td>
</tr>
<tr>
<td>
<div class="statusCell">
<span class="badge" data-status="Success" title="Deployment completed successfully">
<span class="label">Success</span><span class="sig" aria-hidden="true"></span>
</span>
</div>
</td>
<td title="image-processor">image-processor</td>
<td class="muted" title="ca-central-1">ca-central-1</td>
<td title="Ethan Foster"><span class="mono">ethan.f</span></td>
<td class="mono" title="Duration">07m 54s</td>
<td title="Timestamp">2026-07-26 09:14:38</td>
</tr>
</tbody>
</table>
</div>
<div class="footer" aria-label="Table footer">
<div>
Showing <span class="kbd">8</span> records • Click headers to sort
</div>
<div class="muted">
Hover: <span class="kbd">billing-worker</span> • Selected: <span class="kbd">orders-service</span>
</div>
</div>
</div>
</div>
<script>
// Optional: keep UI states as requested (demo hover + one selected).
// Also make the sort header feel real (no external dependencies).
(function(){
const table = document.querySelector('table');
if(!table) return;
const ths = Array.from(table.querySelectorAll('thead th'));
const tbody = table.querySelector('tbody');
// Ensure only the demo hover row is visually hovered; prevent runtime hover from breaking the demo state.
const hoverRow = tbody.querySelector('tr.demoHover');
const selectedRow = tbody.querySelector('tr.selected');
// Provide an interactive sort on header click for professionalism,
// while leaving the visual "sorted descending" state on Duration.
function sortBy(colIndex, direction){
const rows = Array.from(tbody.querySelectorAll('tr'));
const getCellText = (tr) => {
const cell = tr.children[colIndex];
return cell ? cell.textContent.trim() : '';
};
const typeHint = colIndex; // 4 duration, 5 timestamp
function parseDuration(s){
// "MMm SSs"
const m = s.match(/(\d+)\s*m\s*(\d+)\s*s/i);
if(!m) return 0;
return (parseInt(m[1],10)*60 + parseInt(m[2],10)) * 1000;
}
function parseTimestamp(s){
// "YYYY-MM-DD HH:MM:SS"
// Convert to Date safely (assume local timezone)
const cleaned = s.replace(' ', 'T');
const d = new Date(cleaned);
return d.getTime();
}
rows.sort((a,b)=>{
const sa = getCellText(a);
const sb = getCellText(b);
let va, vb;
if(typeHint === 4){
va = parseDuration(sa);
vb = parseDuration(sb);
} else if(typeHint === 5){
va = parseTimestamp(sa);
vb = parseTimestamp(sb);
} else {
// status/service/region/deployer: case-insensitive
va = sa.toLowerCase();
vb = sb.toLowerCase();
}
if(va < vb) return direction === 'asc' ? -1 : 1;
if(va > vb) return direction === 'asc' ? 1 : -1;
return 0;
});
rows.forEach(r => tbody.appendChild(r));
}
// Initialize Duration header visual state already handled in CSS/markup.
// Make clicks update ordering but keep demo hovered/selected rows intact if possible.
const durationHeaderIndex = 4; // col index in table children
let lastDir = 'desc';
// Map headers to column indices
// Columns: status(0), service(1), region(2), deployed by(3), duration(4), timestamp(5)
const headerToCol = [0,1,2,3,4,5];
ths.forEach((th, i)=>{
th.style.cursor = 'pointer';
th.addEventListener('click', ()=>{
const col = headerToCol[i];
let dir = 'asc';
if(th.querySelector('.activeSort')) dir = (lastDir === 'asc') ? 'desc' : 'asc';
// Visual toggle on active sort (minimal changes)
// Remove activeSort class from all
ths.forEach(x=>{
const btn = x.querySelector('.thBtn');
if(btn) btn.classList.remove('activeSort');
});
const clickedBtn = th.querySelector('.thBtn');
if(clickedBtn) clickedBtn.classList.add('activeSort');
// Sort actual data
if(col === 4){
// Duration
lastDir = dir;
} else {
lastDir = 'asc';
}
// Use direction
sortBy(col, dir);
// Re-apply demo selected + hover after sorting, by matching service name
const selectedName = selectedRow ? selectedRow.querySelector('td:nth-child(2)')?.textContent.trim() : null;
const hoverName = hoverRow ? hoverRow.querySelector('td:nth-child(2)')?.textContent.trim() : null;
const newSelected = selectedName ? Array.from(tbody.querySelectorAll('tr')).find(tr=>{
const td = tr.querySelector('td:nth-child(2)');
return td && td.textContent.trim() === selectedName;
}) : null;
const newHover = hoverName ? Array.from(tbody.querySelectorAll('tr')).find(tr=>{
const td = tr.querySelector('td:nth-child(2)');
return td && td.textContent.trim() === hoverName;
}) : null;
tbody.querySelectorAll('tr.selected').forEach(tr=>tr.classList.remove('selected'));
tbody.querySelectorAll('tr.demoHover').forEach(tr=>tr.classList.remove('demoHover'));
if(newHover) newHover.classList.add('demoHover');
if(newSelected) newSelected.classList.add('selected');
});
});
// Keep the specified states even if user moves mouse quickly.
// If the user hovers over another row, revert to demo visuals shortly.
let lock = false;
document.addEventListener('mouseover', (e)=>{
if(lock) return;
const tr = e.target && e.target.closest ? e.target.closest('tr') : null;
if(!tr) return;
lock = true;
setTimeout(()=>{
lock = false;
// Ensure demo hover and selected remain
tbody.querySelectorAll('tr').forEach(r=>{
r.classList.remove('demoHover');
});
if(hoverRow){
const name = hoverRow.querySelector('td:nth-child(2)')?.textContent.trim();
if(name){
const rowNow = Array.from(tbody.querySelectorAll('tr')).find(r=>{
const td = r.querySelector('td:nth-child(2)');
return td && td.textContent.trim() === name;
});
if(rowNow) rowNow.classList.add('demoHover');
}
}
// Ensure selected remains
tbody.querySelectorAll('tr').forEach(r=>r.classList.remove('selected'));
if(selectedRow){
const name = selectedRow.querySelector('td:nth-child(2)')?.textContent.trim();
if(name){
const rowNow = Array.from(tbody.querySelectorAll('tr')).find(r=>{
const td = r.querySelector('td:nth-child(2)');
return td && td.textContent.trim() === name;
});
if(rowNow) rowNow.classList.add('selected');
}
}
}, 20);
}, {passive:true});
})();
</script>
</body>
</html>