<!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:#5b667a;
--muted2:#7a869c;
--border:#e6eaf0;
--border2:#d7dde6;
--shadow: 0 10px 30px rgba(15, 23, 42, .06);
--focus:#2563eb;
--ok-bg: rgba(16,185,129,.10);
--ok-bd: rgba(16,185,129,.35);
--warn-bg: rgba(245,158,11,.10);
--warn-bd: rgba(245,158,11,.35);
--err-bg: rgba(239,68,68,.10);
--err-bd: rgba(239,68,68,.35);
--run-bg: rgba(59,130,246,.10);
--run-bd: rgba(59,130,246,.35);
--sel-bg: rgba(37,99,235,.10);
--sel-bd: rgba(37,99,235,.45);
--hov-bg: rgba(15,23,42,.035);
--hov-bd: rgba(15,23,42,.08);
--thead-bg: linear-gradient(#fbfcfe, #f3f6fb);
--row-h: 44px;
--radius: 14px;
--font: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
}
*{box-sizing:border-box}
body{
margin:0;
font-family:var(--font);
background: var(--bg);
color:var(--text);
display:flex;
min-height:100vh;
align-items:flex-start;
justify-content:center;
padding:48px 18px;
}
.stage{
width: 980px; /* natural/photographable workspace */
max-width: 98vw;
padding: 10px;
}
.card{
background: var(--panel);
border: 1px solid var(--border);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow:hidden;
}
.card-header{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:16px;
padding: 16px 16px 10px 16px;
}
.title{
display:flex;
flex-direction:column;
gap:4px;
}
.title h1{
margin:0;
font-size:14px;
letter-spacing:.2px;
font-weight:650;
color:#0b1324;
}
.subtitle{
font-size:12px;
color:var(--muted);
}
.legend{
display:flex;
gap:10px;
align-items:center;
color:var(--muted2);
font-size:12px;
user-select:none;
}
.pill{
display:inline-flex;
align-items:center;
gap:8px;
padding: 7px 10px;
border-radius: 999px;
border:1px solid var(--border);
background: linear-gradient(#ffffff, #fbfcfe);
}
.swatch{
width:10px; height:10px;
border-radius:3px;
border:1px solid rgba(0,0,0,.15);
background: transparent;
}
/* purely presentational small swatches */
.swatch.ok{ background: rgba(16,185,129,.12); border-color: rgba(16,185,129,.55); }
.swatch.run{ background: rgba(59,130,246,.12); border-color: rgba(59,130,246,.55); }
.swatch.warn{ background: rgba(245,158,11,.12); border-color: rgba(245,158,11,.55); }
.swatch.err{ background: rgba(239,68,68,.12); border-color: rgba(239,68,68,.55); }
.table-wrap{
padding: 6px 10px 14px 10px;
}
table{
width:100%;
border-collapse:separate;
border-spacing:0;
font-size:13px;
}
thead th{
text-align:left;
font-weight:650;
color:#1f2a44;
padding: 10px 12px;
border-bottom: 1px solid var(--border);
background: var(--thead-bg);
position:sticky;
top:0;
z-index:2;
white-space:nowrap;
}
th, td { border-right: 1px solid rgba(230,234,240,.75); }
th:last-child, td:last-child{ border-right:none; }
tbody td{
padding: 10px 12px;
border-bottom:1px solid rgba(230,234,240,.85);
color:#0f172a;
vertical-align:middle;
height: var(--row-h);
background: #fff;
}
tbody tr:last-child td{ border-bottom:none; }
/* Sort header state (simultaneous) */
.sortable{
display:flex;
align-items:center;
justify-content:space-between;
gap:10px;
}
.sort-indicator{
display:flex;
align-items:center;
gap:8px;
flex:0 0 auto;
}
.sort-label{
display:inline-flex;
align-items:center;
gap:8px;
}
.sort-arrow{
width:18px; height:18px;
display:inline-grid;
place-items:center;
border-radius:6px;
border:1px solid var(--border2);
background: linear-gradient(#fff, #f7faff);
color:#334155;
}
.sort-arrow svg{
display:block;
}
/* Column header "active sort" */
th.is-sorted{
background: linear-gradient(#fdfdff, #f2f6ff);
box-shadow: inset 0 -1px 0 rgba(37,99,235,.10);
}
th.is-sorted .sort-arrow{
border-color: rgba(37,99,235,.35);
box-shadow: inset 0 0 0 1px rgba(37,99,235,.12);
color: #1d4ed8;
}
/* Status presentation: legible without color alone */
.status{
display:inline-flex;
align-items:center;
gap:10px;
padding: 6px 10px;
border-radius: 999px;
border: 1px solid transparent;
line-height:1;
font-weight:700;
letter-spacing:.2px;
}
.status .dot{
width:10px; height:10px;
border-radius:50%;
border:1px solid rgba(0,0,0,.18);
flex:0 0 auto;
}
/* Use both icon/text and subtle border to remain legible without colour reliance */
.status .label{
font-weight:750;
font-size:12.5px;
}
.status .tag{
font-weight:700;
font-size:12px;
color:#42506c;
opacity:.95;
}
.status.ok{
background: var(--ok-bg);
border-color: var(--ok-bd);
}
.status.ok .dot{ background: rgba(16,185,129,.25); border-color: rgba(16,185,129,.55); }
.status.running{
background: var(--run-bg);
border-color: var(--run-bd);
}
.status.running .dot{ background: rgba(59,130,246,.25); border-color: rgba(59,130,246,.55); }
.status.warning{
background: var(--warn-bg);
border-color: var(--warn-bd);
}
.status.warning .dot{ background: rgba(245,158,11,.25); border-color: rgba(245,158,11,.55); }
.status.error{
background: var(--err-bg);
border-color: var(--err-bd);
}
.status.error .dot{ background: rgba(239,68,68,.25); border-color: rgba(239,68,68,.55); }
/* Row hover and selected simultaneously */
tbody tr.hovered td{
background: var(--hov-bg);
border-bottom-color: var(--hov-bd);
}
tbody tr.hovered td .row-emph{
color:#0b1b3c;
}
tbody tr.selected td{
background: var(--sel-bg);
border-bottom-color: rgba(37,99,235,.20);
}
tbody tr.selected td{
box-shadow: inset 0 0 0 1px rgba(37,99,235,.00);
}
tbody tr.selected td:first-child{
border-top-left-radius: 10px;
}
tbody tr.selected td:last-child{
border-top-right-radius: 10px;
}
/* Outer selected frame: subtle */
tbody tr.selected{
outline: 1px solid rgba(37,99,235,.45);
outline-offset: -1px;
position:relative;
z-index:3;
}
/* Make the table feel "tool-like" and dense but calm */
.meta{
display:flex;
flex-direction:column;
gap:2px;
}
.meta .main{
font-weight:700;
color:#0f172a;
}
.meta .sub{
color: var(--muted);
font-size:12px;
}
.mono{
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size:12.5px;
color:#22314d;
}
.small-muted{ color: var(--muted); font-size:12px; }
.foot{
padding: 0 16px 14px 16px;
display:flex;
align-items:center;
justify-content:space-between;
gap:12px;
color: var(--muted);
font-size:12px;
}
.kbd{
display:inline-flex;
align-items:center;
gap:6px;
}
.key{
border:1px solid var(--border2);
background: linear-gradient(#fff,#f7fbff);
border-radius:8px;
padding:3px 8px;
font-weight:700;
color:#334155;
box-shadow: inset 0 1px 0 rgba(255,255,255,.7);
}
/* Ensure row hover/selection visible even without interaction */
/* (We simulate states by classes already added.) */
</style>
</head>
<body>
<div class="stage">
<div class="card" role="region" aria-label="Server deployment records table">
<div class="card-header">
<div class="title">
<h1>Deployment Records</h1>
<div class="subtitle">Last 8 server deployments across services and regions.</div>
</div>
<div class="legend" aria-hidden="true">
<div class="pill"><span class="swatch run"></span><span>RUNNING</span></div>
<div class="pill"><span class="swatch ok"></span><span>SUCCESS</span></div>
<div class="pill"><span class="swatch warn"></span><span>WARNING</span></div>
<div class="pill"><span class="swatch err"></span><span>FAILED</span></div>
</div>
</div>
<div class="table-wrap">
<table aria-label="Server deployments">
<thead>
<tr>
<th style="width:150px;">
<div class="sortable">
<span class="sort-label"><span>Status</span></span>
<span class="sort-indicator" aria-hidden="true">
<!-- neutral icon since not sorted -->
<span class="sort-arrow" style="opacity:.55">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<path d="M8 10l4-4 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 14l4 4 4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity=".65"/>
</svg>
</span>
</span>
</div>
</th>
<th style="width:240px;">Service name</th>
<th style="width:150px;">Region</th>
<th style="width:190px;">Deployed by</th>
<th style="width:150px;">
<div class="sortable">
<span class="sort-label">Duration</span>
<!-- Sorted column header state -->
<span class="sort-indicator">
<span class="sort-arrow" title="Sorted ascending" aria-hidden="true">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<path d="M8 12h8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M12 8v8" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>
</span>
</span>
</div>
</th>
<th style="width:220px;">
<div class="sortable">
<span class="sort-label">Timestamp</span>
<span class="sort-indicator" aria-hidden="true">
<span class="sort-arrow" style="opacity:.55">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none">
<path d="M8 10l4-4 4 4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 14l4 4 4-4" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" opacity=".65"/>
</svg>
</span>
</span>
</div>
</th>
</tr>
</thead>
<tbody>
<tr class="selected" aria-selected="true">
<td>
<span class="status ok" role="status" aria-label="Status: SUCCESS">
<span class="dot" aria-hidden="true"></span>
<span class="label">SUCCESS</span>
<span class="tag">OK</span>
</span>
</td>
<td>
<div class="meta">
<div class="main row-emph">billing-api</div>
<div class="sub">v2.14.7 • rollout canary</div>
</div>
</td>
<td>
<div class="meta">
<div class="main row-emph">us-east-1</div>
<div class="sub">AZ: a,b</div>
</div>
</td>
<td>
<div class="meta">
<div class="main row-emph">M. Chen</div>
<div class="sub">pipeline: deploy-canary</div>
</div>
</td>
<td>
<span class="mono" title="Duration">02:41</span>
<div class="small-muted">CPU: 62%</div>
</td>
<td>
<div class="meta">
<div class="main row-emph mono">2026-07-26 09:14:08</div>
<div class="sub">build #8c31a</div>
</div>
</td>
</tr>
<tr class="hovered" aria-label="Hovered row">
<td>
<span class="status running" role="status" aria-label="Status: RUNNING">
<span class="dot" aria-hidden="true"></span>
<span class="label">RUNNING</span>
<span class="tag">LIVE</span>
</span>
</td>
<td>
<div class="meta">
<div class="main row-emph">auth-gateway</div>
<div class="sub">v1.9.3 • edge routing</div>
</div>
</td>
<td>
<div class="meta">
<div class="main row-emph">eu-west-2</div>
<div class="sub">AZ: b,c</div>
</div>
</td>
<td>
<div class="meta">
<div class="main row-emph">A. Rivera</div>
<div class="sub">pipeline: deploy-live</div>
</div>
</td>
<td>
<span class="mono" title="Duration">00:58</span>
<div class="small-muted">ETA: ~3m</div>
</td>
<td>
<div class="meta">
<div class="main row-emph mono">2026-07-26 09:18:42</div>
<div class="sub">build #d0a7f</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status warning" role="status" aria-label="Status: WARNING">
<span class="dot" aria-hidden="true"></span>
<span class="label">WARNING</span>
<span class="tag">DEG</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">search-indexer</div>
<div class="sub">v3.2.1 • shard refresh</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">ap-south-1</div>
<div class="sub">AZ: a</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">S. Patel</div>
<div class="sub">pipeline: deploy-batch</div>
</div>
</td>
<td>
<span class="mono">03:07</span>
<div class="small-muted">p95 latency +8%</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 08:56:31</div>
<div class="sub">build #61c9b</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status error" role="status" aria-label="Status: FAILED">
<span class="dot" aria-hidden="true"></span>
<span class="label">FAILED</span>
<span class="tag">ERR</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">email-worker</div>
<div class="sub">v5.0.0 • queue consumer</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">us-west-1</div>
<div class="sub">AZ: c</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">K. Mensah</div>
<div class="sub">pipeline: deploy-worker</div>
</div>
</td>
<td>
<span class="mono">01:22</span>
<div class="small-muted">error: image pull</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 08:41:10</div>
<div class="sub">build #0aa19</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status ok" role="status" aria-label="Status: SUCCESS">
<span class="dot" aria-hidden="true"></span>
<span class="label">SUCCESS</span>
<span class="tag">OK</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">ui-portal</div>
<div class="sub">v7.3.12 • static deploy</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">eu-central-1</div>
<div class="sub">AZ: a,b</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">D. Okafor</div>
<div class="sub">pipeline: deploy-edge</div>
</div>
</td>
<td>
<span class="mono">00:44</span>
<div class="small-muted">CDN warm</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 08:26:55</div>
<div class="sub">build #f7b62</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status running" role="status" aria-label="Status: RUNNING">
<span class="dot" aria-hidden="true"></span>
<span class="label">RUNNING</span>
<span class="tag">LIVE</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">stream-processor</div>
<div class="sub">v9.1.0 • rolling restart</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">sa-east-1</div>
<div class="sub">AZ: a</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">R. Almeida</div>
<div class="sub">pipeline: deploy-stream</div>
</div>
</td>
<td>
<span class="mono">01:34</span>
<div class="small-muted">stage: drain</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 08:10:22</div>
<div class="sub">build #2c4dd</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status warning" role="status" aria-label="Status: WARNING">
<span class="dot" aria-hidden="true"></span>
<span class="label">WARNING</span>
<span class="tag">DEG</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">reporting-api</div>
<div class="sub">v2.0.4 • background sync</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">us-east-2</div>
<div class="sub">AZ: b</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">T. Nguyen</div>
<div class="sub">pipeline: deploy-sync</div>
</div>
</td>
<td>
<span class="mono">02:06</span>
<div class="small-muted">retry: 2</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 07:48:03</div>
<div class="sub">build #8e21c</div>
</div>
</td>
</tr>
<tr>
<td>
<span class="status ok" role="status" aria-label="Status: SUCCESS">
<span class="dot" aria-hidden="true"></span>
<span class="label">SUCCESS</span>
<span class="tag">OK</span>
</span>
</td>
<td>
<div class="meta">
<div class="main">config-service</div>
<div class="sub">v1.6.9 • config push</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">ca-central-1</div>
<div class="sub">AZ: a,c</div>
</div>
</td>
<td>
<div class="meta">
<div class="main">J. Kowalski</div>
<div class="sub">pipeline: deploy-config</div>
</div>
</td>
<td>
<span class="mono">00:36</span>
<div class="small-muted">no errors</div>
</td>
<td>
<div class="meta">
<div class="main mono">2026-07-26 07:30:49</div>
<div class="sub">build #31aa0</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="foot">
<div class="kbd" aria-hidden="true">
<span class="key">Sort</span><span class="small-muted">active on <b>Duration</b></span>
</div>
<div class="small-muted">Rows: 8 • Sorted header shown • Hovered + selected states simulated</div>
</div>
</div>
</div>
<script>
// No external resources; keep component static but accessible.
// We still implement a tiny assist: when user hovers/clicks, states won't conflict with the demo states.
// This preserves the requirement of one hovered row and one selected row simultaneously.
(function(){
const table = document.querySelector('table');
if(!table) return;
// Keep the demo classes stable; prevent interactive toggles from removing them.
table.addEventListener('mouseover', (e)=>{
const tr = e.target.closest('tr');
if(!tr) return;
// Do not replace demo hovered row if it is different.
const demoHovered = table.querySelector('tbody tr.hovered');
if(demoHovered && tr !== demoHovered) return;
});
// Prevent click selection from changing demo selected row.
table.addEventListener('click', (e)=>{
const tr = e.target.closest('tr');
if(!tr) return;
const demoSelected = table.querySelector('tbody tr.selected');
if(demoSelected && tr !== demoSelected) {
e.preventDefault();
e.stopPropagation();
}
});
})();
</script>
</body>
</html>