Hey
<style>
* { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
.wrap { padding: 20px 0 12px; }
.row { display: flex; align-items: center; gap: 0; margin-bottom: 6px; }
.tl { flex: 1; position: relative; height: 38px; }
.appt {
position: absolute; height: 38px;
border-radius: 8px; border: 0.5px solid;
display: flex; align-items: center; justify-content: center;
font-size: 13px; font-weight: 500;
transition: left 0.7s cubic-bezier(.4,0,.2,1), background 0.4s, border-color 0.4s;
box-sizing: border-box; cursor: default; user-select: none;
}
.appt.past { background: #f5f5f3; border-color: #ccc; color: #888; }
.appt.future { background: #E6F1FB; border-color: #185FA5; color: #0C447C; }
.appt.moved { background: #FAEEDA; border-color: #BA7517; color: #633806; }
.appt.replanned { background: #E1F5EE; border-color: #0F6E56; color: #085041; }
.section-wrap { margin-bottom: 28px; }
.section-title { font-size: 12px; font-weight: 500; color: #222; margin-bottom: 10px; display: flex; align-items: center; gap: 8px; }
.badge { font-size: 10px; font-weight: 500; padding: 2px 7px; border-radius: 4px; border: 0.5px solid; }
.badge-off { background: #f5f5f3; color: #888; border-color: #ccc; }
.badge-on { background: #EAF3DE; color: #3B6D11; border-color: #639922; }
.caption { font-size: 11px; color: #888; margin-bottom: 10px; min-height: 16px; }
.btn-row { display: flex; gap: 8px; margin-bottom: 24px; }
.pill { padding: 5px 14px; border-radius: 20px; border: 0.5px solid #ccc; font-size: 12px; cursor: pointer; background: #f5f5f3; color: #888; transition: all .2s; font-family: inherit; }
.pill.active { background: #222; color: #fff; border-color: #222; }
.legend { display: flex; gap: 16px; margin: 4px 0 20px 0; flex-wrap: wrap; }
.leg-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: #888; }
.leg-dot { width: 10px; height: 10px; border-radius: 3px; border: 0.5px solid; flex-shrink: 0; }
.leg-dot.past { background: #f5f5f3; border-color: #ccc; }
.leg-dot.future { background: #E6F1FB; border-color: #185FA5; }
.leg-dot.moved { background: #FAEEDA; border-color: #BA7517; }
.leg-dot.replanned { background: #E1F5EE; border-color: #0F6E56; }
.divider { border: none; border-top: 0.5px solid #ddd; margin: 4px 0 20px; }
</style>
<div class="wrap">
<div class="btn-row">
<button class="pill active" id="btn-before" onclick="setState('before')">Before replan</button>
<button class="pill" id="btn-after" onclick="setState('after')">After replan</button>
</div>
<div class="section-wrap">
<div class="section-title">Replan always update schedule <span class="badge badge-off">OFF</span></div>
<div class="caption" id="cap-off">Appointments a, b, c, d sit evenly on the timeline.</div>
<div class="row"><div class="tl" id="tl-off"></div></div>
</div>
<hr class="divider">
<div class="section-wrap">
<div class="section-title">Replan always update schedule <span class="badge badge-on">ON</span></div>
<div class="caption" id="cap-on">Appointments a, b, c, d sit evenly on the timeline.</div>
<div class="row"><div class="tl" id="tl-on"></div></div>
</div>
<div class="legend">
<div class="leg-item"><div class="leg-dot past"></div> Past / unchanged</div>
<div class="leg-item"><div class="leg-dot replanned"></div> Replanned (b)</div>
<div class="leg-item"><div class="leg-dot moved"></div> Shifted downstream</div>
</div>
</div>
<script>
const W = 11;
const before = [
{ id:'a', label:'a', left:2, type:'past' },
{ id:'b', label:'b', left:24, type:'future' },
{ id:'c', label:'c', left:46, type:'future' },
{ id:'d', label:'d', left:68, type:'future' },
];
const afterOff = [
{ id:'a', label:'a', left:2, type:'past' },
{ id:'b', label:'b', left:33, type:'replanned' },
{ id:'c', label:'c', left:46, type:'future' },
{ id:'d', label:'d', left:68, type:'future' },
];
const afterOn = [
{ id:'a', label:'a', left:2, type:'past' },
{ id:'b', label:'b', left:33, type:'replanned' },
{ id:'c', label:'c', left:55, type:'moved' },
{ id:'d', label:'d', left:77, type:'moved' },
];
function render(tlId, appts) {
const tl = document.getElementById(tlId);
tl.innerHTML = '';
appts.forEach(ap => {
const el = document.createElement('div');
el.id = tlId + '-' + ap.id;
el.className = 'appt ' + ap.type;
el.style.left = ap.left + '%';
el.style.width = W + '%';
el.textContent = ap.label;
tl.appendChild(el);
});
}
function animateTo(tlId, appts) {
appts.forEach(ap => {
const el = document.getElementById(tlId + '-' + ap.id);
if (!el) return;
el.style.left = ap.left + '%';
el.className = 'appt ' + ap.type;
});
}
render('tl-off', before);
render('tl-on', before);
function setState(s) {
document.getElementById('btn-before').classList.toggle('active', s === 'before');
document.getElementById('btn-after').classList.toggle('active', s === 'after');
if (s === 'before') {
animateTo('tl-off', before);
animateTo('tl-on', before);
document.getElementById('cap-off').textContent = 'Appointments a, b, c, d sit evenly on the timeline.';
document.getElementById('cap-on').textContent = 'Appointments a, b, c, d sit evenly on the timeline.';
} else {
animateTo('tl-off', afterOff);
animateTo('tl-on', afterOn);
document.getElementById('cap-off').textContent = 'b is replanned and moves right — but c and d stay put. b is now crowded against c.';
document.getElementById('cap-on').textContent = 'b is replanned and moves right — and c, d shift right by the same amount, preserving spacing.';
}
}
</script>