Game — Store Page (Steam-style: media · specs · buy)
A Steam-style game store page in a neon dark gaming UI: a media column with gradient screenshot viewer and keyboard-navigable thumbnail strip, a purchase column with capsule art, discount pricing, live deal countdown, add-to-cart and wishlist toggles with cart badge, an animated review sentiment bar, genre tag chips, an expandable about section, minimum and recommended system requirement tabs, and an editions and DLC rail — all vanilla HTML, CSS, and JS with 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.1);
--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; }
body {
margin: 0;
background:
radial-gradient(1100px 480px at 75% -10%, rgba(124, 77, 255, 0.16), transparent 60%),
radial-gradient(900px 420px at 8% 4%, rgba(0, 229, 255, 0.08), 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;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
button { font-family: inherit; cursor: pointer; }
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
border-radius: 3px;
}
/* ============ Nav ============ */
.store-nav {
position: sticky;
top: 0;
z-index: 40;
background: rgba(10, 11, 16, 0.86);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--line);
}
.nav-inner {
max-width: 1180px;
margin: 0 auto;
padding: 12px 20px;
display: flex;
align-items: center;
gap: 26px;
}
.brand {
font-family: var(--font-display);
font-weight: 900;
font-size: 15px;
letter-spacing: 0.18em;
color: var(--text);
display: flex;
align-items: center;
gap: 8px;
}
.brand:hover { text-decoration: none; }
.brand-mark {
width: 12px;
height: 12px;
background: linear-gradient(135deg, var(--accent), var(--accent-2));
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
box-shadow: var(--glow);
}
.brand-sub {
font-size: 9px;
color: var(--accent);
letter-spacing: 0.3em;
align-self: flex-end;
padding-bottom: 2px;
}
.nav-links { display: flex; gap: 4px; margin-right: auto; }
.nav-link {
color: var(--muted);
font-size: 13px;
font-weight: 600;
padding: 7px 12px;
border-radius: var(--r-sm);
transition: color 0.15s, background 0.15s;
}
.nav-link:hover { color: var(--text); background: var(--panel-2); text-decoration: none; }
.nav-link.is-active { color: var(--accent); }
.cart-btn {
display: inline-flex;
align-items: center;
gap: 8px;
background: var(--panel);
border: 1px solid var(--line-2);
color: var(--text);
font-size: 13px;
font-weight: 700;
padding: 8px 14px;
border-radius: var(--r-sm);
transition: border-color 0.15s, box-shadow 0.15s;
}
.cart-btn:hover { border-color: var(--accent); box-shadow: var(--glow); }
.cart-count {
min-width: 20px;
height: 20px;
display: inline-grid;
place-items: center;
padding: 0 5px;
background: var(--accent);
color: #04141a;
border-radius: 999px;
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
transition: transform 0.2s;
}
.cart-count.bump { animation: bump 0.35s ease; }
@keyframes bump {
0% { transform: scale(1); }
40% { transform: scale(1.5); }
100% { transform: scale(1); }
}
/* ============ Layout ============ */
.page {
max-width: 1180px;
margin: 0 auto;
padding: 22px 20px 56px;
}
.breadcrumbs {
display: flex;
gap: 8px;
font-size: 12.5px;
color: var(--muted);
margin-bottom: 14px;
}
.breadcrumbs a { color: var(--muted); }
.breadcrumbs a:hover { color: var(--accent); }
.breadcrumbs [aria-current] { color: var(--text); font-weight: 600; }
.game-title-bar {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 14px;
margin-bottom: 18px;
}
.game-title {
margin: 0;
font-family: var(--font-display);
font-weight: 900;
font-size: clamp(26px, 4.4vw, 42px);
letter-spacing: 0.08em;
background: linear-gradient(95deg, var(--text) 40%, var(--accent) 80%, var(--accent-2));
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 32px rgba(0, 229, 255, 0.25);
}
.title-badges { display: flex; gap: 8px; flex-wrap: wrap; }
.badge {
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.14em;
padding: 6px 11px;
border-radius: var(--r-sm);
clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
}
.badge-deal {
background: linear-gradient(120deg, var(--accent-3), var(--accent-2));
color: #fff;
animation: pulse-deal 2.4s ease-in-out infinite;
}
@keyframes pulse-deal {
0%, 100% { box-shadow: 0 0 0 rgba(255, 61, 113, 0); }
50% { box-shadow: 0 0 18px rgba(255, 61, 113, 0.55); }
}
.badge-new {
background: var(--panel-2);
color: var(--accent);
border: 1px solid var(--line-2);
}
/* ============ Hero grid ============ */
.hero-grid {
display: grid;
grid-template-columns: minmax(0, 1.55fr) minmax(300px, 1fr);
gap: 22px;
margin-bottom: 26px;
}
/* Media viewer */
.media-viewer { margin: 0; }
.media-frame {
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--r-lg);
border: 1px solid var(--line-2);
overflow: hidden;
display: grid;
place-items: center;
box-shadow: inset 0 0 80px rgba(0, 0, 0, 0.45), 0 18px 40px rgba(0, 0, 0, 0.45);
transition: background 0.3s;
}
.media-frame::after {
content: "";
position: absolute;
inset: 0;
background:
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.022) 0 1px, transparent 1px 3px),
radial-gradient(120% 90% at 50% 0%, transparent 55%, rgba(0, 0, 0, 0.5));
pointer-events: none;
}
.media-frame.is-switching { animation: media-in 0.32s ease; }
@keyframes media-in {
from { opacity: 0.25; transform: scale(1.015); }
to { opacity: 1; transform: scale(1); }
}
.media-kind {
position: absolute;
top: 14px;
left: 14px;
z-index: 2;
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.22em;
color: var(--accent);
background: rgba(10, 11, 16, 0.72);
border: 1px solid var(--line-2);
padding: 5px 10px;
clip-path: polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%);
}
.media-play {
position: relative;
z-index: 2;
width: 74px;
height: 74px;
border-radius: 50%;
border: 2px solid var(--accent);
background: rgba(10, 11, 16, 0.62);
color: var(--accent);
display: grid;
place-items: center;
transition: transform 0.2s, box-shadow 0.2s, background 0.2s;
}
.media-play:hover {
transform: scale(1.08);
box-shadow: var(--glow);
background: rgba(0, 229, 255, 0.12);
}
.media-play[hidden] { display: none; }
.media-caption {
position: absolute;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
padding: 26px 16px 12px;
font-size: 13px;
font-weight: 600;
color: var(--text);
background: linear-gradient(transparent, rgba(8, 9, 14, 0.88));
}
/* gradient "screenshots" */
.media-grad-1 { background: linear-gradient(135deg, #131a33 0%, #2b1c52 45%, #07242e 100%); }
.media-grad-2 { background: linear-gradient(150deg, #2a1430 0%, #511f3f 50%, #1a0f24 100%); }
.media-grad-3 { background: linear-gradient(135deg, #2e0e16 0%, #6d1f33 55%, #160a14 100%); }
.media-grad-4 { background: linear-gradient(160deg, #0d2b29 0%, #14524a 55%, #081a20 100%); }
.media-grad-5 { background: linear-gradient(140deg, #101637 0%, #243a8a 55%, #0a0e23 100%); }
.thumb-strip {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin-top: 12px;
}
.thumb {
position: relative;
aspect-ratio: 16 / 9;
border-radius: var(--r-sm);
border: 2px solid var(--line);
overflow: hidden;
color: var(--text);
display: grid;
place-items: center;
opacity: 0.62;
transition: opacity 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.thumb:hover { opacity: 1; transform: translateY(-2px); }
.thumb.is-active {
opacity: 1;
border-color: var(--accent);
box-shadow: var(--glow);
}
.thumb-tag {
position: absolute;
top: 5px;
left: 6px;
font-size: 9px;
color: var(--accent);
}
.thumb-label {
font-family: var(--font-display);
font-size: 10px;
font-weight: 700;
letter-spacing: 0.12em;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.8);
}
/* ============ Buy column ============ */
.buy-col {
background: linear-gradient(175deg, var(--panel) 0%, var(--bg-2) 100%);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 18px;
align-self: start;
position: relative;
}
.buy-col::before {
content: "";
position: absolute;
inset: 0 0 auto 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--accent), var(--accent-2), transparent);
border-radius: var(--r-lg) var(--r-lg) 0 0;
opacity: 0.8;
}
.capsule {
aspect-ratio: 16 / 7;
border-radius: var(--r-md);
border: 1px solid var(--line-2);
display: grid;
place-items: center;
margin-bottom: 14px;
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.4);
}
.capsule-logo {
font-family: var(--font-display);
font-weight: 900;
font-size: 24px;
letter-spacing: 0.22em;
text-align: center;
line-height: 1.25;
text-shadow: 0 0 24px rgba(0, 229, 255, 0.5);
}
.short-desc {
font-size: 13.5px;
color: var(--muted);
margin: 0 0 14px;
}
.meta-rows { margin: 0 0 12px; }
.meta-row {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 6px 0;
border-bottom: 1px solid var(--line);
font-size: 13px;
}
.meta-row dt { color: var(--muted); font-weight: 500; }
.meta-row dd { margin: 0; font-weight: 600; text-align: right; }
.review-link {
background: none;
border: none;
padding: 0;
color: var(--success);
font: inherit;
font-weight: 700;
}
.review-link:hover { text-decoration: underline; }
.muted { color: var(--muted); font-weight: 500; }
.review-bar-wrap { margin-bottom: 14px; }
.review-bar {
height: 8px;
border-radius: 999px;
background: rgba(255, 77, 77, 0.35);
overflow: hidden;
border: 1px solid var(--line);
}
.review-fill {
display: block;
height: 100%;
width: 0;
background: linear-gradient(90deg, var(--success), #8ff5b6);
border-radius: 999px;
box-shadow: 0 0 10px rgba(54, 226, 122, 0.55);
transition: width 1.1s cubic-bezier(0.2, 0.7, 0.2, 1);
}
.review-fill.is-filled { width: var(--pct); }
.review-bar-legend {
display: flex;
justify-content: space-between;
font-size: 11px;
margin-top: 5px;
}
.review-bar-legend .pos { color: var(--success); font-weight: 700; }
.review-bar-legend .neg { color: var(--danger); }
.tag-chips {
display: flex;
flex-wrap: wrap;
gap: 7px;
margin-bottom: 16px;
}
.chip {
background: var(--panel-2);
border: 1px solid var(--line);
color: var(--muted);
font-size: 11.5px;
font-weight: 600;
padding: 5px 11px;
border-radius: 999px;
transition: color 0.15s, border-color 0.15s, box-shadow 0.15s;
}
.chip:hover { color: var(--accent); border-color: var(--accent); box-shadow: 0 0 10px rgba(0, 229, 255, 0.25); }
.chip-more { color: var(--accent); border-style: dashed; }
.price-block {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 14px;
background: rgba(0, 229, 255, 0.05);
border: 1px solid var(--line-2);
border-radius: var(--r-md);
margin-bottom: 6px;
}
.discount-pct {
font-family: var(--font-display);
font-weight: 900;
font-size: 18px;
color: #06140a;
background: var(--success);
padding: 6px 9px;
clip-path: polygon(7px 0, 100% 0, calc(100% - 7px) 100%, 0 100%);
}
.price-old {
color: var(--muted);
text-decoration: line-through;
font-size: 14px;
}
.price-now {
margin-left: auto;
font-family: var(--font-display);
font-weight: 900;
font-size: 26px;
color: var(--accent);
text-shadow: 0 0 18px rgba(0, 229, 255, 0.4);
}
.deal-timer {
font-size: 12px;
color: var(--warn);
margin: 0 0 14px;
}
.deal-timer strong { font-family: var(--font-display); letter-spacing: 0.08em; }
.cta-row {
display: grid;
grid-template-columns: 1.4fr 1fr;
gap: 10px;
margin-bottom: 16px;
}
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: var(--font-display);
font-weight: 700;
font-size: 13px;
letter-spacing: 0.1em;
padding: 14px 12px;
border-radius: var(--r-sm);
clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
border: none;
transition: transform 0.12s, box-shadow 0.2s, filter 0.2s;
}
.btn:active { transform: scale(0.97); }
.btn-buy {
background: linear-gradient(110deg, var(--accent), #19b9d8 60%, var(--accent-2));
color: #04141a;
}
.btn-buy:hover { box-shadow: var(--glow); filter: brightness(1.1); }
.btn-buy.in-cart {
background: linear-gradient(110deg, var(--success), #1eb45c);
color: #06140a;
box-shadow: 0 0 14px rgba(54, 226, 122, 0.45);
}
.btn-wish {
background: var(--panel-2);
color: var(--text);
border: 1px solid var(--line-2);
}
.btn-wish:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 12px rgba(0, 229, 255, 0.3); }
.btn-wish[aria-pressed="true"] {
color: var(--accent-3);
border-color: var(--accent-3);
box-shadow: 0 0 12px rgba(255, 61, 113, 0.4);
}
.btn-wish[aria-pressed="true"] svg { fill: var(--accent-3); animation: heart-pop 0.35s ease; }
@keyframes heart-pop {
0% { transform: scale(1); }
45% { transform: scale(1.45); }
100% { transform: scale(1); }
}
.platform-row {
display: flex;
flex-wrap: wrap;
gap: 7px;
}
.plat {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11.5px;
font-weight: 600;
color: var(--muted);
background: var(--bg-2);
border: 1px solid var(--line);
padding: 5px 10px;
border-radius: var(--r-sm);
}
.plat svg { color: var(--accent); }
/* ============ Lower grid ============ */
.lower-grid {
display: grid;
grid-template-columns: minmax(0, 1.55fr) minmax(300px, 1fr);
gap: 22px;
}
.lower-main { display: grid; gap: 22px; align-content: start; }
.lower-side { display: grid; gap: 22px; align-content: start; }
.panel {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 20px;
}
.panel-heading {
margin: 0 0 14px;
font-family: var(--font-display);
font-size: 14px;
font-weight: 700;
letter-spacing: 0.18em;
color: var(--text);
padding-left: 12px;
border-left: 3px solid var(--accent);
}
.panel-head-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 6px;
}
.panel-head-row .panel-heading { margin-bottom: 0; }
/* About */
.about-body {
font-size: 14px;
color: var(--muted);
max-height: 230px;
overflow: hidden;
position: relative;
transition: max-height 0.4s ease;
}
.about-body::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: 0;
height: 70px;
background: linear-gradient(transparent, var(--panel));
transition: opacity 0.3s;
}
.about-body.is-open { max-height: 1200px; }
.about-body.is-open::after { opacity: 0; }
.about-body p { margin: 0 0 12px; }
.about-body strong, .about-body em { color: var(--text); }
.feature-list {
list-style: none;
padding: 0;
margin: 16px 0 0;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
}
.feature-list li {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 10px 12px;
font-size: 12.5px;
}
.feat-key {
display: block;
font-family: var(--font-display);
font-size: 12px;
font-weight: 700;
color: var(--accent);
letter-spacing: 0.06em;
margin-bottom: 2px;
}
.about-toggle {
margin-top: 12px;
background: none;
border: 1px solid var(--line-2);
color: var(--accent);
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.16em;
padding: 9px 18px;
border-radius: var(--r-sm);
clip-path: polygon(8px 0, 100% 0, calc(100% - 8px) 100%, 0 100%);
transition: box-shadow 0.15s, border-color 0.15s;
}
.about-toggle:hover { border-color: var(--accent); box-shadow: var(--glow); }
/* System requirements */
.seg-tabs {
display: inline-flex;
background: var(--bg-2);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 3px;
gap: 3px;
}
.seg-tab {
background: none;
border: none;
color: var(--muted);
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.12em;
padding: 8px 16px;
border-radius: 4px;
transition: color 0.15s, background 0.15s, box-shadow 0.15s;
}
.seg-tab:hover { color: var(--text); }
.seg-tab.is-active {
background: linear-gradient(110deg, rgba(0, 229, 255, 0.18), rgba(124, 77, 255, 0.18));
color: var(--accent);
box-shadow: inset 0 0 0 1px rgba(0, 229, 255, 0.45), 0 0 10px rgba(0, 229, 255, 0.2);
}
.req-grid {
margin-top: 14px;
display: grid;
gap: 8px;
animation: req-in 0.3s ease;
}
@keyframes req-in {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
.req-row {
display: grid;
grid-template-columns: 96px 1fr;
gap: 14px;
align-items: center;
background: var(--bg-2);
border: 1px solid var(--line);
border-left: 3px solid var(--accent-2);
border-radius: var(--r-sm);
padding: 10px 14px;
}
.req-key {
font-family: var(--font-display);
font-size: 11px;
font-weight: 700;
letter-spacing: 0.14em;
color: var(--muted);
}
.req-val { font-size: 13px; font-weight: 600; }
/* Editions */
.edition-card {
background: var(--bg-2);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 14px;
margin-bottom: 12px;
transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}
.edition-card:hover {
border-color: var(--line-2);
transform: translateY(-2px);
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.edition-card:last-child { margin-bottom: 0; }
.edition-featured {
border-color: rgba(124, 77, 255, 0.55);
box-shadow: 0 0 18px rgba(124, 77, 255, 0.22);
background: linear-gradient(160deg, rgba(124, 77, 255, 0.1), var(--bg-2) 55%);
}
.edition-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.edition-name { margin: 0; font-size: 14px; font-weight: 800; }
.edition-tag {
font-family: var(--font-display);
font-size: 9px;
font-weight: 700;
letter-spacing: 0.16em;
color: var(--muted);
border: 1px solid var(--line-2);
padding: 3px 8px;
clip-path: polygon(5px 0, 100% 0, calc(100% - 5px) 100%, 0 100%);
white-space: nowrap;
}
.tag-best { color: #14081f; background: linear-gradient(110deg, var(--warn), #ffe29a); border: none; }
.tag-dlc { color: var(--accent-2); border-color: rgba(124, 77, 255, 0.5); }
.edition-desc { font-size: 12.5px; color: var(--muted); margin: 8px 0 12px; }
.edition-buy {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.edition-price { font-family: var(--font-display); font-size: 14px; font-weight: 700; color: var(--accent); }
.edition-price s { color: var(--muted); font-size: 11px; font-weight: 500; margin-right: 6px; }
.btn-mini {
background: var(--panel-2);
border: 1px solid var(--line-2);
color: var(--text);
font-family: var(--font-display);
font-size: 10.5px;
font-weight: 700;
letter-spacing: 0.14em;
padding: 8px 16px;
border-radius: var(--r-sm);
clip-path: polygon(7px 0, 100% 0, calc(100% - 7px) 100%, 0 100%);
transition: border-color 0.15s, box-shadow 0.15s, color 0.15s, background 0.15s;
}
.btn-mini:hover { border-color: var(--accent); color: var(--accent); box-shadow: 0 0 12px rgba(0, 229, 255, 0.3); }
.btn-mini.is-added {
background: rgba(54, 226, 122, 0.15);
border-color: var(--success);
color: var(--success);
}
/* Awards */
.quote {
margin: 0 0 14px;
padding: 12px 14px;
font-size: 13.5px;
font-style: italic;
color: var(--text);
background: var(--bg-2);
border-left: 3px solid var(--warn);
border-radius: var(--r-sm);
}
.quote:last-child { margin-bottom: 0; }
.quote cite {
display: block;
margin-top: 6px;
font-style: normal;
font-size: 12px;
color: var(--muted);
}
.legal {
margin-top: 28px;
font-size: 11.5px;
color: var(--muted);
text-align: center;
opacity: 0.75;
}
/* ============ Toast ============ */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 16px);
background: var(--panel-2);
border: 1px solid var(--accent);
color: var(--text);
font-size: 13px;
font-weight: 600;
padding: 12px 22px;
border-radius: var(--r-sm);
clip-path: polygon(10px 0, 100% 0, calc(100% - 10px) 100%, 0 100%);
box-shadow: var(--glow);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 60;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ============ Responsive ============ */
@media (max-width: 900px) {
.hero-grid, .lower-grid { grid-template-columns: 1fr; }
.nav-links { display: none; }
}
@media (max-width: 520px) {
.page { padding: 16px 14px 44px; }
.nav-inner { padding: 10px 14px; gap: 14px; }
.game-title-bar { gap: 10px; }
.thumb-strip { grid-template-columns: repeat(5, 1fr); gap: 6px; }
.thumb-label { font-size: 8px; }
.cta-row { grid-template-columns: 1fr; }
.panel { padding: 16px 14px; }
.panel-head-row { flex-direction: column; align-items: flex-start; }
.feature-list { grid-template-columns: 1fr; }
.req-row { grid-template-columns: 76px 1fr; gap: 10px; padding: 9px 11px; }
.price-now { font-size: 22px; }
.media-play { width: 58px; height: 58px; }
}/* Hollow Reign — store page interactions (vanilla JS, illustrative only) */
(() => {
"use strict";
const $ = (sel, root = document) => root.querySelector(sel);
const $$ = (sel, root = document) => Array.from(root.querySelectorAll(sel));
/* ---------- Toast helper ---------- */
const toastEl = $("#toast");
let toastTimer = null;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(() => toastEl.classList.remove("show"), 2400);
}
/* ---------- Media viewer: thumbnail switching ---------- */
const frame = $("#mediaFrame");
const kindEl = $("#mediaKind");
const captionEl = $("#mediaCaption");
const playBtn = $("#mediaPlay");
const thumbs = $$(".thumb");
const GRADS = ["media-grad-1", "media-grad-2", "media-grad-3", "media-grad-4", "media-grad-5"];
function selectMedia(thumb) {
thumbs.forEach((t) => {
const active = t === thumb;
t.classList.toggle("is-active", active);
t.setAttribute("aria-selected", String(active));
});
frame.classList.remove(...GRADS);
frame.classList.add(thumb.dataset.grad);
kindEl.textContent = thumb.dataset.kind;
captionEl.textContent = thumb.dataset.caption;
playBtn.hidden = thumb.dataset.video !== "true";
frame.setAttribute("aria-label", "Hollow Reign media — " + thumb.dataset.caption);
// retrigger switch animation
frame.classList.remove("is-switching");
void frame.offsetWidth;
frame.classList.add("is-switching");
}
thumbs.forEach((thumb, i) => {
thumb.addEventListener("click", () => selectMedia(thumb));
thumb.addEventListener("keydown", (e) => {
let next = null;
if (e.key === "ArrowRight") next = thumbs[(i + 1) % thumbs.length];
if (e.key === "ArrowLeft") next = thumbs[(i - 1 + thumbs.length) % thumbs.length];
if (next) {
e.preventDefault();
next.focus();
selectMedia(next);
}
});
});
playBtn.addEventListener("click", () => {
toast("Trailer playback is illustrative in this demo");
});
/* ---------- Cart ---------- */
const cartCount = $("#cartCount");
const navCartBtn = $("#navCartBtn");
let cart = 0;
function bumpCart(delta) {
cart = Math.max(0, cart + delta);
cartCount.textContent = String(cart);
navCartBtn.setAttribute("aria-label", "Cart, " + cart + " item" + (cart === 1 ? "" : "s"));
cartCount.classList.remove("bump");
void cartCount.offsetWidth;
cartCount.classList.add("bump");
}
navCartBtn.addEventListener("click", () => {
toast(cart === 0 ? "Your cart is empty" : "Cart: " + cart + " item" + (cart === 1 ? "" : "s") + " — checkout is illustrative");
});
/* Main Add to Cart toggle */
const addCartBtn = $("#addCartBtn");
let mainInCart = false;
addCartBtn.addEventListener("click", () => {
mainInCart = !mainInCart;
addCartBtn.classList.toggle("in-cart", mainInCart);
addCartBtn.querySelector(".btn-label").textContent = mainInCart ? "✓ IN CART" : "ADD TO CART";
bumpCart(mainInCart ? 1 : -1);
toast(mainInCart ? "Hollow Reign added to cart — $25.99" : "Hollow Reign removed from cart");
});
/* Wishlist toggle */
const wishlistBtn = $("#wishlistBtn");
wishlistBtn.addEventListener("click", () => {
const wished = wishlistBtn.getAttribute("aria-pressed") !== "true";
wishlistBtn.setAttribute("aria-pressed", String(wished));
wishlistBtn.querySelector(".btn-label").textContent = wished ? "WISHLISTED" : "WISHLIST";
toast(wished ? "Added to your wishlist — we'll ping you on deals" : "Removed from wishlist");
});
/* Edition / DLC add buttons */
$$(".btn-mini").forEach((btn) => {
btn.addEventListener("click", () => {
const added = !btn.classList.contains("is-added");
btn.classList.toggle("is-added", added);
btn.textContent = added ? "✓ ADDED" : "ADD";
bumpCart(added ? 1 : -1);
toast(
added
? btn.dataset.edition + " added — $" + btn.dataset.price
: btn.dataset.edition + " removed from cart"
);
});
});
/* ---------- Reviews sentiment bar (animate on load + scroll-to on click) ---------- */
const reviewFill = $("#reviewFill");
requestAnimationFrame(() => {
setTimeout(() => reviewFill.classList.add("is-filled"), 250);
});
$("#reviewLink").addEventListener("click", () => {
const wrap = $("#reviewBarWrap");
wrap.scrollIntoView({ behavior: "smooth", block: "center" });
reviewFill.classList.remove("is-filled");
void reviewFill.offsetWidth;
setTimeout(() => reviewFill.classList.add("is-filled"), 120);
toast("48,213 reviews — 91% positive (last 30 days: Very Positive)");
});
/* ---------- System requirements: Min / Recommended tabs ---------- */
const tabMin = $("#tabMin");
const tabRec = $("#tabRec");
const reqMin = $("#reqMin");
const reqRec = $("#reqRec");
function showReq(which) {
const min = which === "min";
tabMin.classList.toggle("is-active", min);
tabRec.classList.toggle("is-active", !min);
tabMin.setAttribute("aria-selected", String(min));
tabRec.setAttribute("aria-selected", String(!min));
reqMin.hidden = !min;
reqRec.hidden = min;
}
tabMin.addEventListener("click", () => showReq("min"));
tabRec.addEventListener("click", () => showReq("rec"));
[tabMin, tabRec].forEach((tab) => {
tab.addEventListener("keydown", (e) => {
if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
e.preventDefault();
const other = tab === tabMin ? tabRec : tabMin;
other.focus();
showReq(other === tabMin ? "min" : "rec");
}
});
});
/* ---------- About: read more ---------- */
const aboutBody = $("#aboutBody");
const aboutToggle = $("#aboutToggle");
aboutToggle.addEventListener("click", () => {
const open = !aboutBody.classList.contains("is-open");
aboutBody.classList.toggle("is-open", open);
aboutToggle.setAttribute("aria-expanded", String(open));
aboutToggle.textContent = open ? "SHOW LESS" : "READ MORE";
});
/* ---------- Tag chips ---------- */
const moreTags = $("#moreTags");
moreTags.addEventListener("click", () => {
$$(".chip-hidden").forEach((c) => (c.hidden = false));
moreTags.setAttribute("aria-expanded", "true");
moreTags.hidden = true;
});
document.querySelector(".tag-chips").addEventListener("click", (e) => {
const chip = e.target.closest(".chip");
if (chip && chip !== moreTags) toast('Browsing tag "' + chip.textContent.trim() + '" — illustrative');
});
/* ---------- Deal countdown ---------- */
const dealTimer = $("#dealTimer");
let remaining = 47 * 3600 + 59 * 60 + 12; // seconds
const pad = (n) => String(n).padStart(2, "0");
setInterval(() => {
remaining = Math.max(0, remaining - 1);
const h = Math.floor(remaining / 3600);
const m = Math.floor((remaining % 3600) / 60);
const s = remaining % 60;
dealTimer.textContent = pad(h) + ":" + pad(m) + ":" + pad(s);
}, 1000);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hollow Reign — Store Page</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>
<header class="store-nav" aria-label="Store navigation">
<div class="nav-inner">
<a class="brand" href="#"><span class="brand-mark" aria-hidden="true"></span>VOIDFRONT<span class="brand-sub">STORE</span></a>
<nav class="nav-links" aria-label="Store sections">
<a href="#" class="nav-link is-active">Discover</a>
<a href="#" class="nav-link">Library</a>
<a href="#" class="nav-link">News</a>
<a href="#" class="nav-link">Community</a>
</nav>
<button class="cart-btn" id="navCartBtn" aria-label="Cart, 0 items">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="9" cy="20" r="1.6"/><circle cx="18" cy="20" r="1.6"/><path d="M2 3h3l2.6 12.5a1 1 0 0 0 1 .8h9.7a1 1 0 0 0 1-.8L21 8H6"/></svg>
Cart <span class="cart-count" id="cartCount">0</span>
</button>
</div>
</header>
<main class="page">
<!-- ===================== Title bar ===================== -->
<div class="breadcrumbs" aria-label="Breadcrumb">
<a href="#">All Games</a><span aria-hidden="true">/</span>
<a href="#">Action RPG</a><span aria-hidden="true">/</span>
<span aria-current="page">Hollow Reign</span>
</div>
<header class="game-title-bar">
<h1 class="game-title">HOLLOW REIGN</h1>
<div class="title-badges">
<span class="badge badge-deal">FORGE WEEK DEAL</span>
<span class="badge badge-new">v1.4 — Crown of Ash Update</span>
</div>
</header>
<!-- ===================== Hero: media + purchase ===================== -->
<section class="hero-grid" aria-label="Game media and purchase">
<!-- Media column -->
<div class="media-col">
<figure class="media-viewer" id="mediaViewer">
<div class="media-frame media-grad-1" id="mediaFrame" role="img" aria-label="Hollow Reign screenshot — The Sundered Keep at dusk">
<span class="media-kind" id="mediaKind">TRAILER</span>
<button class="media-play" id="mediaPlay" aria-label="Play trailer">
<svg viewBox="0 0 24 24" width="30" height="30" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg>
</button>
<figcaption class="media-caption" id="mediaCaption">Official Trailer — Crown of Ash</figcaption>
</div>
</figure>
<div class="thumb-strip" role="tablist" aria-label="Media gallery">
<button class="thumb media-grad-1 is-active" role="tab" aria-selected="true" data-kind="TRAILER" data-grad="media-grad-1" data-caption="Official Trailer — Crown of Ash" data-video="true">
<span class="thumb-tag">▶</span><span class="thumb-label">Trailer</span>
</button>
<button class="thumb media-grad-2" role="tab" aria-selected="false" data-kind="SCREENSHOT" data-grad="media-grad-2" data-caption="The Sundered Keep — exterior siege at dusk">
<span class="thumb-label">Keep</span>
</button>
<button class="thumb media-grad-3" role="tab" aria-selected="false" data-kind="SCREENSHOT" data-grad="media-grad-3" data-caption="Boss fight — Vespera, Last Magistrate">
<span class="thumb-label">Boss</span>
</button>
<button class="thumb media-grad-4" role="tab" aria-selected="false" data-kind="SCREENSHOT" data-grad="media-grad-4" data-caption="Ashfall Marsh — co-op exploration">
<span class="thumb-label">Marsh</span>
</button>
<button class="thumb media-grad-5" role="tab" aria-selected="false" data-kind="SCREENSHOT" data-grad="media-grad-5" data-caption="Skill web — Umbral branch fully unlocked">
<span class="thumb-label">Skills</span>
</button>
</div>
</div>
<!-- Purchase column -->
<aside class="buy-col" aria-label="Purchase Hollow Reign">
<div class="capsule media-grad-1" role="img" aria-label="Hollow Reign capsule art">
<span class="capsule-logo">HOLLOW<br/>REIGN</span>
</div>
<p class="short-desc">
A dark-fantasy action RPG from Nullforge. Reclaim a kingdom devoured by the
Hollow — alone or in 3-player co-op — with a class-free skill web, brutal
stagger combat, and a world that remembers every choice.
</p>
<dl class="meta-rows">
<div class="meta-row"><dt>Reviews</dt><dd><button class="review-link" id="reviewLink">Very Positive <span class="muted">(48,213)</span></button></dd></div>
<div class="meta-row"><dt>Release</dt><dd>March 14, 2026</dd></div>
<div class="meta-row"><dt>Developer</dt><dd><a href="#">Nullforge</a></dd></div>
<div class="meta-row"><dt>Publisher</dt><dd><a href="#">Voidfront</a></dd></div>
</dl>
<div class="review-bar-wrap" id="reviewBarWrap" aria-label="Review sentiment: 91 percent positive">
<div class="review-bar"><span class="review-fill" id="reviewFill" style="--pct:91%"></span></div>
<div class="review-bar-legend"><span class="pos">91% positive</span><span class="neg">9% negative</span></div>
</div>
<div class="tag-chips" aria-label="Genre tags">
<button class="chip">Action RPG</button>
<button class="chip">Souls-like</button>
<button class="chip">Co-op</button>
<button class="chip">Dark Fantasy</button>
<button class="chip">Open World</button>
<button class="chip chip-more" id="moreTags" aria-expanded="false">+4 more</button>
<button class="chip chip-hidden" hidden>Atmospheric</button>
<button class="chip chip-hidden" hidden>Difficult</button>
<button class="chip chip-hidden" hidden>Lore-Rich</button>
<button class="chip chip-hidden" hidden>Controller</button>
</div>
<div class="price-block">
<span class="discount-pct">−35%</span>
<span class="price-old" aria-label="Original price $39.99">$39.99</span>
<span class="price-now">$25.99</span>
</div>
<p class="deal-timer">Deal ends in <strong id="dealTimer">47:59:12</strong></p>
<div class="cta-row">
<button class="btn btn-buy" id="addCartBtn">
<span class="btn-label">ADD TO CART</span>
</button>
<button class="btn btn-wish" id="wishlistBtn" aria-pressed="false">
<svg viewBox="0 0 24 24" width="17" height="17" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M19.5 12.6 12 20l-7.5-7.4A5 5 0 1 1 12 6.3a5 5 0 1 1 7.5 6.3Z"/></svg>
<span class="btn-label">WISHLIST</span>
</button>
</div>
<div class="platform-row" aria-label="Available platforms">
<span class="plat" title="Windows"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M3 5.5 10.5 4.4v7.1H3V5.5ZM3 12.5h7.5v7.1L3 18.5v-6ZM11.5 4.2 21 3v8.5h-9.5V4.2ZM11.5 12.5H21V21l-9.5-1.3v-7.2Z"/></svg>Windows</span>
<span class="plat" title="Linux / Deck"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><circle cx="12" cy="7" r="3.4"/><path d="M6.5 20c0-4 2-7.5 5.5-7.5S17.5 16 17.5 20c-1.6 1-3.5 1.4-5.5 1.4S8.1 21 6.5 20Z"/></svg>Linux</span>
<span class="plat" title="Cloud saves"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M7 18a4.5 4.5 0 0 1-.6-8.96A6 6 0 0 1 18 9.3 4 4 0 0 1 17.5 18H7Z"/></svg>Cloud</span>
<span class="plat" title="Full controller support"><svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M6.5 8h11a4.5 4.5 0 0 1 4.4 5.4l-.8 4a2.4 2.4 0 0 1-4.2 1L15 16H9l-1.9 2.4a2.4 2.4 0 0 1-4.2-1l-.8-4A4.5 4.5 0 0 1 6.5 8Zm1 2.5v1.3H6.2v1.4h1.3v1.3h1.4v-1.3h1.3v-1.4H8.9v-1.3H7.5Zm8.3.4a1 1 0 1 0 0 2 1 1 0 0 0 0-2Zm2.6 1.9a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z"/></svg>Controller</span>
</div>
</aside>
</section>
<!-- ===================== Lower grid ===================== -->
<div class="lower-grid">
<div class="lower-main">
<!-- About -->
<section class="panel about-panel" aria-labelledby="aboutHeading">
<h2 class="panel-heading" id="aboutHeading">ABOUT THIS GAME</h2>
<div class="about-body" id="aboutBody">
<p>
The crown is gone. The Hollow wears it now. <strong>Hollow Reign</strong> drops you
into the ruined kingdom of Vael Maren a decade after its sun was extinguished —
a seamless open world of drowned cathedrals, ember forests, and cities that
calcified mid-scream. Every region is hand-built, every shortcut earned.
</p>
<p>
Combat is deliberate and punishing: a stagger system rewards reading enemy
rhythm over button speed, and the <em>class-free skill web</em> lets you splice
Umbral sorcery into greatsword play or turn a shield build into a parry-counter
machine. Respec freely at any Bonewarden shrine — experimentation is the point.
</p>
<p>
Bring up to two allies into your world with full progression sharing, or invade
the Reigns of others as a Hollowed revenant. Your choices persist: factions you
betray remember, gates you open stay open, and the ending you earn is stitched
from a hundred small decisions — not one dialog wheel.
</p>
<ul class="feature-list">
<li><span class="feat-key">5 regions</span> hand-crafted open world, zero loading walls</li>
<li><span class="feat-key">140+ skills</span> class-free web with free respec</li>
<li><span class="feat-key">3-player co-op</span> drop-in, full progression for guests</li>
<li><span class="feat-key">27 bosses</span> each with a no-hit cosmetic reward</li>
</ul>
</div>
<button class="about-toggle" id="aboutToggle" aria-expanded="false" aria-controls="aboutBody">READ MORE</button>
</section>
<!-- System requirements -->
<section class="panel sysreq-panel" aria-labelledby="sysreqHeading">
<div class="panel-head-row">
<h2 class="panel-heading" id="sysreqHeading">SYSTEM REQUIREMENTS</h2>
<div class="seg-tabs" role="tablist" aria-label="Requirement level">
<button class="seg-tab is-active" id="tabMin" role="tab" aria-selected="true" aria-controls="reqMin">MINIMUM</button>
<button class="seg-tab" id="tabRec" role="tab" aria-selected="false" aria-controls="reqRec">RECOMMENDED</button>
</div>
</div>
<div class="req-grid" id="reqMin" role="tabpanel" aria-labelledby="tabMin">
<div class="req-row"><span class="req-key">OS</span><span class="req-val">Windows 10 64-bit / SteamOS 3.5</span></div>
<div class="req-row"><span class="req-key">CPU</span><span class="req-val">Ryzen 5 2600 / Core i5-9400F</span></div>
<div class="req-row"><span class="req-key">RAM</span><span class="req-val">12 GB</span></div>
<div class="req-row"><span class="req-key">GPU</span><span class="req-val">GTX 1060 6GB / RX 580 8GB</span></div>
<div class="req-row"><span class="req-key">Storage</span><span class="req-val">68 GB SSD required</span></div>
<div class="req-row"><span class="req-key">Target</span><span class="req-val">1080p · 30 FPS · Low preset</span></div>
</div>
<div class="req-grid" id="reqRec" role="tabpanel" aria-labelledby="tabRec" hidden>
<div class="req-row"><span class="req-key">OS</span><span class="req-val">Windows 11 64-bit</span></div>
<div class="req-row"><span class="req-key">CPU</span><span class="req-val">Ryzen 7 5800X / Core i7-12700K</span></div>
<div class="req-row"><span class="req-key">RAM</span><span class="req-val">16 GB</span></div>
<div class="req-row"><span class="req-key">GPU</span><span class="req-val">RTX 3070 / RX 6800 XT</span></div>
<div class="req-row"><span class="req-key">Storage</span><span class="req-val">68 GB NVMe SSD</span></div>
<div class="req-row"><span class="req-key">Target</span><span class="req-val">1440p · 60 FPS · High preset + RT shadows</span></div>
</div>
</section>
</div>
<!-- DLC / Editions -->
<aside class="lower-side">
<section class="panel editions-panel" aria-labelledby="editionsHeading">
<h2 class="panel-heading" id="editionsHeading">EDITIONS & DLC</h2>
<article class="edition-card">
<div class="edition-head">
<h3 class="edition-name">Standard Edition</h3>
<span class="edition-tag">BASE</span>
</div>
<p class="edition-desc">Base game + Crown of Ash update.</p>
<div class="edition-buy">
<span class="edition-price"><s>$39.99</s> $25.99</span>
<button class="btn-mini" data-edition="Standard Edition" data-price="25.99">ADD</button>
</div>
</article>
<article class="edition-card edition-featured">
<div class="edition-head">
<h3 class="edition-name">Sovereign Edition</h3>
<span class="edition-tag tag-best">BEST VALUE</span>
</div>
<p class="edition-desc">Base game, both expansions, artbook + OST, Gravewrought armor set.</p>
<div class="edition-buy">
<span class="edition-price"><s>$69.99</s> $48.99</span>
<button class="btn-mini" data-edition="Sovereign Edition" data-price="48.99">ADD</button>
</div>
</article>
<article class="edition-card">
<div class="edition-head">
<h3 class="edition-name">DLC — The Drowned Court</h3>
<span class="edition-tag tag-dlc">EXPANSION</span>
</div>
<p class="edition-desc">New region, 6 bosses, Tidecaller skill branch. Requires base game.</p>
<div class="edition-buy">
<span class="edition-price">$19.99</span>
<button class="btn-mini" data-edition="The Drowned Court" data-price="19.99">ADD</button>
</div>
</article>
<article class="edition-card">
<div class="edition-head">
<h3 class="edition-name">DLC — Ashes of the Magistrate</h3>
<span class="edition-tag tag-dlc">EXPANSION</span>
</div>
<p class="edition-desc">Vespera prequel campaign + roguelite Trial of Embers mode.</p>
<div class="edition-buy">
<span class="edition-price">$14.99</span>
<button class="btn-mini" data-edition="Ashes of the Magistrate" data-price="14.99">ADD</button>
</div>
</article>
</section>
<section class="panel awards-panel" aria-label="Accolades">
<blockquote class="quote">“The best stagger combat since the genre was named.”<cite>— Polyhedral, 9.5/10</cite></blockquote>
<blockquote class="quote">“Vael Maren is a world you grieve for.”<cite>— GLHF Weekly</cite></blockquote>
</section>
</aside>
</div>
<p class="legal">Hollow Reign © 2026 Nullforge. Voidfront Store is a fictional storefront. All names, prices, and reviews on this page are illustrative.</p>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Store Page (Steam-style: media · specs · buy)
A full product page for the fictional action RPG Hollow Reign on the equally fictional Voidfront store, laid out the way PC storefronts do it: a wide media column with a 16:9 viewer and a five-slot thumbnail strip on the left, and a purchase column on the right with capsule art, a short pitch, review summary, genre tag chips, a discounted price block with a live deal countdown, and big angled Add to Cart / Wishlist CTAs over a platform-support row.
The thumbnail strip is a proper tablist — click or arrow-key between slides and the viewer
swaps its gradient art, kind badge (trailer vs. screenshot), and caption with a quick crossfade;
the play button only appears on video slides. Cart and wishlist are real toggles: the header
cart badge bumps as editions and DLC are added or removed, the wishlist heart pops and persists
its pressed state, and every action confirms through a neon toast. The review sentiment bar
fills to 91% on load and replays when the review link is clicked.
Below the fold, an About panel clamps long copy behind a Read More toggle with a feature grid, the System Requirements panel switches between Minimum and Recommended via segmented tabs, and an Editions & DLC rail lists the standard, best-value, and expansion SKUs with their own add buttons — capped off with pull-quote accolades.
Illustrative UI only — fictional games, studios, characters, and data. Not engine integrations.