Game — Features Showcase (mechanics scroll)
A scrolling game features showcase for a fictional co-op action RPG: alternating left/right mechanic blocks for combat, exploration, crafting, and co-op, each pairing CSS-drawn neon illustrations with headline copy and feature bullets. A sticky side rail tracks scroll progress and highlights the active section, while IntersectionObserver drives staggered reveals, animated stat bars, and count-up numbers in a stats band, all closing with glowing wishlist CTAs 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;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
overflow-x: hidden;
background: var(--bg);
color: var(--text);
font-family: var(--font-body);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a { color: inherit; }
/* ---------- Buttons ---------- */
.btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-family: var(--font-display);
font-weight: 700;
font-size: 0.85rem;
letter-spacing: 0.08em;
text-transform: uppercase;
text-decoration: none;
color: var(--text);
background: var(--panel-2);
border: 1px solid var(--line-2);
padding: 0.85rem 1.6rem;
cursor: pointer;
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, color 0.18s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn:active { transform: translateY(0); }
.btn:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 3px;
}
.btn-accent {
background: linear-gradient(120deg, var(--accent), var(--accent-2));
color: #051018;
border: none;
box-shadow: var(--glow);
}
.btn-accent:hover { box-shadow: 0 0 28px rgba(0, 229, 255, 0.65); }
.btn-ghost {
background: rgba(231, 233, 243, 0.04);
}
.btn-ghost:hover {
border-color: var(--accent);
color: var(--accent);
box-shadow: 0 0 14px rgba(0, 229, 255, 0.25);
}
.btn-sm { padding: 0.55rem 1.1rem; font-size: 0.72rem; }
.btn-lg { padding: 1.05rem 2.2rem; font-size: 0.95rem; }
.btn.is-added {
background: linear-gradient(120deg, var(--success), #1fae5d);
color: #04140a;
box-shadow: 0 0 18px rgba(54, 226, 122, 0.45);
}
/* ---------- Topbar ---------- */
.topbar {
position: fixed;
inset: 0 0 auto 0;
z-index: 50;
display: flex;
align-items: center;
gap: 2rem;
padding: 0.8rem 1.5rem;
background: rgba(10, 11, 16, 0.78);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--line);
}
.brand {
display: inline-flex;
align-items: center;
gap: 0.6rem;
text-decoration: none;
}
.brand:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.brand-mark {
width: 22px;
height: 22px;
background: conic-gradient(from 220deg, var(--accent), var(--accent-2), var(--accent-3), var(--accent));
clip-path: polygon(50% 0, 100% 38%, 81% 100%, 19% 100%, 0 38%);
box-shadow: var(--glow);
}
.brand-name {
font-family: var(--font-display);
font-weight: 900;
font-size: 0.95rem;
letter-spacing: 0.14em;
}
.brand-name em {
font-style: normal;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.topnav {
display: flex;
gap: 1.4rem;
margin-left: auto;
}
.topnav a {
font-size: 0.82rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
text-decoration: none;
color: var(--muted);
padding: 0.3rem 0.1rem;
border-bottom: 2px solid transparent;
transition: color 0.18s ease, border-color 0.18s ease;
}
.topnav a:hover { color: var(--text); }
.topnav a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.topnav a.is-active {
color: var(--accent);
border-bottom-color: var(--accent);
text-shadow: 0 0 12px rgba(0, 229, 255, 0.6);
}
/* ---------- Hero ---------- */
.hero {
position: relative;
min-height: 92vh;
display: grid;
place-items: center;
overflow: hidden;
padding: 7rem 1.5rem 4rem;
}
.hero-bg { position: absolute; inset: 0; pointer-events: none; }
.hero-grid {
position: absolute;
inset: 0;
background-image:
linear-gradient(rgba(0, 229, 255, 0.05) 1px, transparent 1px),
linear-gradient(90deg, rgba(0, 229, 255, 0.05) 1px, transparent 1px);
background-size: 56px 56px;
mask-image: radial-gradient(ellipse 80% 70% at 50% 40%, #000 30%, transparent 75%);
-webkit-mask-image: radial-gradient(ellipse 80% 70% at 50% 40%, #000 30%, transparent 75%);
}
.hero-orb {
position: absolute;
border-radius: 50%;
filter: blur(70px);
opacity: 0.35;
animation: orbDrift 14s ease-in-out infinite alternate;
}
.orb-a {
width: 420px; height: 420px;
background: var(--accent-2);
top: -8%; left: -6%;
}
.orb-b {
width: 360px; height: 360px;
background: var(--accent);
bottom: -12%; right: -4%;
animation-delay: -7s;
}
@keyframes orbDrift {
from { transform: translate(0, 0) scale(1); }
to { transform: translate(40px, 30px) scale(1.12); }
}
.hero-inner {
position: relative;
max-width: 860px;
text-align: center;
}
.kicker {
font-family: var(--font-display);
font-weight: 500;
font-size: 0.78rem;
letter-spacing: 0.34em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 1.2rem;
}
.hero-title {
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(2.1rem, 6vw, 4.2rem);
line-height: 1.08;
letter-spacing: 0.04em;
margin: 0 0 1.4rem;
}
.grad {
background: linear-gradient(95deg, var(--accent) 10%, var(--accent-2) 60%, var(--accent-3));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
filter: drop-shadow(0 0 22px rgba(0, 229, 255, 0.35));
}
.hero-sub {
color: var(--muted);
font-size: 1.05rem;
max-width: 620px;
margin: 0 auto 2.2rem;
}
.hero-actions {
display: flex;
flex-wrap: wrap;
gap: 1rem;
justify-content: center;
}
.scroll-hint {
width: 24px;
height: 40px;
margin: 3.5rem auto 0;
border: 2px solid var(--line-2);
border-radius: 14px;
position: relative;
}
.scroll-hint span {
position: absolute;
left: 50%;
top: 7px;
width: 4px;
height: 8px;
margin-left: -2px;
border-radius: 2px;
background: var(--accent);
box-shadow: var(--glow);
animation: scrollHint 1.8s ease-in-out infinite;
}
@keyframes scrollHint {
0%, 100% { transform: translateY(0); opacity: 1; }
60% { transform: translateY(12px); opacity: 0.2; }
}
/* ---------- Features shell + side rail ---------- */
.features-shell {
position: relative;
max-width: 1180px;
margin: 0 auto;
padding: 2rem 1.5rem 4rem;
display: grid;
grid-template-columns: 170px 1fr;
gap: 2rem;
}
.side-rail {
position: sticky;
top: 110px;
align-self: start;
display: flex;
gap: 0.9rem;
height: fit-content;
}
.rail-track {
width: 3px;
border-radius: 2px;
background: var(--line);
position: relative;
overflow: hidden;
}
.rail-fill {
position: absolute;
top: 0; left: 0; right: 0;
height: 0%;
background: linear-gradient(var(--accent), var(--accent-2));
box-shadow: var(--glow);
transition: height 0.25s linear;
}
.rail-list {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 1.6rem;
}
.rail-list a {
display: flex;
align-items: center;
gap: 0.6rem;
text-decoration: none;
color: var(--muted);
font-family: var(--font-display);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
transition: color 0.2s ease;
}
.rail-list a:hover { color: var(--text); }
.rail-list a:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.rail-dot {
width: 9px;
height: 9px;
border-radius: 50%;
border: 2px solid var(--line-2);
background: var(--bg-2);
transition: all 0.2s ease;
flex-shrink: 0;
}
.rail-list a.is-active { color: var(--accent); text-shadow: 0 0 10px rgba(0, 229, 255, 0.5); }
.rail-list a.is-active .rail-dot {
border-color: var(--accent);
background: var(--accent);
box-shadow: var(--glow);
animation: dotPulse 1.6s ease-in-out infinite;
}
@keyframes dotPulse {
0%, 100% { box-shadow: 0 0 8px rgba(0, 229, 255, 0.5); }
50% { box-shadow: 0 0 18px rgba(0, 229, 255, 0.9); }
}
/* ---------- Feature blocks ---------- */
.features {
display: flex;
flex-direction: column;
gap: 6rem;
min-width: 0;
}
.feature {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: center;
scroll-margin-top: 100px;
}
.feature-flip .feature-art { order: 2; }
.feature-flip .feature-copy { order: 1; }
.feature-index {
font-family: var(--font-display);
font-weight: 500;
font-size: 0.74rem;
letter-spacing: 0.3em;
text-transform: uppercase;
color: var(--accent-2);
margin: 0 0 0.7rem;
}
.feature-title {
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(1.4rem, 3vw, 2.1rem);
line-height: 1.15;
letter-spacing: 0.04em;
margin: 0 0 1.1rem;
}
.feature-copy p { color: var(--muted); margin: 0 0 1rem; }
.feature-points {
list-style: none;
margin: 1.4rem 0 0;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.7rem;
}
.feature-points li {
position: relative;
padding-left: 1.6rem;
font-weight: 600;
font-size: 0.92rem;
}
.feature-points li::before {
content: "";
position: absolute;
left: 0;
top: 0.42em;
width: 10px;
height: 10px;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
clip-path: polygon(0 0, 100% 50%, 0 100%);
filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.6));
}
/* ---------- CSS art panels ---------- */
.art {
position: relative;
aspect-ratio: 5 / 4;
background:
radial-gradient(120% 90% at 30% 20%, rgba(124, 77, 255, 0.14), transparent 55%),
linear-gradient(165deg, var(--panel-2), var(--panel) 60%, var(--bg-2));
border: 1px solid var(--line-2);
clip-path: polygon(22px 0, 100% 0, 100% calc(100% - 22px), calc(100% - 22px) 100%, 0 100%, 0 22px);
overflow: hidden;
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.45);
transition: box-shadow 0.3s ease, border-color 0.3s ease;
}
.feature:hover .art {
border-color: rgba(0, 229, 255, 0.4);
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.45), 0 0 30px rgba(0, 229, 255, 0.12);
}
.art-frame {
position: absolute;
inset: 12px;
border: 1px dashed var(--line);
clip-path: polygon(16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%, 0 16px);
pointer-events: none;
}
.hud-chip {
position: absolute;
font-family: var(--font-display);
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.18em;
color: var(--accent);
background: rgba(10, 11, 16, 0.8);
border: 1px solid rgba(0, 229, 255, 0.35);
padding: 0.35rem 0.7rem;
clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
z-index: 3;
}
.chip-tl { top: 22px; left: 22px; }
.chip-br { bottom: 22px; right: 22px; }
/* Combat art */
.combat-core {
position: absolute;
left: 50%; top: 50%;
width: 70px; height: 70px;
transform: translate(-50%, -50%) rotate(45deg);
background: linear-gradient(135deg, var(--accent), var(--accent-2));
box-shadow: 0 0 40px rgba(0, 229, 255, 0.55);
animation: coreSpin 8s linear infinite;
}
@keyframes coreSpin {
from { transform: translate(-50%, -50%) rotate(45deg); }
to { transform: translate(-50%, -50%) rotate(405deg); }
}
.combat-ring {
position: absolute;
left: 50%; top: 50%;
width: 160px; height: 160px;
transform: translate(-50%, -50%);
border: 2px solid rgba(0, 229, 255, 0.35);
border-top-color: var(--accent);
border-radius: 50%;
animation: ringSpin 4s linear infinite;
}
@keyframes ringSpin {
from { transform: translate(-50%, -50%) rotate(0deg); }
to { transform: translate(-50%, -50%) rotate(360deg); }
}
.slash {
position: absolute;
height: 3px;
background: linear-gradient(90deg, transparent, var(--accent-3), transparent);
filter: drop-shadow(0 0 8px rgba(255, 61, 113, 0.7));
animation: slashSweep 2.6s ease-in-out infinite;
}
.slash-1 { width: 60%; top: 30%; left: 12%; transform: rotate(-18deg); }
.slash-2 { width: 45%; top: 62%; left: 38%; transform: rotate(14deg); animation-delay: 0.5s; }
.slash-3 { width: 35%; top: 48%; left: 8%; transform: rotate(-32deg); animation-delay: 1.1s; }
@keyframes slashSweep {
0%, 100% { opacity: 0.25; }
50% { opacity: 1; }
}
/* Exploration art */
.isle {
position: absolute;
background: linear-gradient(180deg, var(--panel-2), var(--bg-2));
border: 1px solid var(--line-2);
border-top: 2px solid rgba(0, 229, 255, 0.55);
clip-path: polygon(0 0, 100% 0, 84% 100%, 16% 100%);
animation: isleBob 6s ease-in-out infinite;
}
.isle-1 { width: 32%; height: 17%; left: 8%; top: 28%; }
.isle-2 { width: 26%; height: 14%; left: 52%; top: 48%; animation-delay: -2s; }
.isle-3 { width: 22%; height: 12%; left: 26%; top: 68%; animation-delay: -4s; }
@keyframes isleBob {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-8px); }
}
.bridge {
position: absolute;
height: 2px;
background: linear-gradient(90deg, rgba(0, 229, 255, 0.1), var(--accent), rgba(0, 229, 255, 0.1));
filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.6));
}
.bridge-1 { width: 24%; left: 38%; top: 42%; transform: rotate(18deg); }
.bridge-2 { width: 22%; left: 36%; top: 64%; transform: rotate(-12deg); }
.ping {
position: absolute;
width: 12px; height: 12px;
border-radius: 50%;
background: var(--warn);
box-shadow: 0 0 12px rgba(255, 200, 87, 0.8);
}
.ping::after {
content: "";
position: absolute;
inset: -6px;
border: 2px solid var(--warn);
border-radius: 50%;
animation: pingWave 2s ease-out infinite;
}
.ping-1 { left: 20%; top: 32%; }
.ping-2 { left: 60%; top: 52%; }
.ping-2::after { animation-delay: 1s; }
@keyframes pingWave {
from { transform: scale(0.6); opacity: 1; }
to { transform: scale(2.4); opacity: 0; }
}
/* Crafting art */
.craft-slot {
position: absolute;
width: 26%;
aspect-ratio: 1;
display: grid;
place-items: center;
background: var(--bg-2);
border: 1px solid var(--line-2);
clip-path: polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px);
font-family: var(--font-display);
font-size: 0.62rem;
font-weight: 700;
letter-spacing: 0.14em;
color: var(--muted);
}
.slot-a { left: 10%; top: 22%; border-color: rgba(0, 229, 255, 0.45); color: var(--accent); }
.slot-b { right: 10%; top: 22%; border-color: rgba(124, 77, 255, 0.55); color: var(--accent-2); }
.craft-fuse {
position: absolute;
left: 38%; right: 38%;
top: calc(22% + 13%);
height: 3px;
background: linear-gradient(90deg, var(--accent), var(--warn), var(--accent-2));
filter: drop-shadow(0 0 8px rgba(255, 200, 87, 0.7));
animation: fusePulse 1.8s ease-in-out infinite;
}
@keyframes fusePulse {
0%, 100% { opacity: 0.5; transform: scaleX(0.92); }
50% { opacity: 1; transform: scaleX(1); }
}
.craft-result {
position: absolute;
left: 50%;
bottom: 26%;
transform: translateX(-50%);
padding: 0.7rem 1.4rem;
background: linear-gradient(120deg, rgba(255, 200, 87, 0.14), rgba(255, 61, 113, 0.12));
border: 1px solid var(--warn);
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
font-family: var(--font-display);
font-weight: 700;
font-size: 0.78rem;
letter-spacing: 0.16em;
color: var(--warn);
box-shadow: 0 0 24px rgba(255, 200, 87, 0.25);
animation: resultGlow 2.4s ease-in-out infinite;
}
@keyframes resultGlow {
0%, 100% { box-shadow: 0 0 14px rgba(255, 200, 87, 0.2); }
50% { box-shadow: 0 0 30px rgba(255, 200, 87, 0.45); }
}
/* Co-op art */
.coop-link {
position: absolute;
inset: 24%;
border: 2px dashed rgba(0, 229, 255, 0.3);
transform: rotate(45deg);
animation: ringSpin 18s linear infinite;
}
.sigil {
position: absolute;
width: 56px; height: 56px;
display: grid;
place-items: center;
font-family: var(--font-display);
font-weight: 900;
font-size: 0.8rem;
clip-path: polygon(50% 0, 100% 38%, 81% 100%, 19% 100%, 0 38%);
animation: sigilFloat 4s ease-in-out infinite;
}
.sigil-1 { left: calc(50% - 28px); top: 12%; background: linear-gradient(140deg, var(--accent), #0a6f7c); color: #04181c; }
.sigil-2 { right: 12%; top: calc(50% - 28px); background: linear-gradient(140deg, var(--accent-2), #3b2480); color: #efe9ff; animation-delay: -1s; }
.sigil-3 { left: calc(50% - 28px); bottom: 18%; background: linear-gradient(140deg, var(--accent-3), #8c1d3e); color: #ffe9ef; animation-delay: -2s; }
.sigil-4 { left: 12%; top: calc(50% - 28px); background: linear-gradient(140deg, var(--success), #15703c); color: #052e17; animation-delay: -3s; }
@keyframes sigilFloat {
0%, 100% { transform: translateY(0); filter: drop-shadow(0 0 6px rgba(0, 229, 255, 0.3)); }
50% { transform: translateY(-7px); filter: drop-shadow(0 0 14px rgba(0, 229, 255, 0.55)); }
}
.coop-banner {
position: absolute;
left: 50%; top: 50%;
transform: translate(-50%, -50%);
font-family: var(--font-display);
font-size: 0.66rem;
font-weight: 700;
letter-spacing: 0.2em;
color: var(--text);
background: rgba(10, 11, 16, 0.85);
border: 1px solid var(--line-2);
padding: 0.45rem 0.9rem;
white-space: nowrap;
}
/* ---------- Stat bars inside art ---------- */
.art-bar {
position: absolute;
left: 22px;
right: 22px;
bottom: 60px;
z-index: 3;
}
.stat-bar {
display: flex;
align-items: center;
gap: 0.7rem;
}
.stat-bar-label {
font-family: var(--font-display);
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--muted);
min-width: 52px;
}
.stat-bar-track {
flex: 1;
height: 8px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid var(--line);
border-radius: 4px;
overflow: hidden;
}
.stat-bar-fill {
display: block;
height: 100%;
width: 0;
border-radius: 4px;
background: linear-gradient(90deg, var(--accent), var(--accent-2));
box-shadow: 0 0 10px rgba(0, 229, 255, 0.6);
transition: width 1.2s cubic-bezier(0.22, 1, 0.36, 1);
}
.fill-danger { background: linear-gradient(90deg, var(--accent-3), var(--danger)); box-shadow: 0 0 10px rgba(255, 77, 77, 0.55); }
.fill-warn { background: linear-gradient(90deg, var(--warn), var(--accent-3)); box-shadow: 0 0 10px rgba(255, 200, 87, 0.55); }
/* ---------- Scroll reveals ---------- */
.reveal {
opacity: 0;
transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.22, 1, 0.36, 1);
will-change: opacity, transform;
}
.from-left { transform: translateX(-44px); }
.from-right { transform: translateX(44px); }
.from-bottom { transform: translateY(44px); }
.reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
.reveal { opacity: 1; transform: none; }
}
/* ---------- Stats band ---------- */
.stats-band {
max-width: 1180px;
margin: 2rem auto 0;
padding: 0 1.5rem;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.stat-cell {
background: linear-gradient(165deg, var(--panel-2), var(--panel));
border: 1px solid var(--line);
clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%, 0 14px);
padding: 1.6rem 1.2rem;
text-align: center;
display: flex;
flex-direction: column;
gap: 0.3rem;
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.stat-cell:hover {
border-color: rgba(0, 229, 255, 0.45);
box-shadow: 0 0 22px rgba(0, 229, 255, 0.14);
transform: translateY(-3px);
}
.stat-num {
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(1.7rem, 3.4vw, 2.5rem);
background: linear-gradient(95deg, var(--accent), var(--accent-2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.stat-label {
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--muted);
}
/* ---------- Closing ---------- */
.closing {
margin: 6rem auto 0;
padding: 5rem 1.5rem 4rem;
text-align: center;
background:
radial-gradient(60% 80% at 50% 0%, rgba(124, 77, 255, 0.16), transparent 70%),
linear-gradient(180deg, var(--bg), var(--bg-2));
border-top: 1px solid var(--line);
}
.closing-inner { max-width: 720px; margin: 0 auto; }
.closing-title {
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(1.8rem, 5vw, 3.2rem);
letter-spacing: 0.05em;
margin: 0 0 0.8rem;
}
.closing-sub { color: var(--muted); margin: 0 0 2rem; }
.fineprint {
margin-top: 3rem;
font-size: 0.74rem;
color: var(--muted);
opacity: 0.7;
}
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 16px);
z-index: 100;
background: var(--panel-2);
border: 1px solid var(--accent);
color: var(--text);
font-weight: 600;
font-size: 0.88rem;
padding: 0.8rem 1.4rem;
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
box-shadow: var(--glow);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.features-shell { grid-template-columns: 1fr; }
.side-rail {
position: sticky;
top: 58px;
z-index: 40;
background: rgba(10, 11, 16, 0.88);
backdrop-filter: blur(10px);
border: 1px solid var(--line);
padding: 0.6rem 0.9rem;
clip-path: polygon(10px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 10px);
}
.rail-track { display: none; }
.rail-list { flex-direction: row; gap: 1rem; flex-wrap: wrap; }
.feature, .feature-flip { grid-template-columns: 1fr; gap: 1.6rem; }
.feature-flip .feature-art { order: 0; }
.feature-flip .feature-copy { order: 1; }
.stats-band { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
.topnav { display: none; }
.topbar { gap: 1rem; }
.topbar .btn-sm { margin-left: auto; }
.hero { min-height: 80vh; padding-top: 6rem; }
.hero-sub { font-size: 0.95rem; }
.btn-lg { padding: 0.9rem 1.5rem; font-size: 0.82rem; }
.features { gap: 4rem; }
.rail-list a .rail-label { font-size: 0.6rem; }
.stats-band { grid-template-columns: 1fr 1fr; gap: 0.7rem; }
.stat-cell { padding: 1.1rem 0.7rem; }
.art-bar { bottom: 48px; left: 14px; right: 14px; }
.hud-chip { font-size: 0.52rem; }
.chip-tl { top: 14px; left: 14px; }
.chip-br { bottom: 14px; right: 14px; }
}(() => {
"use strict";
/* ---------- Toast helper ---------- */
const toastEl = document.getElementById("toast");
let toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toastEl.classList.remove("show"), 2600);
}
/* ---------- Smooth-scroll jump links ---------- */
document.querySelectorAll("[data-jump]").forEach((link) => {
link.addEventListener("click", (e) => {
const id = link.getAttribute("href");
const target = id && document.querySelector(id);
if (!target) return;
e.preventDefault();
target.scrollIntoView({ behavior: "smooth", block: "start" });
history.replaceState(null, "", id);
});
});
/* ---------- Scroll reveals ---------- */
const revealObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add("is-visible");
// Animate any stat-bar fills inside the revealed element
entry.target.querySelectorAll(".stat-bar-fill[data-fill]").forEach((bar) => {
requestAnimationFrame(() => {
bar.style.width = bar.dataset.fill + "%";
});
});
// Animate counters inside the revealed element
entry.target.querySelectorAll(".stat-num[data-count]").forEach(animateCount);
revealObserver.unobserve(entry.target);
});
},
{ threshold: 0.25, rootMargin: "0px 0px -8% 0px" }
);
document.querySelectorAll(".reveal").forEach((el) => revealObserver.observe(el));
/* ---------- Count-up stats ---------- */
function animateCount(el) {
if (el.dataset.done) return;
el.dataset.done = "1";
const target = parseInt(el.dataset.count, 10) || 0;
const suffix = el.dataset.suffix || "";
const duration = 1100;
const start = performance.now();
function tick(now) {
const p = Math.min((now - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased) + (p === 1 ? suffix : "");
if (p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
/* ---------- Active side-nav + topnav highlighting ---------- */
const sections = Array.from(document.querySelectorAll(".feature[data-section]"));
const railLinks = new Map(
Array.from(document.querySelectorAll("[data-rail]")).map((a) => [a.dataset.rail, a])
);
const topLinks = new Map(
Array.from(document.querySelectorAll(".topnav a")).map((a) => [
a.getAttribute("href").slice(1),
a,
])
);
function setActive(id) {
railLinks.forEach((a, key) => a.classList.toggle("is-active", key === id));
topLinks.forEach((a, key) => a.classList.toggle("is-active", key === id));
}
const sectionObserver = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) setActive(entry.target.dataset.section);
});
},
{ rootMargin: "-40% 0px -50% 0px", threshold: 0 }
);
sections.forEach((s) => sectionObserver.observe(s));
/* ---------- Rail progress fill ---------- */
const railFill = document.getElementById("railFill");
const featuresEl = document.getElementById("features");
function updateRail() {
const rect = featuresEl.getBoundingClientRect();
const viewH = window.innerHeight;
const total = rect.height - viewH * 0.4;
const scrolled = Math.min(Math.max(viewH * 0.6 - rect.top, 0), total);
railFill.style.height = (total > 0 ? (scrolled / total) * 100 : 0) + "%";
}
window.addEventListener("scroll", updateRail, { passive: true });
window.addEventListener("resize", updateRail);
updateRail();
/* ---------- Wishlist buttons ---------- */
function wireWishlist(btn) {
btn.addEventListener("click", () => {
const added = btn.classList.toggle("is-added");
btn.textContent = added ? "✓ Wishlisted" : btn.dataset.label;
toast(
added
? "Hollow Reign added to your wishlist."
: "Removed from wishlist."
);
});
}
["wishlistTop", "wishlistMain"].forEach((id) => {
const btn = document.getElementById(id);
btn.dataset.label = btn.textContent;
wireWishlist(btn);
});
/* ---------- Other CTAs ---------- */
document.getElementById("trailerBtn").addEventListener("click", () => {
toast("Trailer drops at the Nullforge showcase — stay tuned.");
});
document.getElementById("newsletterBtn").addEventListener("click", () => {
toast("Subscribed to Nullforge dev updates. First dispatch: Friday.");
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hollow Reign — Features</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>
<!-- Top bar -->
<header class="topbar">
<a class="brand" href="#top" data-jump>
<span class="brand-mark" aria-hidden="true"></span>
<span class="brand-name">HOLLOW <em>REIGN</em></span>
</a>
<nav class="topnav" aria-label="Page sections">
<a href="#combat" data-jump>Combat</a>
<a href="#exploration" data-jump>Exploration</a>
<a href="#crafting" data-jump>Crafting</a>
<a href="#coop" data-jump>Co-op</a>
</nav>
<button class="btn btn-accent btn-sm" id="wishlistTop" type="button">+ Wishlist</button>
</header>
<!-- Hero -->
<section class="hero" id="top">
<div class="hero-bg" aria-hidden="true">
<div class="hero-grid"></div>
<div class="hero-orb orb-a"></div>
<div class="hero-orb orb-b"></div>
</div>
<div class="hero-inner">
<p class="kicker">A Nullforge Production · Coming 2027</p>
<h1 class="hero-title">FOUR WAYS<br /><span class="grad">TO RECLAIM THE THRONE</span></h1>
<p class="hero-sub">
Hollow Reign is a co-op action RPG set in the shattered kingdom of Vel Adris.
Scroll to explore the four pillars that define every run — from blade-dance combat
to a fully shared four-player campaign.
</p>
<div class="hero-actions">
<a class="btn btn-accent" href="#combat" data-jump>Explore the Mechanics</a>
<button class="btn btn-ghost" id="trailerBtn" type="button">▶ Watch Trailer</button>
</div>
<div class="scroll-hint" aria-hidden="true"><span></span></div>
</div>
</section>
<!-- Features + sticky side nav -->
<div class="features-shell">
<aside class="side-rail" aria-label="Feature progress">
<div class="rail-track" aria-hidden="true"><div class="rail-fill" id="railFill"></div></div>
<ol class="rail-list">
<li><a href="#combat" data-jump data-rail="combat"><span class="rail-dot"></span><span class="rail-label">01 · Combat</span></a></li>
<li><a href="#exploration" data-jump data-rail="exploration"><span class="rail-dot"></span><span class="rail-label">02 · Exploration</span></a></li>
<li><a href="#crafting" data-jump data-rail="crafting"><span class="rail-dot"></span><span class="rail-label">03 · Crafting</span></a></li>
<li><a href="#coop" data-jump data-rail="coop"><span class="rail-dot"></span><span class="rail-label">04 · Co-op</span></a></li>
</ol>
</aside>
<main class="features" id="features">
<!-- 01 COMBAT -->
<section class="feature" id="combat" data-section="combat">
<div class="feature-art reveal from-left">
<div class="art art-combat" role="img" aria-label="Stylized illustration of a duelist mid blade-dance">
<div class="art-frame"></div>
<div class="slash slash-1"></div>
<div class="slash slash-2"></div>
<div class="slash slash-3"></div>
<div class="combat-core"></div>
<div class="combat-ring"></div>
<div class="hud-chip chip-tl">STANCE · TEMPEST</div>
<div class="hud-chip chip-br">COMBO ×14</div>
<div class="stat-bar art-bar">
<span class="stat-bar-label">Stagger</span>
<span class="stat-bar-track"><span class="stat-bar-fill fill-danger" data-fill="82"></span></span>
</div>
</div>
</div>
<div class="feature-copy reveal from-right">
<p class="feature-index">01 / Core Pillar</p>
<h2 class="feature-title">BLADE-DANCE<br />COMBAT</h2>
<p>
Chain stances mid-swing. Every weapon in Hollow Reign carries three stances —
Tempest, Bulwark, and Veil — and switching between them at the apex of a combo
triggers a stance echo that rewrites your next three attacks.
</p>
<p>
No cooldown bars. No ability bloat. Just timing, spacing, and a stagger system
that punishes button mashing and rewards reads.
</p>
<ul class="feature-points">
<li>3 stances per weapon, swappable mid-combo</li>
<li>Directional parries with posture-break finishers</li>
<li>Boss duels tuned for zero-damage mastery runs</li>
</ul>
</div>
</section>
<!-- 02 EXPLORATION -->
<section class="feature feature-flip" id="exploration" data-section="exploration">
<div class="feature-art reveal from-right">
<div class="art art-explore" role="img" aria-label="Stylized map of floating islands connected by light bridges">
<div class="art-frame"></div>
<div class="isle isle-1"></div>
<div class="isle isle-2"></div>
<div class="isle isle-3"></div>
<div class="bridge bridge-1"></div>
<div class="bridge bridge-2"></div>
<div class="ping ping-1"></div>
<div class="ping ping-2"></div>
<div class="hud-chip chip-tl">BIOME · ASHFEN MIRE</div>
<div class="hud-chip chip-br">3 SECRETS NEARBY</div>
</div>
</div>
<div class="feature-copy reveal from-left">
<p class="feature-index">02 / Core Pillar</p>
<h2 class="feature-title">A KINGDOM<br />WITHOUT LOADING WALLS</h2>
<p>
Vel Adris is one continuous map: four biomes stitched together by grapple lines,
ash storms, and collapsed leyways. If you can see a spire on the horizon,
you can climb it — and something is buried under it.
</p>
<p>
The Cartographer's Eye marks anomalies, not objectives. The map fills with what
you discover, never with what a quest log demands.
</p>
<ul class="feature-points">
<li>4 hand-built biomes, zero loading screens</li>
<li>Grapple, glide, and wall-run traversal kit</li>
<li>200+ optional vaults, shrines, and world bosses</li>
</ul>
</div>
</section>
<!-- 03 CRAFTING -->
<section class="feature" id="crafting" data-section="crafting">
<div class="feature-art reveal from-left">
<div class="art art-craft" role="img" aria-label="Stylized forge interface fusing two weapon cores">
<div class="art-frame"></div>
<div class="craft-slot slot-a"><span>CORE A</span></div>
<div class="craft-fuse"></div>
<div class="craft-slot slot-b"><span>CORE B</span></div>
<div class="craft-result"><span>EMBERFANG ★</span></div>
<div class="hud-chip chip-tl">NULLFORGE BENCH</div>
<div class="stat-bar art-bar">
<span class="stat-bar-label">Fusion</span>
<span class="stat-bar-track"><span class="stat-bar-fill fill-warn" data-fill="64"></span></span>
</div>
</div>
</div>
<div class="feature-copy reveal from-right">
<p class="feature-index">03 / Core Pillar</p>
<h2 class="feature-title">FORGE WEAPONS<br />THAT REMEMBER</h2>
<p>
Fuse any two weapon cores at a Nullforge bench to create a hybrid that inherits
a moveset from one parent and an elemental memory from the other. Kill a flame
revenant with it, and the blade keeps a spark of that fight forever.
</p>
<p>
Weapons level with deeds, not grind: each named kill etches a rune that
permanently alters the weapon's stance echoes.
</p>
<ul class="feature-points">
<li>60+ base weapons, thousands of fusion outcomes</li>
<li>Deed-runes earned from named enemies</li>
<li>No durability, no dismantle regret — refund any fusion</li>
</ul>
</div>
</section>
<!-- 04 CO-OP -->
<section class="feature feature-flip" id="coop" data-section="coop">
<div class="feature-art reveal from-right">
<div class="art art-coop" role="img" aria-label="Four player sigils linked around a shared banner">
<div class="art-frame"></div>
<div class="coop-link" aria-hidden="true"></div>
<div class="sigil sigil-1"><span>P1</span></div>
<div class="sigil sigil-2"><span>P2</span></div>
<div class="sigil sigil-3"><span>P3</span></div>
<div class="sigil sigil-4"><span>P4</span></div>
<div class="coop-banner">WARBAND · IRONVOW</div>
<div class="hud-chip chip-br">SHARED PROGRESSION ON</div>
</div>
</div>
<div class="feature-copy reveal from-left">
<p class="feature-index">04 / Core Pillar</p>
<h2 class="feature-title">FOUR CROWNS,<br />ONE CAMPAIGN</h2>
<p>
The entire campaign — every cutscene, every vault, every ending — supports
drop-in co-op for up to four players. Progression is shared both ways:
whatever your warband unlocks, you keep in your own world.
</p>
<p>
Warband revives cost banner charges, not respawns, and the final battle scales
its phases to your party — solo crowns are a different fight than four-player ones.
</p>
<ul class="feature-points">
<li>Full campaign drop-in / drop-out, 1–4 players</li>
<li>Cross-platform warbands with shared stash</li>
<li>Bidirectional progression — host nothing, lose nothing</li>
</ul>
</div>
</section>
</main>
</div>
<!-- Stats band -->
<section class="stats-band reveal from-bottom" aria-label="Game stats">
<div class="stat-cell">
<span class="stat-num" data-count="4">0</span>
<span class="stat-label">Biomes</span>
</div>
<div class="stat-cell">
<span class="stat-num" data-count="60" data-suffix="+">0</span>
<span class="stat-label">Weapons</span>
</div>
<div class="stat-cell">
<span class="stat-num" data-count="200" data-suffix="+">0</span>
<span class="stat-label">Optional vaults</span>
</div>
<div class="stat-cell">
<span class="stat-num" data-count="1">0</span>
<span class="stat-label">Loading screen — the first one</span>
</div>
</section>
<!-- Closing CTA -->
<section class="closing reveal from-bottom">
<div class="closing-inner">
<p class="kicker">Hollow Reign · PC / Console · 2027</p>
<h2 class="closing-title">THE THRONE IS <span class="grad">EMPTY</span>.</h2>
<p class="closing-sub">Wishlist now and get the Ashen Vanguard banner set at launch.</p>
<div class="hero-actions">
<button class="btn btn-accent btn-lg" id="wishlistMain" type="button">+ Add to Wishlist</button>
<button class="btn btn-ghost btn-lg" id="newsletterBtn" type="button">Get Dev Updates</button>
</div>
<p class="fineprint">© 2026 Nullforge Studios. Hollow Reign is a fictional game. ESRB rating pending.</p>
</div>
</section>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Features Showcase (mechanics scroll)
A long-scroll marketing page for the fictional co-op action RPG Hollow Reign (by studio Nullforge). Four alternating feature blocks present the game’s core pillars — blade-dance combat, open-world exploration, weapon-fusion crafting, and four-player co-op. Each block pairs an animated, pure-CSS illustration panel (spinning combat core, floating islands with ping markers, a fusion bench, orbiting player sigils) with a headline, copy, and clipped-corner feature bullets in the neon HUD style.
A sticky side rail mirrors the page structure: a glowing progress track fills as you scroll through the features, and IntersectionObserver highlights the dot and label of the section currently in view (the top nav follows suit). Blocks slide in from the left or right as they enter the viewport, stat bars inside the art panels fill on reveal, and the “4 biomes · 60+ weapons” stats band counts its numbers up with an eased animation.
All jump links — top nav, rail, and hero CTA — smooth-scroll to their sections. Wishlist buttons toggle between added and removed states with a success glow, and every CTA (trailer, newsletter) responds through a shared toast helper. The layout collapses gracefully to a single column with a horizontal sticky rail under 900px and stays usable down to 360px, with prefers-reduced-motion support throughout.
Illustrative UI only — fictional games, studios, characters, and data. Not engine integrations.