Game — Game Roadmap / Early Access Timeline
A dark neon early-access roadmap page for a fictional game, Hollow Reign by Nullforge Studio. A glowing vertical timeline tracks five milestones from launch stabilization to the 1.0 release, each card carrying a status pill, date window, feature bullets, and per-milestone progress bars. Includes a Phase 2 of 5 progress header, status filter chips with live counts, an animated spine that fills to the current marker, expandable cards, a timeline/list view toggle, and toast feedback.
MCP
Code
:root {
--bg: #0a0b10;
--bg-2: #12131c;
--panel: #171926;
--panel-2: #1f2233;
--text: #e7e9f3;
--muted: #9aa0bf;
--line: rgba(231, 233, 243, 0.10);
--line-2: rgba(231, 233, 243, 0.18);
--accent: #00e5ff;
--accent-2: #7c4dff;
--accent-3: #ff3d71;
--success: #36e27a;
--warn: #ffc857;
--danger: #ff4d4d;
--glow: 0 0 18px rgba(0, 229, 255, 0.45);
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
--font-display: "Orbitron", sans-serif;
--font-body: "Inter", sans-serif;
}
*, *::before, *::after { box-sizing: border-box; }
body {
margin: 0;
background:
radial-gradient(900px 420px at 80% -10%, rgba(124, 77, 255, 0.14), transparent 60%),
radial-gradient(700px 380px at 10% 0%, rgba(0, 229, 255, 0.10), transparent 55%),
var(--bg);
color: var(--text);
font-family: var(--font-body);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page { max-width: 980px; margin: 0 auto; padding: 0 20px 64px; }
button { font: inherit; color: inherit; cursor: pointer; }
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: var(--r-sm);
}
/* ===== Top bar ===== */
.topbar {
display: flex;
align-items: center;
gap: 24px;
padding: 18px 0;
border-bottom: 1px solid var(--line);
}
.brand {
display: inline-flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: var(--text);
}
.brand-mark {
color: var(--accent);
text-shadow: var(--glow);
font-size: 18px;
}
.brand-name {
font-family: var(--font-display);
font-weight: 900;
font-size: 15px;
letter-spacing: 0.14em;
}
.topnav { display: flex; gap: 4px; margin-left: auto; }
.topnav a {
color: var(--muted);
text-decoration: none;
font-size: 13px;
font-weight: 600;
padding: 8px 12px;
border-radius: var(--r-sm);
transition: color 0.18s, background 0.18s;
}
.topnav a:hover { color: var(--text); background: rgba(231, 233, 243, 0.06); }
.topnav a.active {
color: var(--accent);
background: rgba(0, 229, 255, 0.08);
}
/* ===== Buttons ===== */
.btn {
font-family: var(--font-display);
font-weight: 700;
font-size: 13px;
letter-spacing: 0.06em;
text-transform: uppercase;
border: 1px solid transparent;
padding: 13px 24px;
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
transition: transform 0.15s, box-shadow 0.2s, background 0.2s, filter 0.2s;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn-primary {
background: linear-gradient(135deg, var(--accent), #00aaff);
color: #021016;
box-shadow: 0 0 0 rgba(0, 229, 255, 0);
}
.btn-primary:hover { box-shadow: var(--glow); filter: brightness(1.08); }
.btn-ghost {
background: transparent;
border-color: var(--line-2);
color: var(--text);
}
.btn-ghost:hover { border-color: var(--accent); box-shadow: var(--glow); }
.btn-sm { padding: 9px 16px; font-size: 11px; }
/* ===== Hero ===== */
.hero { padding: 56px 0 24px; text-align: center; }
.hero-kicker {
margin: 0 0 10px;
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent);
text-shadow: 0 0 12px rgba(0, 229, 255, 0.4);
}
.hero h1 {
margin: 0;
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(30px, 5.4vw, 52px);
letter-spacing: 0.04em;
background: linear-gradient(180deg, #ffffff, #9aa0bf);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.hero-sub {
max-width: 600px;
margin: 14px auto 0;
color: var(--muted);
font-size: 15px;
}
/* ===== Phase meter ===== */
.phase-meter {
max-width: 640px;
margin: 32px auto 0;
padding: 20px 22px 16px;
background: linear-gradient(180deg, var(--panel-2), var(--panel));
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
box-shadow: inset 0 1px 0 rgba(231, 233, 243, 0.06), 0 14px 36px rgba(0, 0, 0, 0.4);
text-align: left;
}
.phase-meter-head {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 12px;
}
.phase-label {
font-family: var(--font-display);
font-size: 14px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.phase-label strong { color: var(--accent); }
.phase-pct {
font-family: var(--font-display);
font-size: 12px;
font-weight: 700;
color: var(--muted);
letter-spacing: 0.06em;
}
.meter {
height: 10px;
background: rgba(0, 0, 0, 0.45);
border: 1px solid var(--line);
border-radius: 999px;
overflow: hidden;
}
.meter-fill {
height: 100%;
width: 0;
border-radius: 999px;
background: linear-gradient(90deg, var(--accent-2), var(--accent));
box-shadow: 0 0 14px rgba(0, 229, 255, 0.55);
transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.meter-fill.is-filled { width: var(--target); }
.meter-sm { height: 8px; }
.meter-ticks {
display: flex;
justify-content: space-between;
margin-top: 10px;
}
.tick {
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
color: var(--muted);
}
.tick.done { color: var(--success); }
.tick.active {
color: var(--accent);
text-shadow: 0 0 10px rgba(0, 229, 255, 0.6);
animation: pulse-text 2.2s ease-in-out infinite;
}
@keyframes pulse-text {
0%, 100% { opacity: 1; }
50% { opacity: 0.55; }
}
/* ===== Controls ===== */
.controls {
display: flex;
flex-wrap: wrap;
gap: 14px;
justify-content: space-between;
align-items: center;
margin: 40px 0 28px;
}
.chips { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--panel);
border: 1px solid var(--line);
color: var(--muted);
font-size: 12.5px;
font-weight: 600;
padding: 8px 14px;
border-radius: 999px;
transition: border-color 0.18s, color 0.18s, box-shadow 0.2s, background 0.18s;
}
.chip:hover { border-color: var(--line-2); color: var(--text); }
.chip.is-on {
border-color: var(--accent);
color: var(--text);
background: rgba(0, 229, 255, 0.08);
box-shadow: 0 0 12px rgba(0, 229, 255, 0.25);
}
.chip-released.is-on { border-color: var(--success); background: rgba(54, 226, 122, 0.08); box-shadow: 0 0 12px rgba(54, 226, 122, 0.25); }
.chip-progress.is-on { border-color: var(--warn); background: rgba(255, 200, 87, 0.08); box-shadow: 0 0 12px rgba(255, 200, 87, 0.25); }
.chip-planned.is-on { border-color: var(--accent-2); background: rgba(124, 77, 255, 0.1); box-shadow: 0 0 12px rgba(124, 77, 255, 0.3); }
.chip-count {
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
background: rgba(0, 0, 0, 0.4);
border-radius: 999px;
padding: 2px 7px;
}
.view-toggle {
display: flex;
border: 1px solid var(--line-2);
border-radius: var(--r-md);
overflow: hidden;
}
.vt-btn {
background: transparent;
border: none;
color: var(--muted);
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 9px 16px;
transition: background 0.18s, color 0.18s;
}
.vt-btn + .vt-btn { border-left: 1px solid var(--line); }
.vt-btn:hover { color: var(--text); }
.vt-btn.is-on {
background: rgba(0, 229, 255, 0.1);
color: var(--accent);
text-shadow: 0 0 8px rgba(0, 229, 255, 0.5);
}
/* ===== Roadmap / timeline ===== */
.roadmap { position: relative; padding-left: 44px; }
.spine {
position: absolute;
left: 13px;
top: 8px;
bottom: 8px;
width: 3px;
background: rgba(231, 233, 243, 0.08);
border-radius: 999px;
}
.spine-fill {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 0;
border-radius: 999px;
background: linear-gradient(180deg, var(--success), var(--accent));
box-shadow: 0 0 12px rgba(0, 229, 255, 0.5);
transition: height 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.milestone { position: relative; margin-bottom: 26px; }
.milestone:last-of-type { margin-bottom: 0; }
.node {
position: absolute;
left: -38px;
top: 26px;
width: 15px;
height: 15px;
border-radius: 50%;
background: var(--panel-2);
border: 2px solid var(--line-2);
z-index: 1;
}
.milestone[data-status="released"] .node {
background: var(--success);
border-color: var(--success);
box-shadow: 0 0 12px rgba(54, 226, 122, 0.6);
}
.milestone[data-status="progress"] .node {
background: var(--accent);
border-color: var(--accent);
box-shadow: 0 0 14px rgba(0, 229, 255, 0.75);
animation: pulse-node 2s ease-in-out infinite;
}
@keyframes pulse-node {
0%, 100% { box-shadow: 0 0 6px rgba(0, 229, 255, 0.5); }
50% { box-shadow: 0 0 18px rgba(0, 229, 255, 0.95); }
}
.node-final { border-radius: 3px; transform: rotate(45deg); }
.current-flag {
position: absolute;
left: -8px;
top: -14px;
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--accent);
text-shadow: 0 0 10px rgba(0, 229, 255, 0.6);
animation: pulse-text 2.2s ease-in-out infinite;
}
/* ===== Cards ===== */
.card {
background: linear-gradient(180deg, var(--panel-2), var(--panel));
border: 1px solid var(--line);
border-radius: var(--r-lg);
clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
box-shadow: inset 0 1px 0 rgba(231, 233, 243, 0.05);
transition: border-color 0.2s, box-shadow 0.25s, transform 0.2s;
}
.card:hover {
border-color: var(--line-2);
transform: translateY(-2px);
box-shadow: inset 0 1px 0 rgba(231, 233, 243, 0.05), 0 12px 32px rgba(0, 0, 0, 0.45);
}
.milestone.is-current .card {
border-color: rgba(0, 229, 255, 0.45);
box-shadow: inset 0 1px 0 rgba(0, 229, 255, 0.12), 0 0 24px rgba(0, 229, 255, 0.18);
}
.card-head {
position: relative;
display: block;
width: 100%;
text-align: left;
background: transparent;
border: none;
padding: 20px 52px 18px 22px;
}
.card-meta { display: flex; align-items: center; gap: 12px; margin-bottom: 8px; }
.card-date {
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.1em;
color: var(--muted);
text-transform: uppercase;
}
.card-title {
margin: 0;
font-family: var(--font-display);
font-size: 19px;
font-weight: 700;
letter-spacing: 0.03em;
}
.card-tag { margin: 5px 0 0; color: var(--muted); font-size: 13.5px; }
.card-caret {
position: absolute;
right: 22px;
top: 50%;
transform: translateY(-50%);
color: var(--muted);
font-size: 15px;
transition: transform 0.25s, color 0.2s;
}
.card-head[aria-expanded="true"] .card-caret {
transform: translateY(-50%) rotate(180deg);
color: var(--accent);
}
.card-head:hover .card-caret { color: var(--accent); }
.card-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.card-body > * { overflow: hidden; }
.milestone.is-open .card-body { grid-template-rows: 1fr; }
.feature-list {
list-style: none;
margin: 0;
padding: 0 22px;
border-top: 1px solid var(--line);
}
.feature-list li {
position: relative;
padding: 10px 0 10px 26px;
font-size: 14px;
color: var(--text);
border-bottom: 1px dashed var(--line);
}
.feature-list li:first-child { padding-top: 16px; }
.feature-list li::before {
content: "◇";
position: absolute;
left: 2px;
color: var(--accent-2);
}
.feature-list li.done { color: var(--muted); }
.feature-list li.done::before { content: "✓"; color: var(--success); }
.feature-list li.wip::before {
content: "◈";
color: var(--warn);
animation: pulse-text 1.8s ease-in-out infinite;
}
.milestone-progress { padding: 16px 22px 0; }
.mp-head {
display: flex;
justify-content: space-between;
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 8px;
}
.card-foot {
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: space-between;
align-items: center;
padding: 16px 22px 22px;
}
.foot-stat { font-size: 12.5px; color: var(--muted); }
.foot-stat strong { color: var(--text); }
.link {
color: var(--accent);
font-size: 13px;
font-weight: 700;
text-decoration: none;
transition: text-shadow 0.2s;
}
.link:hover { text-shadow: 0 0 10px rgba(0, 229, 255, 0.6); text-decoration: underline; }
/* ===== Pills ===== */
.pill {
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
padding: 4px 11px;
border-radius: 999px;
border: 1px solid;
}
.pill-released { color: var(--success); border-color: rgba(54, 226, 122, 0.5); background: rgba(54, 226, 122, 0.1); }
.pill-progress {
color: var(--warn);
border-color: rgba(255, 200, 87, 0.5);
background: rgba(255, 200, 87, 0.1);
animation: pulse-text 2.4s ease-in-out infinite;
}
.pill-planned { color: #b39bff; border-color: rgba(124, 77, 255, 0.55); background: rgba(124, 77, 255, 0.12); }
/* ===== List view ===== */
.roadmap.view-list { padding-left: 0; }
.roadmap.view-list .spine,
.roadmap.view-list .node,
.roadmap.view-list .current-flag { display: none; }
.roadmap.view-list .milestone { margin-bottom: 12px; }
.roadmap.view-list .card { clip-path: none; border-radius: var(--r-md); }
.roadmap.view-list .card-head { padding: 16px 52px 14px 20px; }
.roadmap.view-list .card-title { font-size: 16px; }
.roadmap.view-list .milestone.is-current .card { border-left: 3px solid var(--accent); }
/* ===== Filter hide ===== */
.milestone.is-hidden { display: none; }
.empty-note {
color: var(--muted);
text-align: center;
font-size: 14px;
padding: 32px 0;
}
/* ===== CTA band ===== */
.cta-band {
margin-top: 56px;
padding: 30px 32px;
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: space-between;
align-items: center;
background:
linear-gradient(120deg, rgba(0, 229, 255, 0.08), rgba(124, 77, 255, 0.10)),
var(--panel);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
clip-path: polygon(18px 0, 100% 0, 100% calc(100% - 18px), calc(100% - 18px) 100%, 0 100%, 0 18px);
}
.cta-copy h2 {
margin: 0;
font-family: var(--font-display);
font-size: 20px;
font-weight: 700;
letter-spacing: 0.04em;
}
.cta-copy p { margin: 6px 0 0; color: var(--muted); font-size: 14px; }
.cta-actions { display: flex; flex-wrap: wrap; gap: 12px; }
/* ===== Toast ===== */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 16px);
background: var(--panel-2);
color: var(--text);
border: 1px solid var(--accent);
box-shadow: var(--glow);
border-radius: var(--r-md);
padding: 12px 22px;
font-size: 14px;
font-weight: 600;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 50;
max-width: calc(100vw - 40px);
}
.toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* ===== Reduced motion ===== */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important; transition: none !important; }
}
/* ===== Responsive ===== */
@media (max-width: 760px) {
.topnav { display: none; }
.topbar { justify-content: space-between; }
}
@media (max-width: 520px) {
.page { padding: 0 14px 48px; }
.hero { padding: 40px 0 16px; }
.hero-sub { font-size: 14px; }
.phase-meter { padding: 16px 16px 12px; }
.controls { flex-direction: column; align-items: stretch; }
.view-toggle { align-self: stretch; }
.vt-btn { flex: 1; }
.roadmap { padding-left: 34px; }
.spine { left: 9px; }
.node { left: -31px; top: 24px; width: 13px; height: 13px; }
.current-flag { left: -4px; }
.card-head { padding: 16px 44px 14px 16px; }
.card-caret { right: 16px; }
.card-title { font-size: 16px; }
.feature-list { padding: 0 16px; }
.milestone-progress { padding: 14px 16px 0; }
.card-foot { padding: 14px 16px 18px; }
.cta-band { padding: 22px 18px; }
.cta-actions { width: 100%; }
.cta-actions .btn { flex: 1; padding-left: 12px; padding-right: 12px; }
}/* Hollow Reign — Roadmap page interactions */
(() => {
"use strict";
const roadmap = document.getElementById("roadmap");
const milestones = Array.from(roadmap.querySelectorAll(".milestone"));
const chips = Array.from(document.querySelectorAll(".chip"));
const viewBtns = Array.from(document.querySelectorAll(".vt-btn"));
const spineFill = document.getElementById("spineFill");
const emptyNote = document.getElementById("emptyNote");
const toastEl = document.getElementById("toast");
/* ---------- Toast helper ---------- */
let toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("is-show");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toastEl.classList.remove("is-show"), 2400);
}
/* ---------- Expandable cards ---------- */
milestones.forEach((m) => {
const head = m.querySelector(".card-head");
// Sync initial state from aria-expanded markup
if (head.getAttribute("aria-expanded") === "true") m.classList.add("is-open");
head.addEventListener("click", () => {
const open = m.classList.toggle("is-open");
head.setAttribute("aria-expanded", String(open));
});
});
/* ---------- Status filtering ---------- */
const counts = { all: milestones.length, released: 0, progress: 0, planned: 0 };
milestones.forEach((m) => { counts[m.dataset.status] += 1; });
document.querySelectorAll(".chip-count").forEach((el) => {
el.textContent = counts[el.dataset.count];
});
const FILTER_LABELS = {
all: "Showing all milestones",
released: "Showing released updates",
progress: "Showing work in progress",
planned: "Showing planned updates",
};
function applyFilter(filter) {
let visible = 0;
milestones.forEach((m) => {
const show = filter === "all" || m.dataset.status === filter;
m.classList.toggle("is-hidden", !show);
if (show) visible += 1;
});
emptyNote.hidden = visible > 0;
requestAnimationFrame(updateSpine);
}
chips.forEach((chip) => {
chip.addEventListener("click", () => {
if (chip.classList.contains("is-on")) return;
chips.forEach((c) => c.classList.remove("is-on"));
chip.classList.add("is-on");
const filter = chip.dataset.filter;
applyFilter(filter);
toast(FILTER_LABELS[filter]);
});
});
/* ---------- Timeline / list view toggle ---------- */
viewBtns.forEach((btn) => {
btn.addEventListener("click", () => {
if (btn.classList.contains("is-on")) return;
viewBtns.forEach((b) => {
const on = b === btn;
b.classList.toggle("is-on", on);
b.setAttribute("aria-pressed", String(on));
});
const view = btn.dataset.view;
roadmap.classList.toggle("view-timeline", view === "timeline");
roadmap.classList.toggle("view-list", view === "list");
if (view === "timeline") requestAnimationFrame(updateSpine);
toast(view === "timeline" ? "Timeline view" : "List view");
});
});
/* ---------- Animated progress line to current milestone ---------- */
function updateSpine() {
const current = roadmap.querySelector(".milestone.is-current:not(.is-hidden)");
if (!current) { spineFill.style.height = "0px"; return; }
const node = current.querySelector(".node");
const spineRect = spineFill.parentElement.getBoundingClientRect();
const nodeRect = node.getBoundingClientRect();
const h = nodeRect.top + nodeRect.height / 2 - spineRect.top;
spineFill.style.height = Math.max(0, h) + "px";
}
// Re-measure when cards expand/collapse settle and on resize.
roadmap.addEventListener("transitionend", (e) => {
if (e.target.classList.contains("card-body")) updateSpine();
});
window.addEventListener("resize", updateSpine);
/* ---------- Phase meter fill on load ---------- */
function fillMeters() {
document.querySelectorAll(".meter-fill[style*='--target']").forEach((el) => {
el.classList.add("is-filled");
});
}
/* ---------- CTAs ---------- */
const wire = (id, msg) => {
const el = document.getElementById(id);
if (el) el.addEventListener("click", () => toast(msg));
};
wire("wishlistBtn", "Hollow Reign added to your wishlist ✦");
wire("ctaWishlist", "Hollow Reign added to your wishlist ✦");
wire("ctaDiscord", "Invite sent — welcome to the Council, Vanguard.");
/* ---------- Init ---------- */
window.addEventListener("load", () => {
fillMeters();
// Slight delay so the spine animates after layout is final.
setTimeout(updateSpine, 250);
});
// Fallback in case load already fired (e.g. inlined in srcdoc)
if (document.readyState === "complete") {
fillMeters();
setTimeout(updateSpine, 250);
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hollow Reign — Early Access Roadmap</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@500;700;900&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<!-- ===== Top bar ===== -->
<header class="topbar">
<a class="brand" href="#" aria-label="Hollow Reign home">
<span class="brand-mark" aria-hidden="true">◆</span>
<span class="brand-name">HOLLOW REIGN</span>
</a>
<nav class="topnav" aria-label="Site">
<a href="#">Overview</a>
<a href="#" class="active" aria-current="page">Roadmap</a>
<a href="#">Patch Notes</a>
<a href="#">Community</a>
</nav>
<button class="btn btn-primary btn-sm" id="wishlistBtn" type="button">+ Wishlist</button>
</header>
<!-- ===== Hero / progress header ===== -->
<section class="hero" aria-labelledby="heroTitle">
<p class="hero-kicker">Early Access · by Nullforge Studio</p>
<h1 id="heroTitle">Development Roadmap</h1>
<p class="hero-sub">
Hollow Reign launched into Early Access on Dec 12, 2025. Five major phases stand between
us and 1.0 — every milestone below is shaped with the community, in the open.
</p>
<div class="phase-meter" role="group" aria-label="Overall development progress">
<div class="phase-meter-head">
<span class="phase-label">Phase <strong id="phaseCurrent">2</strong> of <strong>5</strong></span>
<span class="phase-pct" id="phasePct">38% to 1.0</span>
</div>
<div class="meter" role="progressbar" aria-valuenow="38" aria-valuemin="0" aria-valuemax="100" aria-label="38 percent complete toward 1.0">
<div class="meter-fill" id="meterFill" style="--target:38%"></div>
</div>
<div class="meter-ticks" aria-hidden="true">
<span class="tick done">I</span>
<span class="tick active">II</span>
<span class="tick">III</span>
<span class="tick">IV</span>
<span class="tick">1.0</span>
</div>
</div>
</section>
<!-- ===== Controls ===== -->
<div class="controls">
<div class="chips" role="group" aria-label="Filter milestones by status">
<button class="chip is-on" type="button" data-filter="all">All <span class="chip-count" data-count="all"></span></button>
<button class="chip chip-released" type="button" data-filter="released">Released <span class="chip-count" data-count="released"></span></button>
<button class="chip chip-progress" type="button" data-filter="progress">In Progress <span class="chip-count" data-count="progress"></span></button>
<button class="chip chip-planned" type="button" data-filter="planned">Planned <span class="chip-count" data-count="planned"></span></button>
</div>
<div class="view-toggle" role="group" aria-label="Switch view">
<button class="vt-btn is-on" type="button" data-view="timeline" aria-pressed="true">
<span aria-hidden="true">⟋</span> Timeline
</button>
<button class="vt-btn" type="button" data-view="list" aria-pressed="false">
<span aria-hidden="true">☰</span> List
</button>
</div>
</div>
<!-- ===== Timeline ===== -->
<section class="roadmap view-timeline" id="roadmap" aria-label="Roadmap milestones">
<div class="spine" aria-hidden="true">
<div class="spine-fill" id="spineFill"></div>
</div>
<!-- Milestone 1 -->
<article class="milestone" data-status="released">
<div class="node" aria-hidden="true"></div>
<div class="card">
<button class="card-head" type="button" aria-expanded="false">
<div class="card-meta">
<span class="pill pill-released">Released</span>
<span class="card-date">Dec 2025</span>
</div>
<h2 class="card-title">Update 1.1 · “First Embers”</h2>
<p class="card-tag">Launch stabilization & quality-of-life pass</p>
<span class="card-caret" aria-hidden="true">▾</span>
</button>
<div class="card-body">
<ul class="feature-list">
<li class="done">Co-op session reconnect & host migration</li>
<li class="done">Loadout presets (3 slots) and quick-swap wheel</li>
<li class="done">Performance pass: +22% avg FPS on mid-tier GPUs</li>
<li class="done">112 community-reported bugs fixed</li>
</ul>
<div class="card-foot">
<span class="foot-stat"><strong>4/4</strong> deliverables shipped</span>
<a class="link" href="#">Read patch notes →</a>
</div>
</div>
</div>
</article>
<!-- Milestone 2 -->
<article class="milestone" data-status="released">
<div class="node" aria-hidden="true"></div>
<div class="card">
<button class="card-head" type="button" aria-expanded="false">
<div class="card-meta">
<span class="pill pill-released">Released</span>
<span class="card-date">Feb 2026</span>
</div>
<h2 class="card-title">Update 1.2 · “Ashen Vanguard”</h2>
<p class="card-tag">First major content drop — new region & faction</p>
<span class="card-caret" aria-hidden="true">▾</span>
</button>
<div class="card-body">
<ul class="feature-list">
<li class="done">New region: the Cindervault (12 zones, 3 bosses)</li>
<li class="done">Ashen Vanguard faction with reputation track</li>
<li class="done">Weapon crafting tier III + 40 new blueprints</li>
<li class="done">Photo mode with HUD-free capture</li>
</ul>
<div class="card-foot">
<span class="foot-stat"><strong>4/4</strong> deliverables shipped</span>
<a class="link" href="#">Read patch notes →</a>
</div>
</div>
</div>
</article>
<!-- Milestone 3 (current) -->
<article class="milestone is-current" data-status="progress" id="currentMilestone">
<div class="node" aria-hidden="true"></div>
<div class="current-flag" aria-hidden="true">▶ You are here</div>
<div class="card">
<button class="card-head" type="button" aria-expanded="true">
<div class="card-meta">
<span class="pill pill-progress">In Progress</span>
<span class="card-date">Apr – Jul 2026</span>
</div>
<h2 class="card-title">Update 2.0 · “Neon Drift”</h2>
<p class="card-tag">PvP arenas, vehicle traversal & the Drift economy</p>
<span class="card-caret" aria-hidden="true">▾</span>
</button>
<div class="card-body">
<ul class="feature-list">
<li class="done">Hoverbike traversal system (internal alpha complete)</li>
<li class="wip">3v3 Drift Arenas — closed beta live now</li>
<li class="wip">Ranked ladder with seasonal rewards</li>
<li>Cross-progression between PC and cloud saves</li>
</ul>
<div class="milestone-progress">
<div class="mp-head">
<span>Milestone progress</span><span>62%</span>
</div>
<div class="meter meter-sm" role="progressbar" aria-valuenow="62" aria-valuemin="0" aria-valuemax="100" aria-label="Update 2.0 progress: 62 percent">
<div class="meter-fill" style="--target:62%"></div>
</div>
</div>
<div class="card-foot">
<span class="foot-stat"><strong>1/4</strong> shipped · <strong>2</strong> in beta</span>
<a class="link" href="#">Join the beta →</a>
</div>
</div>
</div>
</article>
<!-- Milestone 4 -->
<article class="milestone" data-status="planned">
<div class="node" aria-hidden="true"></div>
<div class="card">
<button class="card-head" type="button" aria-expanded="false">
<div class="card-meta">
<span class="pill pill-planned">Planned</span>
<span class="card-date">Q4 2026</span>
</div>
<h2 class="card-title">Update 3.0 · “Hollow Depths”</h2>
<p class="card-tag">Endgame expansion — raids & the Depths biome</p>
<span class="card-caret" aria-hidden="true">▾</span>
</button>
<div class="card-body">
<ul class="feature-list">
<li>8-player raid: the Sunken Throne</li>
<li>Procedural Depths expeditions with weekly modifiers</li>
<li>Artifact system — exotic gear with mutable perks</li>
<li>Guild halls and shared stash</li>
</ul>
<div class="card-foot">
<span class="foot-stat">Scope locked · design in review</span>
<a class="link" href="#">Vote on features →</a>
</div>
</div>
</div>
</article>
<!-- Milestone 5 -->
<article class="milestone" data-status="planned">
<div class="node node-final" aria-hidden="true"></div>
<div class="card">
<button class="card-head" type="button" aria-expanded="false">
<div class="card-meta">
<span class="pill pill-planned">Planned</span>
<span class="card-date">2027</span>
</div>
<h2 class="card-title">Version 1.0 · “Reign Eternal”</h2>
<p class="card-tag">Full launch — story finale, new game+, console release</p>
<span class="card-caret" aria-hidden="true">▾</span>
</button>
<div class="card-body">
<ul class="feature-list">
<li>Act III story conclusion + cinematic finale</li>
<li>New Game+ with remixed encounters</li>
<li>Console launch (cross-play enabled)</li>
<li>Full localization: 12 languages</li>
</ul>
<div class="card-foot">
<span class="foot-stat">Target window · subject to change</span>
<a class="link" href="#">Follow development →</a>
</div>
</div>
</div>
</article>
<p class="empty-note" id="emptyNote" hidden>No milestones match this filter.</p>
</section>
<!-- ===== Footer CTA ===== -->
<footer class="cta-band">
<div class="cta-copy">
<h2>Shape what ships next</h2>
<p>Roadmap priorities are voted on by the community every season.</p>
</div>
<div class="cta-actions">
<button class="btn btn-primary" id="ctaWishlist" type="button">+ Wishlist on Store</button>
<button class="btn btn-ghost" id="ctaDiscord" type="button">Join the Council</button>
</div>
</footer>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Game Roadmap / Early Access Timeline
An early-access development roadmap page in a sci-fi HUD style: Orbitron display type, clipped-corner panels, and neon cyan/violet accents over a deep dark background. The hero header shows a “Phase 2 of 5” meter with phase ticks and an animated fill, followed by a vertical timeline of five milestones — two released updates, the in-progress “Neon Drift” update flagged with a pulsing “You are here” marker, and two planned drops up to version 1.0.
Each milestone is an expandable card with a status pill (Released / In Progress / Planned), a date window, feature bullets with done/in-beta/planned glyphs, and — for the current update — its own animated progress bar. Filter chips narrow the timeline by status and show live counts, while a view toggle switches between the glowing timeline layout and a compact list layout.
The script handles card expansion with smooth grid-row transitions, status filtering with an empty-state message, the timeline/list toggle, and a progress spine that measures and animates its fill height to the current milestone node — re-measuring on resize and after card transitions. Wishlist and community CTAs confirm actions through a small toast helper.
Illustrative UI only — fictional games, studios, characters, and data. Not engine integrations.