Wiki — Fandom / Game Wiki Landing
An immersive Fandom-style landing page for the fictional Verdant Empire game universe, with a dramatic CSS-drawn parallax key-art hero, a big lore search bar that live-previews matching characters, factions, locations, and relics, an animated wiki-stats bar, featured-lore category panels with hover-glow, an auto-rotating featured-character spotlight, a trending-pages ranking, a self-updating recent-activity feed, and a Discord community band — built entirely in semantic HTML, CSS, and vanilla JavaScript.
MCP
Código
:root {
--bg: #0f1014;
--bg-2: #14161c;
--panel: #1a1c22;
--panel-2: #20232b;
--ink: #e8e8ee;
--ink-2: #c2c4cf;
--muted: #8b8e9c;
--line: rgba(255, 255, 255, 0.09);
--line-2: rgba(255, 255, 255, 0.16);
--link: #22d3ee;
--link-hover: #67e8f9;
--accent: #f43f5e;
--accent-2: #fb7185;
--cyan: #22d3ee;
--amber: #f59e0b;
--violet: #a78bfa;
--emerald: #34d399;
--pos: #34d399;
--neg: #fb7185;
--code-bg: #0c0d11;
--kbd-bg: #2a2d36;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 14px;
--r-xl: 20px;
--shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
--shadow-sm: 0 2px 10px rgba(0, 0, 0, 0.35);
--maxw: 1180px;
--ui: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
--display: "Cinzel", Georgia, serif;
--mono: "JetBrains Mono", ui-monospace, monospace;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: var(--ui);
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
line-height: 1.55;
}
img, svg { display: block; max-width: 100%; }
a { color: var(--link); text-decoration: none; }
a:hover { color: var(--link-hover); }
:focus-visible {
outline: 2px solid var(--cyan);
outline-offset: 2px;
border-radius: 4px;
}
.skip-link {
position: absolute;
left: 12px;
top: -60px;
z-index: 200;
background: var(--accent);
color: #fff;
padding: 10px 16px;
border-radius: var(--r-sm);
font-weight: 700;
transition: top .2s;
}
.skip-link:focus { top: 12px; color: #fff; }
.container { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 24px; }
/* ============ BUTTONS ============ */
.btn {
font-family: var(--ui);
font-weight: 600;
font-size: 14px;
border: 1px solid transparent;
border-radius: var(--r-md);
padding: 9px 16px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 8px;
line-height: 1;
transition: transform .15s, background .2s, border-color .2s, box-shadow .2s;
white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn--primary {
background: linear-gradient(180deg, var(--accent-2), var(--accent));
color: #fff;
box-shadow: 0 6px 18px rgba(244, 63, 94, 0.35);
}
.btn--primary:hover { color: #fff; box-shadow: 0 8px 26px rgba(244, 63, 94, 0.5); }
.btn--ghost {
background: rgba(255, 255, 255, 0.04);
border-color: var(--line-2);
color: var(--ink);
}
.btn--ghost:hover { background: rgba(255, 255, 255, 0.09); color: var(--ink); }
.btn--on-dark { background: rgba(255, 255, 255, 0.08); }
.btn--discord {
background: #5865f2;
color: #fff;
box-shadow: 0 6px 18px rgba(88, 101, 242, 0.4);
}
.btn--discord:hover { background: #6d78f5; color: #fff; }
/* ============ TOP BAR ============ */
.topbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(15, 16, 20, 0.82);
backdrop-filter: blur(14px) saturate(140%);
border-bottom: 1px solid var(--line);
}
.topbar__inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 0 24px;
height: 62px;
display: flex;
align-items: center;
gap: 18px;
}
.brand { display: flex; align-items: center; gap: 9px; color: var(--ink); }
.brand:hover { color: var(--ink); }
.brand__mark { color: var(--accent); display: grid; place-items: center; }
.brand__text {
font-family: var(--display);
font-weight: 800;
font-size: 18px;
letter-spacing: 1.5px;
line-height: 1;
}
.brand__text em { font-style: normal; color: var(--accent); }
.brand__tag {
font-size: 9px;
font-weight: 800;
letter-spacing: 2px;
color: var(--bg);
background: var(--cyan);
padding: 3px 6px;
border-radius: 4px;
align-self: center;
}
.primary-nav {
display: flex;
gap: 4px;
margin-left: 8px;
}
.primary-nav a {
color: var(--ink-2);
font-weight: 600;
font-size: 14px;
padding: 8px 12px;
border-radius: var(--r-sm);
transition: background .2s, color .2s;
}
.primary-nav a:hover { background: rgba(255, 255, 255, 0.06); color: var(--ink); }
.topbar__actions { margin-left: auto; display: flex; gap: 10px; }
.nav-toggle {
display: none;
flex-direction: column;
justify-content: center;
gap: 4px;
width: 40px;
height: 40px;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
background: rgba(255, 255, 255, 0.04);
cursor: pointer;
}
.nav-toggle span {
display: block;
height: 2px;
width: 18px;
margin: 0 auto;
background: var(--ink);
border-radius: 2px;
transition: transform .25s, opacity .2s;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
/* ============ HERO ============ */
.hero {
position: relative;
min-height: 560px;
display: flex;
align-items: center;
overflow: hidden;
background: radial-gradient(120% 80% at 50% 0%, #1b2433 0%, #0f1014 60%);
border-bottom: 1px solid var(--line);
}
.hero__scene { position: absolute; inset: -8% -4% -4% -4%; pointer-events: none; }
.layer { position: absolute; inset: 0; will-change: transform; }
.layer--sky {
background:
radial-gradient(80% 60% at 70% 12%, rgba(244, 63, 94, 0.22), transparent 60%),
radial-gradient(70% 50% at 20% 18%, rgba(34, 211, 238, 0.16), transparent 55%),
linear-gradient(180deg, #0c111c 0%, #0f1014 70%);
}
.layer--stars {
background-image:
radial-gradient(1.4px 1.4px at 12% 22%, rgba(255,255,255,.9), transparent),
radial-gradient(1.2px 1.2px at 26% 14%, rgba(255,255,255,.7), transparent),
radial-gradient(1.6px 1.6px at 42% 30%, rgba(255,255,255,.85), transparent),
radial-gradient(1.1px 1.1px at 58% 10%, rgba(255,255,255,.6), transparent),
radial-gradient(1.5px 1.5px at 74% 26%, rgba(255,255,255,.8), transparent),
radial-gradient(1.2px 1.2px at 88% 16%, rgba(255,255,255,.7), transparent),
radial-gradient(1.3px 1.3px at 33% 8%, rgba(255,255,255,.65), transparent),
radial-gradient(1.2px 1.2px at 66% 34%, rgba(255,255,255,.7), transparent);
}
.layer--moon {
background:
radial-gradient(circle at 78% 20%, #f6e7c8 0 1.6%, rgba(246,231,200,.5) 1.6% 2.4%, transparent 3%),
radial-gradient(circle at 78% 20%, rgba(244, 63, 94, 0.18) 0 7%, transparent 9%);
filter: drop-shadow(0 0 24px rgba(246, 231, 200, 0.3));
}
.layer--peaks {
background:
linear-gradient(135deg, transparent 49.6%, #161b27 50%) 0 0 / 360px 280px repeat-x,
linear-gradient(225deg, transparent 49.6%, #1b2230 50%) 0 0 / 360px 280px repeat-x;
background-position: 0 64%, 180px 70%;
opacity: .9;
}
.layer--spires {
bottom: 0;
background:
linear-gradient(180deg, transparent 38%, #20283a 38%) 50% 100% / 90px 60% no-repeat,
linear-gradient(180deg, transparent 50%, #1a2230 50%) 30% 100% / 60px 50% no-repeat,
linear-gradient(180deg, transparent 46%, #1a2230 46%) 70% 100% / 70px 54% no-repeat,
linear-gradient(180deg, transparent 56%, #161d2a 56%) 14% 100% / 46px 44% no-repeat,
linear-gradient(180deg, transparent 56%, #161d2a 56%) 86% 100% / 50px 44% no-repeat;
}
.layer--canopy {
bottom: 0;
height: 46%;
top: auto;
background:
radial-gradient(60% 80% at 12% 100%, #0b1f17 0 40%, transparent 70%),
radial-gradient(70% 90% at 40% 100%, #0d2419 0 42%, transparent 72%),
radial-gradient(70% 90% at 70% 100%, #0b2016 0 42%, transparent 72%),
radial-gradient(60% 80% at 92% 100%, #0c2418 0 42%, transparent 72%),
linear-gradient(180deg, transparent, #081410 86%);
}
.layer--fog {
bottom: 0;
height: 60%;
top: auto;
background: linear-gradient(180deg, transparent, rgba(34, 211, 238, 0.05) 40%, rgba(15, 16, 20, 0.7) 100%);
mix-blend-mode: screen;
}
.layer--vignette {
background: radial-gradient(120% 100% at 50% 30%, transparent 50%, rgba(0,0,0,.55) 100%);
}
.hero__content {
position: relative;
z-index: 5;
max-width: var(--maxw);
width: 100%;
margin: 0 auto;
padding: 72px 24px 88px;
text-align: center;
}
.hero__eyebrow {
font-family: var(--ui);
text-transform: uppercase;
letter-spacing: 4px;
font-size: 12px;
font-weight: 700;
color: var(--cyan);
margin: 0 0 14px;
}
.hero__title {
font-family: var(--display);
font-weight: 800;
font-size: clamp(46px, 9vw, 96px);
line-height: 0.98;
margin: 0 0 18px;
letter-spacing: 1px;
background: linear-gradient(180deg, #fff 0%, #f8d5dc 55%, var(--accent) 130%);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 0 60px rgba(244, 63, 94, 0.35);
}
.hero__sub {
max-width: 620px;
margin: 0 auto 30px;
color: var(--ink-2);
font-size: clamp(15px, 1.6vw, 18px);
line-height: 1.6;
}
/* hero entrance */
.hero__eyebrow, .hero__title, .hero__sub, .hero-search, .hero__chips {
opacity: 0;
transform: translateY(18px);
animation: rise .8s cubic-bezier(.2, .7, .2, 1) forwards;
}
.hero__title { animation-delay: .08s; }
.hero__sub { animation-delay: .18s; }
.hero-search { animation-delay: .28s; }
.hero__chips { animation-delay: .38s; }
@keyframes rise { to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
.hero__eyebrow, .hero__title, .hero__sub, .hero-search, .hero__chips { animation: none; opacity: 1; transform: none; }
html { scroll-behavior: auto; }
}
/* ============ HERO SEARCH ============ */
.hero-search { position: relative; max-width: 620px; margin: 0 auto; }
.hero-search__field {
display: flex;
align-items: center;
background: rgba(20, 22, 28, 0.85);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
padding: 6px 6px 6px 16px;
box-shadow: var(--shadow);
transition: border-color .2s, box-shadow .2s;
}
.hero-search__field:focus-within {
border-color: var(--cyan);
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.18), var(--shadow);
}
.hero-search__icon { color: var(--muted); flex: none; }
.hero-search__input {
flex: 1;
border: 0;
background: transparent;
color: var(--ink);
font-size: 16px;
font-family: var(--ui);
padding: 12px;
outline: none;
}
.hero-search__input::placeholder { color: var(--muted); }
.hero-search__go {
border: 0;
background: linear-gradient(180deg, var(--accent-2), var(--accent));
color: #fff;
font-weight: 700;
font-size: 14px;
padding: 11px 20px;
border-radius: var(--r-md);
cursor: pointer;
transition: filter .2s;
}
.hero-search__go:hover { filter: brightness(1.08); }
.search-preview {
position: absolute;
top: calc(100% + 10px);
left: 0;
right: 0;
z-index: 30;
list-style: none;
margin: 0;
padding: 6px;
text-align: left;
background: var(--panel);
border: 1px solid var(--line-2);
border-radius: var(--r-lg);
box-shadow: var(--shadow);
max-height: 340px;
overflow-y: auto;
}
.search-preview[hidden] { display: none; }
.sp-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
border-radius: var(--r-md);
cursor: pointer;
}
.sp-item:hover, .sp-item[aria-selected="true"] { background: rgba(34, 211, 238, 0.1); }
.sp-item__badge {
font-size: 10px;
font-weight: 800;
letter-spacing: .5px;
text-transform: uppercase;
padding: 3px 7px;
border-radius: 5px;
flex: none;
color: var(--bg);
}
.badge--character { background: var(--accent-2); }
.badge--faction { background: var(--cyan); }
.badge--location { background: var(--amber); }
.badge--item { background: var(--violet); }
.badge--event { background: var(--emerald); }
.sp-item__main { min-width: 0; }
.sp-item__title { font-weight: 600; font-size: 14px; color: var(--ink); }
.sp-item__title mark { background: rgba(244, 63, 94, 0.32); color: #fff; border-radius: 3px; padding: 0 1px; }
.sp-item__sub { font-size: 12px; color: var(--muted); }
.sp-empty { padding: 18px; text-align: center; color: var(--muted); font-size: 14px; }
.hero__chips {
margin-top: 18px;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
flex-wrap: wrap;
}
.hero__chips-label { color: var(--muted); font-size: 13px; }
.chip {
font-family: var(--ui);
font-size: 13px;
font-weight: 600;
color: var(--ink-2);
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--line-2);
border-radius: 999px;
padding: 6px 14px;
cursor: pointer;
transition: background .2s, color .2s, border-color .2s;
}
.chip:hover { background: rgba(34, 211, 238, 0.14); color: var(--cyan); border-color: var(--cyan); }
/* ============ STATS BAR ============ */
.stats {
background: linear-gradient(180deg, #14161c, #0f1014);
border-bottom: 1px solid var(--line);
}
.stats__inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 18px 24px;
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
}
.stat { display: flex; flex-direction: column; gap: 2px; padding: 4px 22px 4px 0; border-right: 1px solid var(--line); }
.stat:last-child { border-right: 0; }
.stat__num { font-family: var(--display); font-weight: 700; font-size: 22px; color: var(--ink); line-height: 1; }
.stat__label { font-size: 12px; color: var(--muted); font-weight: 500; }
.stat--live { flex-direction: row; align-items: center; gap: 8px; margin-left: auto; padding-right: 0; }
.stat__dot { width: 9px; height: 9px; border-radius: 50%; background: var(--emerald); box-shadow: 0 0 0 0 rgba(52, 211, 153, .6); animation: pulse 2s infinite; }
@keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(52,211,153,.5); } 70% { box-shadow: 0 0 0 8px rgba(52,211,153,0); } 100% { box-shadow: 0 0 0 0 rgba(52,211,153,0); } }
.stat--live .stat__label { color: var(--ink-2); }
/* ============ LAYOUT ============ */
.layout {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 32px;
padding-top: 44px;
padding-bottom: 44px;
}
.layout__main { min-width: 0; }
.section-head {
display: flex;
align-items: baseline;
justify-content: space-between;
margin-bottom: 18px;
}
.section-title {
font-family: var(--display);
font-weight: 700;
font-size: 24px;
margin: 0;
letter-spacing: .5px;
}
.section-link { font-weight: 600; font-size: 14px; }
/* ============ FEATURED ROTATOR ============ */
.featured { margin-bottom: 48px; }
.featured__nav { display: flex; gap: 6px; }
.rot-btn {
width: 36px; height: 36px;
border-radius: var(--r-sm);
border: 1px solid var(--line-2);
background: rgba(255,255,255,.04);
color: var(--ink);
font-size: 20px;
line-height: 1;
cursor: pointer;
transition: background .2s, border-color .2s;
}
.rot-btn:hover { background: rgba(244,63,94,.15); border-color: var(--accent); }
.hl-card {
display: grid;
grid-template-columns: 260px 1fr;
gap: 0;
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
}
.hl-card__art {
position: relative;
min-height: 280px;
background:
radial-gradient(80% 70% at 50% 20%, rgba(244,63,94,.35), transparent 70%),
linear-gradient(160deg, #2a2030, #131319);
overflow: hidden;
transition: background .5s;
}
.hl-card__art::after {
content: "";
position: absolute; inset: 0;
background: repeating-linear-gradient(115deg, transparent 0 18px, rgba(255,255,255,.025) 18px 19px);
}
.hl-portrait {
position: absolute;
left: 50%; bottom: 0;
width: 150px; height: 200px;
transform: translateX(-50%);
background:
radial-gradient(40% 30% at 50% 22%, #f3d9b3 0 60%, transparent 62%),
linear-gradient(180deg, transparent 30%, #1b1d24 30% 100%);
border-radius: 80px 80px 0 0;
box-shadow: inset 0 -40px 60px rgba(0,0,0,.5);
}
.hl-portrait::before {
content: "";
position: absolute;
left: 50%; top: 8%;
width: 84px; height: 30px;
transform: translateX(-50%) rotate(-2deg);
background: linear-gradient(180deg, var(--accent), #7a1f33);
clip-path: polygon(0 100%, 12% 0, 28% 60%, 50% 0, 72% 60%, 88% 0, 100% 100%);
filter: drop-shadow(0 0 8px rgba(244,63,94,.5));
}
.hl-card__art[data-art="kaelen"] { background: radial-gradient(80% 70% at 50% 20%, rgba(34,211,238,.32), transparent 70%), linear-gradient(160deg, #142a31, #0f1419); }
.hl-card__art[data-art="kaelen"] .hl-portrait::before { background: linear-gradient(180deg, var(--cyan), #0e5560); }
.hl-card__art[data-art="morwen"] { background: radial-gradient(80% 70% at 50% 20%, rgba(167,139,250,.34), transparent 70%), linear-gradient(160deg, #251f33, #141019); }
.hl-card__art[data-art="morwen"] .hl-portrait::before { background: linear-gradient(180deg, var(--violet), #4a3a8a); }
.hl-card__body { padding: 26px 28px; }
.hl-card__kicker { font-size: 11px; font-weight: 800; letter-spacing: 2px; color: var(--cyan); }
.hl-card__name { font-family: var(--display); font-weight: 700; font-size: 30px; margin: 8px 0 4px; letter-spacing: .5px; }
.hl-card__role { color: var(--accent-2); font-weight: 600; font-size: 14px; margin: 0 0 12px; }
.hl-card__desc { color: var(--ink-2); font-size: 15px; line-height: 1.65; margin: 0 0 16px; }
.hl-card__meta { display: flex; flex-wrap: wrap; gap: 8px 22px; margin-bottom: 20px; }
.hl-card__meta span { font-size: 13px; color: var(--muted); }
.hl-card__meta strong { display: block; color: var(--ink-2); font-size: 11px; text-transform: uppercase; letter-spacing: 1px; font-weight: 700; }
.hl-card__actions { display: flex; gap: 10px; }
.featured__dots { display: flex; justify-content: center; gap: 8px; margin-top: 16px; }
.rot-dot {
width: 9px; height: 9px; border-radius: 50%;
border: 0;
background: var(--line-2);
cursor: pointer;
transition: background .2s, transform .2s, width .2s, border-radius .2s;
}
.rot-dot[aria-selected="true"] { background: var(--accent); width: 22px; border-radius: 6px; }
/* ============ LORE CARDS ============ */
.lore { margin-bottom: 12px; }
.lore__grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 18px; }
.lore-card {
display: flex;
flex-direction: column;
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
color: var(--ink);
transition: transform .25s, border-color .25s, box-shadow .25s;
position: relative;
}
.lore-card::after {
content: "";
position: absolute; inset: 0;
border-radius: var(--r-lg);
pointer-events: none;
box-shadow: 0 0 0 0 transparent;
transition: box-shadow .3s;
}
.lore-card:hover { transform: translateY(-4px); color: var(--ink); }
.lore-card[data-glow="rose"]:hover { border-color: var(--accent); }
.lore-card[data-glow="rose"]:hover::after { box-shadow: inset 0 0 40px rgba(244,63,94,.18), 0 14px 34px rgba(244,63,94,.18); }
.lore-card[data-glow="cyan"]:hover { border-color: var(--cyan); }
.lore-card[data-glow="cyan"]:hover::after { box-shadow: inset 0 0 40px rgba(34,211,238,.16), 0 14px 34px rgba(34,211,238,.16); }
.lore-card[data-glow="amber"]:hover { border-color: var(--amber); }
.lore-card[data-glow="amber"]:hover::after { box-shadow: inset 0 0 40px rgba(245,158,11,.16), 0 14px 34px rgba(245,158,11,.16); }
.lore-card[data-glow="violet"]:hover { border-color: var(--violet); }
.lore-card[data-glow="violet"]:hover::after { box-shadow: inset 0 0 40px rgba(167,139,250,.16), 0 14px 34px rgba(167,139,250,.16); }
.lore-card__thumb {
height: 130px;
position: relative;
overflow: hidden;
}
.thumb--characters { background: radial-gradient(70% 90% at 30% 110%, rgba(244,63,94,.55), transparent 60%), linear-gradient(160deg, #2a1c24, #141016); }
.thumb--factions { background: radial-gradient(70% 90% at 70% 110%, rgba(34,211,238,.5), transparent 60%), linear-gradient(160deg, #15262c, #0f1418); }
.thumb--locations { background: radial-gradient(80% 90% at 50% 120%, rgba(245,158,11,.45), transparent 60%), linear-gradient(160deg, #2a2417, #141008); }
.thumb--items { background: radial-gradient(70% 90% at 60% 110%, rgba(167,139,250,.5), transparent 60%), linear-gradient(160deg, #221d31, #120f19); }
.lore-card__thumb::before {
content: "";
position: absolute; inset: 0;
background:
repeating-linear-gradient(45deg, transparent 0 22px, rgba(255,255,255,.03) 22px 23px);
}
.thumb--characters::after, .thumb--factions::after, .thumb--locations::after, .thumb--items::after {
content: "";
position: absolute;
left: 50%; bottom: -10px;
width: 90px; height: 90px;
transform: translateX(-50%) rotate(45deg);
border: 2px solid rgba(255,255,255,.14);
border-radius: 14px;
}
.lore-card__body { padding: 16px 18px 18px; flex: 1; display: flex; flex-direction: column; }
.lore-card__title { font-family: var(--display); font-weight: 700; font-size: 19px; margin: 0 0 6px; }
.lore-card__desc { color: var(--muted); font-size: 14px; line-height: 1.5; margin: 0 0 12px; flex: 1; }
.lore-card__count {
align-self: flex-start;
font-size: 12px;
font-weight: 700;
color: var(--ink-2);
background: rgba(255,255,255,.05);
border: 1px solid var(--line);
border-radius: 999px;
padding: 4px 11px;
}
/* ============ RIGHT RAIL ============ */
.layout__rail { display: flex; flex-direction: column; gap: 22px; }
.panel {
background: var(--panel);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 18px;
}
.panel__title {
font-family: var(--ui);
font-weight: 700;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 1px;
color: var(--ink-2);
margin: 0 0 14px;
display: flex;
align-items: center;
gap: 8px;
}
.panel__icon { color: var(--accent); }
.trend-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.trend-list a {
display: flex;
align-items: center;
gap: 12px;
padding: 9px 8px;
border-radius: var(--r-sm);
color: var(--ink);
transition: background .2s;
}
.trend-list a:hover { background: rgba(255,255,255,.05); color: var(--ink); }
.trend-list__rank {
width: 22px; height: 22px;
display: grid; place-items: center;
flex: none;
font-family: var(--display);
font-weight: 700;
font-size: 13px;
color: var(--muted);
border: 1px solid var(--line-2);
border-radius: 6px;
}
.trend-list a:hover .trend-list__rank { color: var(--accent); border-color: var(--accent); }
.trend-list__name { flex: 1; font-weight: 600; font-size: 14px; }
.trend-list__hits { font-family: var(--mono); font-size: 12px; color: var(--emerald); font-weight: 500; }
.feed { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.feed__item { display: flex; gap: 11px; padding: 8px 6px; border-radius: var(--r-sm); }
.feed__item.is-new { animation: flash 1.4s ease; }
@keyframes flash { 0% { background: rgba(34,211,238,.16); } 100% { background: transparent; } }
.feed__avatar {
width: 30px; height: 30px;
flex: none;
border-radius: 8px;
display: grid; place-items: center;
font-weight: 800;
font-size: 13px;
color: var(--bg);
}
.feed__avatar[data-c="a"] { background: var(--accent-2); }
.feed__avatar[data-c="b"] { background: var(--cyan); }
.feed__avatar[data-c="c"] { background: var(--amber); }
.feed__avatar[data-c="d"] { background: var(--violet); }
.feed__avatar[data-c="e"] { background: var(--emerald); }
.feed__body p { margin: 0; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.feed__body strong { color: var(--ink); font-weight: 600; }
.feed__meta { font-size: 12px; color: var(--muted); }
.feed__diff { font-family: var(--mono); font-weight: 500; }
.feed__diff--pos { color: var(--pos); }
.feed__diff--neg { color: var(--neg); }
/* ============ COMMUNITY BAND ============ */
.community {
margin-top: 8px;
background:
radial-gradient(70% 120% at 85% 50%, rgba(88,101,242,.25), transparent 60%),
radial-gradient(60% 120% at 10% 20%, rgba(244,63,94,.18), transparent 60%),
linear-gradient(180deg, #161821, #0f1014);
border-top: 1px solid var(--line);
border-bottom: 1px solid var(--line);
}
.community__inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 52px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
flex-wrap: wrap;
}
.community__text { max-width: 580px; }
.community__title { font-family: var(--display); font-weight: 700; font-size: clamp(26px, 4vw, 36px); margin: 0 0 10px; }
.community__desc { color: var(--ink-2); font-size: 16px; line-height: 1.6; margin: 0 0 22px; }
.community__actions { display: flex; gap: 12px; flex-wrap: wrap; }
.community__stats { display: flex; gap: 14px; }
.cstat {
text-align: center;
background: rgba(255,255,255,.04);
border: 1px solid var(--line-2);
border-radius: var(--r-md);
padding: 16px 22px;
min-width: 84px;
}
.cstat__num { display: block; font-family: var(--display); font-weight: 700; font-size: 26px; color: var(--cyan); }
.cstat__label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; }
/* ============ FOOTER ============ */
.footer { background: #0c0d11; border-top: 1px solid var(--line); }
.footer__inner {
max-width: var(--maxw);
margin: 0 auto;
padding: 26px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
}
.footer__inner p { margin: 0; color: var(--muted); font-size: 13px; }
.footer__links { display: flex; gap: 18px; flex-wrap: wrap; }
.footer__links a { color: var(--ink-2); font-size: 13px; font-weight: 500; }
/* ============ TOAST ============ */
.toast {
position: fixed;
left: 50%; bottom: 28px;
transform: translateX(-50%) translateY(20px);
background: var(--panel-2);
color: var(--ink);
border: 1px solid var(--line-2);
border-left: 3px solid var(--cyan);
padding: 12px 18px;
border-radius: var(--r-md);
font-size: 14px;
font-weight: 500;
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
z-index: 300;
transition: opacity .25s, transform .25s;
max-width: min(90vw, 420px);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
/* ============ NAV SCRIM ============ */
.nav-scrim {
position: fixed; inset: 0;
background: rgba(0,0,0,.5);
z-index: 90;
backdrop-filter: blur(2px);
}
/* ============ RESPONSIVE ============ */
@media (max-width: 980px) {
.layout { grid-template-columns: 1fr; }
.layout__rail { flex-direction: row; flex-wrap: wrap; }
.panel { flex: 1 1 280px; }
}
@media (max-width: 820px) {
.primary-nav {
position: fixed;
top: 62px; left: 0; right: 0;
flex-direction: column;
gap: 2px;
background: var(--panel);
border-bottom: 1px solid var(--line-2);
padding: 10px 16px 18px;
transform: translateY(-120%);
transition: transform .28s ease;
z-index: 95;
margin-left: 0;
}
.primary-nav.open { transform: translateY(0); }
.primary-nav a { padding: 12px; font-size: 15px; }
.nav-toggle { display: flex; }
.topbar__actions .btn--ghost { display: none; }
.hl-card { grid-template-columns: 1fr; }
.hl-card__art { min-height: 180px; }
.hl-portrait { height: 150px; width: 110px; }
.community__inner { flex-direction: column; align-items: flex-start; }
}
@media (max-width: 520px) {
.container, .topbar__inner, .hero__content, .stats__inner, .community__inner, .footer__inner { padding-left: 16px; padding-right: 16px; }
.brand__tag { display: none; }
.hero { min-height: 480px; }
.hero__content { padding: 48px 16px 64px; }
.hero-search__field { flex-wrap: wrap; padding: 8px; }
.hero-search__icon { display: none; }
.hero-search__input { min-width: 0; flex: 1 1 100%; padding: 10px; }
.hero-search__go { flex: 1 1 100%; padding: 12px; }
.lore__grid { grid-template-columns: 1fr; }
.stat { padding-right: 14px; }
.stat--live { margin-left: 0; flex-basis: 100%; }
.layout__rail { flex-direction: column; }
.community__stats { width: 100%; justify-content: space-between; }
.cstat { flex: 1; padding: 14px 8px; min-width: 0; }
.footer__inner { flex-direction: column; align-items: flex-start; }
}(function () {
"use strict";
/* ---------------- Toast helper ---------------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
window.toast = toast;
var prefersReduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
/* ---------------- Mobile nav drawer ---------------- */
var navToggle = document.getElementById("navToggle");
var primaryNav = document.getElementById("primaryNav");
var navScrim = document.getElementById("navScrim");
function setNav(open) {
if (!primaryNav || !navToggle) return;
primaryNav.classList.toggle("open", open);
navToggle.setAttribute("aria-expanded", String(open));
navToggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
if (navScrim) navScrim.hidden = !open;
}
if (navToggle) {
navToggle.addEventListener("click", function () {
setNav(navToggle.getAttribute("aria-expanded") !== "true");
});
}
if (navScrim) navScrim.addEventListener("click", function () { setNav(false); });
if (primaryNav) {
primaryNav.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () { setNav(false); });
});
}
/* ---------------- Hero parallax ---------------- */
var heroScene = document.getElementById("heroScene");
var layers = heroScene ? Array.prototype.slice.call(heroScene.querySelectorAll(".layer[data-depth]")) : [];
if (heroScene && layers.length && !prefersReduced) {
var rafPending = false;
var px = 0, py = 0;
function applyParallax() {
rafPending = false;
layers.forEach(function (l) {
var d = parseFloat(l.getAttribute("data-depth")) || 0;
l.style.transform = "translate(" + (px * d * -40) + "px," + (py * d * -24) + "px)";
});
}
var hero = document.querySelector(".hero");
hero.addEventListener("mousemove", function (e) {
var r = hero.getBoundingClientRect();
px = (e.clientX - r.left) / r.width - 0.5;
py = (e.clientY - r.top) / r.height - 0.5;
if (!rafPending) { rafPending = true; requestAnimationFrame(applyParallax); }
});
window.addEventListener("scroll", function () {
var sc = window.scrollY || 0;
if (sc > 620) return;
layers.forEach(function (l) {
var d = parseFloat(l.getAttribute("data-depth")) || 0;
l.style.setProperty("--sy", (sc * d * 0.3) + "px");
});
}, { passive: true });
}
/* ---------------- Animated stat counters ---------------- */
var statNums = document.querySelectorAll(".stat__num[data-count]");
function animateCount(el) {
var target = parseInt(el.getAttribute("data-count"), 10) || 0;
if (prefersReduced) { el.textContent = target.toLocaleString(); return; }
var start = performance.now();
var dur = 1400;
function step(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased).toLocaleString();
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
if ("IntersectionObserver" in window) {
var statObs = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { animateCount(en.target); statObs.unobserve(en.target); }
});
}, { threshold: 0.4 });
statNums.forEach(function (n) { statObs.observe(n); });
} else {
statNums.forEach(animateCount);
}
/* ---------------- Featured character rotator ---------------- */
var featured = [
{
art: "seraphine",
kicker: "CHARACTER · HOUSE VALE",
name: "Seraphine Vale",
role: "The Ashbound Heir · Wielder of the Glass Crown",
desc: "Last scion of the fallen House Vale, Seraphine bound her soul to the Ember Vow to survive the Sundering. Equal parts diplomat and warlord, she unifies the broken duchies under a single thorned banner.",
meta: [
["Allegiance", "The Thornwood Pact"],
["First seen", "Vol. I — Ashes of Vale"],
["Status", "Alive (Reign of Glass)"]
]
},
{
art: "kaelen",
kicker: "CHARACTER · TIDE WARDENS",
name: "Kaelen Drift",
role: "Warden of the Drowned Vaults · Keeper of Tides",
desc: "A reluctant prophet pulled from the Mire of Echoes, Kaelen hears the drowned kings whispering futures no one wants. His loyalty to Seraphine is the thread holding the coastal duchies in the Pact.",
meta: [
["Allegiance", "Tide Wardens"],
["First seen", "Vol. II — The Drowned Court"],
["Status", "Alive (Reign of Glass)"]
]
},
{
art: "morwen",
kicker: "CHARACTER · THE SILENT CHOIR",
name: "Morwen Thorne",
role: "Mistress of the Silent Choir · Architect of the Sundering",
desc: "Once tutor to the Vale heirs, Morwen shattered the old world to remake it in her image. Brilliant and unknowable, she commands the Silent Choir — sorcerers who trade their voices for power.",
meta: [
["Allegiance", "The Silent Choir"],
["First seen", "Vol. I — Ashes of Vale"],
["Status", "Unknown (last seen at Glass Spire)"]
]
}
];
var hlArt = document.getElementById("hlArt");
var hlKicker = document.getElementById("hlKicker");
var hlName = document.getElementById("hlName");
var hlRole = document.getElementById("hlRole");
var hlDesc = document.getElementById("hlDesc");
var hlMeta = document.getElementById("hlMeta");
var hlCard = document.getElementById("hlCard");
var dotsWrap = document.getElementById("rotDots");
var current = 0;
var rotTimer;
function buildDots() {
if (!dotsWrap) return;
featured.forEach(function (f, i) {
var b = document.createElement("button");
b.className = "rot-dot";
b.type = "button";
b.setAttribute("role", "tab");
b.setAttribute("aria-label", "Show " + f.name);
b.addEventListener("click", function () { show(i, true); });
dotsWrap.appendChild(b);
});
}
function render(i) {
var f = featured[i];
if (hlArt) hlArt.setAttribute("data-art", f.art);
if (hlKicker) hlKicker.textContent = f.kicker;
if (hlName) hlName.textContent = f.name;
if (hlRole) hlRole.textContent = f.role;
if (hlDesc) hlDesc.textContent = f.desc;
if (hlMeta) {
hlMeta.innerHTML = "";
f.meta.forEach(function (m) {
var s = document.createElement("span");
var strong = document.createElement("strong");
strong.textContent = m[0];
s.appendChild(strong);
s.appendChild(document.createTextNode(m[1]));
hlMeta.appendChild(s);
});
}
if (dotsWrap) {
dotsWrap.querySelectorAll(".rot-dot").forEach(function (d, di) {
d.setAttribute("aria-selected", String(di === i));
});
}
}
function show(i, userAction) {
current = (i + featured.length) % featured.length;
if (hlCard && !prefersReduced) {
hlCard.animate(
[{ opacity: 0.35 }, { opacity: 1 }],
{ duration: 300, easing: "ease-out" }
);
}
render(current);
if (userAction) restartAuto();
}
function restartAuto() {
clearInterval(rotTimer);
if (prefersReduced) return;
rotTimer = setInterval(function () { show(current + 1, false); }, 6500);
}
if (hlCard) {
buildDots();
render(0);
if (dotsWrap) dotsWrap.querySelector(".rot-dot").setAttribute("aria-selected", "true");
restartAuto();
var prev = document.getElementById("rotPrev");
var next = document.getElementById("rotNext");
if (prev) prev.addEventListener("click", function () { show(current - 1, true); });
if (next) next.addEventListener("click", function () { show(current + 1, true); });
var editBtn = document.getElementById("hlEditBtn");
if (editBtn) editBtn.addEventListener("click", function () { toast("Editing is disabled in this demo"); });
// pause on hover
hlCard.addEventListener("mouseenter", function () { clearInterval(rotTimer); });
hlCard.addEventListener("mouseleave", restartAuto);
}
/* ---------------- Search preview ---------------- */
var INDEX = [
{ t: "Seraphine Vale", s: "Character · The Ashbound Heir", k: "character" },
{ t: "Kaelen Drift", s: "Character · Warden of the Drowned Vaults", k: "character" },
{ t: "Morwen Thorne", s: "Character · Architect of the Sundering", k: "character" },
{ t: "The Glass Crown", s: "Relic · The throne-bound artifact", k: "item" },
{ t: "The Ember Vow", s: "Relic · A pact written in soulfire", k: "item" },
{ t: "Thornwood Canopy", s: "Location · The living capital", k: "location" },
{ t: "Mire of Echoes", s: "Location · Where the drowned kings speak", k: "location" },
{ t: "Glass Spire", s: "Location · Seat of the Silent Choir", k: "location" },
{ t: "The Thornwood Pact", s: "Faction · Seraphine's coalition of duchies", k: "faction" },
{ t: "The Silent Choir", s: "Faction · Voice-traded sorcerers", k: "faction" },
{ t: "Tide Wardens", s: "Faction · Keepers of the coastal vaults", k: "faction" },
{ t: "House Vale", s: "Faction · The fallen royal house", k: "faction" },
{ t: "The Sundering", s: "Event · The shattering of the old world", k: "event" },
{ t: "Reign of Glass", s: "Event · The current age of the Empire", k: "event" },
{ t: "Vault of Drowned Kings", s: "Location · A sunken hall of relics", k: "location" }
];
var input = document.getElementById("searchInput");
var preview = document.getElementById("searchPreview");
var form = document.getElementById("searchForm");
var activeIdx = -1;
var results = [];
function esc(s) { return s.replace(/[&<>"']/g, function (c) { return ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" })[c]; }); }
function highlight(text, q) {
if (!q) return esc(text);
var idx = text.toLowerCase().indexOf(q.toLowerCase());
if (idx < 0) return esc(text);
return esc(text.slice(0, idx)) + "<mark>" + esc(text.slice(idx, idx + q.length)) + "</mark>" + esc(text.slice(idx + q.length));
}
function openPreview() {
if (!preview) return;
preview.hidden = false;
if (input) input.setAttribute("aria-expanded", "true");
}
function closePreview() {
if (!preview) return;
preview.hidden = true;
activeIdx = -1;
if (input) input.setAttribute("aria-expanded", "false");
}
function renderResults(q) {
if (!preview) return;
var ql = q.trim().toLowerCase();
results = ql
? INDEX.filter(function (r) { return r.t.toLowerCase().indexOf(ql) > -1 || r.s.toLowerCase().indexOf(ql) > -1; }).slice(0, 6)
: [];
preview.innerHTML = "";
activeIdx = -1;
if (!ql) { closePreview(); return; }
if (!results.length) {
var li = document.createElement("li");
li.className = "sp-empty";
li.textContent = 'No lore pages match "' + q + '" — try another name.';
preview.appendChild(li);
openPreview();
return;
}
results.forEach(function (r, i) {
var li = document.createElement("li");
li.className = "sp-item";
li.id = "sp-opt-" + i;
li.setAttribute("role", "option");
li.setAttribute("aria-selected", "false");
li.innerHTML =
'<span class="sp-item__badge badge--' + r.k + '">' + r.k + '</span>' +
'<span class="sp-item__main">' +
'<span class="sp-item__title">' + highlight(r.t, q) + '</span>' +
'<span class="sp-item__sub">' + esc(r.s) + '</span></span>';
li.addEventListener("click", function () { choose(r); });
li.addEventListener("mousemove", function () { setActive(i); });
preview.appendChild(li);
});
openPreview();
}
function setActive(i) {
var items = preview.querySelectorAll(".sp-item");
items.forEach(function (el, idx) { el.setAttribute("aria-selected", String(idx === i)); });
activeIdx = i;
if (input) input.setAttribute("aria-activedescendant", i > -1 ? "sp-opt-" + i : "");
if (items[i]) items[i].scrollIntoView({ block: "nearest" });
}
function choose(r) {
if (input) input.value = r.t;
closePreview();
toast("Opening “" + r.t + "” (demo)");
}
if (input) {
input.addEventListener("input", function () { renderResults(input.value); });
input.addEventListener("focus", function () { if (input.value.trim()) renderResults(input.value); });
input.addEventListener("keydown", function (e) {
if (preview.hidden && (e.key === "ArrowDown")) { renderResults(input.value); return; }
if (preview.hidden) return;
if (e.key === "ArrowDown") { e.preventDefault(); setActive(Math.min(activeIdx + 1, results.length - 1)); }
else if (e.key === "ArrowUp") { e.preventDefault(); setActive(Math.max(activeIdx - 1, 0)); }
else if (e.key === "Enter") {
if (activeIdx > -1 && results[activeIdx]) { e.preventDefault(); choose(results[activeIdx]); }
} else if (e.key === "Escape") { closePreview(); }
});
}
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var q = input ? input.value.trim() : "";
if (!q) { toast("Type a name to search the compendium"); return; }
if (results.length && activeIdx > -1) { choose(results[activeIdx]); return; }
if (results.length) { choose(results[0]); return; }
toast('Searching for "' + q + '"… (demo)');
closePreview();
});
}
document.addEventListener("click", function (e) {
if (form && !form.contains(e.target)) closePreview();
});
/* ---------------- Popular chips ---------------- */
document.querySelectorAll(".chip[data-q]").forEach(function (chip) {
chip.addEventListener("click", function () {
if (!input) return;
input.value = chip.getAttribute("data-q");
input.focus();
renderResults(input.value);
});
});
/* ---------------- Global "/" to focus search ---------------- */
document.addEventListener("keydown", function (e) {
if (e.key === "/" && document.activeElement !== input) {
var tag = (document.activeElement && document.activeElement.tagName) || "";
if (tag === "INPUT" || tag === "TEXTAREA") return;
e.preventDefault();
if (input) { input.focus(); input.select(); }
}
});
/* ---------------- Random page ---------------- */
var randomBtn = document.getElementById("randomBtn");
if (randomBtn) {
randomBtn.addEventListener("click", function () {
var r = INDEX[Math.floor(Math.random() * INDEX.length)];
toast("Random page → " + r.t);
});
}
/* ---------------- Community buttons ---------------- */
var discordBtn = document.getElementById("discordBtn");
if (discordBtn) discordBtn.addEventListener("click", function () { toast("Discord invite is a demo — no real link"); });
var newsletterBtn = document.getElementById("newsletterBtn");
if (newsletterBtn) newsletterBtn.addEventListener("click", function () { toast("Subscribed to the lore digest (demo)"); });
/* ---------------- Live activity feed ticker ---------------- */
var feed = document.getElementById("activityFeed");
var editors = [
{ n: "Lorekeeper_Vael", c: "a", l: "L" },
{ n: "MireWalker", c: "b", l: "M" },
{ n: "ThornScribe", c: "c", l: "T" },
{ n: "AshboundAria", c: "d", l: "A" },
{ n: "GlassEcho", c: "e", l: "G" },
{ n: "DuchessOfMire", c: "a", l: "D" }
];
var actions = [
["edited", "The Glass Crown", "pos", "+"],
["created", "Spire of the Choir", "pos", "+"],
["edited", "Reign of Glass", "pos", "+"],
["reverted", "Tide Wardens", "neg", "−"],
["edited", "Seraphine Vale", "pos", "+"],
["uploaded", "sigil-house-vale.svg", "pos", "+"]
];
function randInt(n) { return Math.floor(Math.random() * n); }
function pushActivity() {
if (!feed || prefersReduced) return;
var ed = editors[randInt(editors.length)];
var ac = actions[randInt(actions.length)];
var amount = ac[3] + (randInt(900) + 40).toLocaleString();
var li = document.createElement("li");
li.className = "feed__item is-new";
li.innerHTML =
'<span class="feed__avatar" data-c="' + ed.c + '">' + ed.l + '</span>' +
'<div class="feed__body"><p><strong>' + ed.n + '</strong> ' + ac[0] +
' <a href="#" onclick="return false">' + ac[1] + '</a></p>' +
'<span class="feed__meta"><em class="feed__diff feed__diff--' + ac[2] + '">' + amount + '</em> · just now</span></div>';
feed.insertBefore(li, feed.firstChild);
while (feed.children.length > 5) feed.removeChild(feed.lastChild);
}
setInterval(pushActivity, 5200);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Verdant Empire Wiki — The Official Lore Compendium</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=Cinzel:wght@500;600;700;800&family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<!-- ============ TOP BAR ============ -->
<header class="topbar">
<div class="topbar__inner">
<button class="nav-toggle" id="navToggle" aria-label="Open menu" aria-expanded="false" aria-controls="primaryNav">
<span></span><span></span><span></span>
</button>
<a class="brand" href="#top" aria-label="Verdant Empire Wiki home">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 32 32" width="26" height="26"><path d="M16 2 L29 9 V23 L16 30 L3 23 V9 Z" fill="none" stroke="currentColor" stroke-width="1.6"/><path d="M16 8 l6 10 -6 4 -6 -4 z" fill="currentColor" opacity=".85"/></svg>
</span>
<span class="brand__text">VERDANT<em>EMPIRE</em></span>
<span class="brand__tag">WIKI</span>
</a>
<nav class="primary-nav" id="primaryNav" aria-label="Primary">
<a href="#characters">Characters</a>
<a href="#factions">Factions</a>
<a href="#locations">Locations</a>
<a href="#trending">Trending</a>
<a href="#community">Community</a>
</nav>
<div class="topbar__actions">
<button class="btn btn--ghost" id="randomBtn" type="button">Random page</button>
<button class="btn btn--primary" type="button" onclick="window.toast && toast('Sign-in is a demo only')">Sign in</button>
</div>
</div>
</header>
<main id="main">
<!-- ============ HERO ============ -->
<section class="hero" id="top" aria-label="Verdant Empire">
<div class="hero__scene" id="heroScene" aria-hidden="true">
<div class="layer layer--sky"></div>
<div class="layer layer--stars" data-depth="0.08"></div>
<div class="layer layer--moon" data-depth="0.14"></div>
<div class="layer layer--peaks" data-depth="0.26"></div>
<div class="layer layer--spires" data-depth="0.42"></div>
<div class="layer layer--canopy" data-depth="0.62"></div>
<div class="layer layer--fog" data-depth="0.30"></div>
<div class="layer layer--vignette"></div>
</div>
<div class="hero__content">
<p class="hero__eyebrow">The Official Lore Compendium</p>
<h1 class="hero__title">Verdant Empire</h1>
<p class="hero__sub">
The complete, community-built archive of the Thornwood saga — every hero, House,
relic, and ruin from the Sundering to the Reign of Glass.
</p>
<form class="hero-search" id="searchForm" role="search" autocomplete="off">
<div class="hero-search__field">
<svg class="hero-search__icon" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true"><circle cx="11" cy="11" r="7" fill="none" stroke="currentColor" stroke-width="2"/><path d="m20 20-3.2-3.2" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
<input
type="search"
id="searchInput"
class="hero-search__input"
placeholder="Search 4,812 lore pages… (press /)"
aria-label="Search the Verdant Empire Wiki"
aria-expanded="false"
aria-controls="searchPreview"
role="combobox"
aria-autocomplete="list"
/>
<button class="hero-search__go" type="submit">Search</button>
</div>
<ul class="search-preview" id="searchPreview" role="listbox" aria-label="Search results" hidden></ul>
</form>
<div class="hero__chips" aria-label="Popular searches">
<span class="hero__chips-label">Try:</span>
<button class="chip" type="button" data-q="Seraphine">Seraphine Vale</button>
<button class="chip" type="button" data-q="Glass Crown">Glass Crown</button>
<button class="chip" type="button" data-q="Thornwood">Thornwood</button>
<button class="chip" type="button" data-q="Sundering">The Sundering</button>
</div>
</div>
<div class="hero__fade"></div>
</section>
<!-- ============ STATS BAR ============ -->
<section class="stats" aria-label="Wiki statistics">
<div class="stats__inner">
<div class="stat"><span class="stat__num" data-count="4812">0</span><span class="stat__label">Pages</span></div>
<div class="stat"><span class="stat__num" data-count="318094">0</span><span class="stat__label">Edits</span></div>
<div class="stat"><span class="stat__num" data-count="1276">0</span><span class="stat__label">Active editors</span></div>
<div class="stat"><span class="stat__num" data-count="59">0</span><span class="stat__label">Files / hr</span></div>
<div class="stat stat--live"><span class="stat__dot" aria-hidden="true"></span><span class="stat__label">214 reading now</span></div>
</div>
</section>
<div class="container layout">
<div class="layout__main">
<!-- ============ FEATURED CHARACTER ROTATOR ============ -->
<section class="featured" id="featured" aria-label="Featured character">
<div class="section-head">
<h2 class="section-title">Featured of the Day</h2>
<div class="featured__nav">
<button class="rot-btn" id="rotPrev" type="button" aria-label="Previous featured">‹</button>
<button class="rot-btn" id="rotNext" type="button" aria-label="Next featured">›</button>
</div>
</div>
<article class="hl-card" id="hlCard" aria-live="polite">
<div class="hl-card__art" id="hlArt" data-art="seraphine" aria-hidden="true">
<div class="hl-portrait"></div>
</div>
<div class="hl-card__body">
<span class="hl-card__kicker" id="hlKicker">CHARACTER · HOUSE VALE</span>
<h3 class="hl-card__name" id="hlName">Seraphine Vale</h3>
<p class="hl-card__role" id="hlRole">The Ashbound Heir · Wielder of the Glass Crown</p>
<p class="hl-card__desc" id="hlDesc">
Last scion of the fallen House Vale, Seraphine bound her soul to the Ember Vow to
survive the Sundering. Equal parts diplomat and warlord, she unifies the broken
duchies under a single thorned banner.
</p>
<div class="hl-card__meta" id="hlMeta">
<span><strong>Allegiance</strong> The Thornwood Pact</span>
<span><strong>First seen</strong> Vol. I — Ashes of Vale</span>
<span><strong>Status</strong> Alive (Reign of Glass)</span>
</div>
<div class="hl-card__actions">
<a class="btn btn--primary" href="#" onclick="return false">Read full article</a>
<button class="btn btn--ghost" type="button" id="hlEditBtn">Edit</button>
</div>
</div>
</article>
<div class="featured__dots" id="rotDots" role="tablist" aria-label="Featured selector"></div>
</section>
<!-- ============ LORE CATEGORY CARDS ============ -->
<section class="lore" id="characters" aria-label="Explore the lore">
<div class="section-head">
<h2 class="section-title">Explore the Compendium</h2>
<a class="section-link" href="#" onclick="return false">All categories →</a>
</div>
<div class="lore__grid">
<a class="lore-card" href="#characters" data-glow="rose" id="factions">
<div class="lore-card__thumb thumb--characters" aria-hidden="true"></div>
<div class="lore-card__body">
<h3 class="lore-card__title">Characters</h3>
<p class="lore-card__desc">Heroes, villains, and the ghosts between — 612 profiled souls.</p>
<span class="lore-card__count">612 pages</span>
</div>
</a>
<a class="lore-card" href="#characters" data-glow="cyan">
<div class="lore-card__thumb thumb--factions" aria-hidden="true"></div>
<div class="lore-card__body">
<h3 class="lore-card__title">Factions & Houses</h3>
<p class="lore-card__desc">Pacts, duchies, and cults vying for the Glass Throne.</p>
<span class="lore-card__count">148 pages</span>
</div>
</a>
<a class="lore-card" href="#characters" data-glow="amber" id="locations">
<div class="lore-card__thumb thumb--locations" aria-hidden="true"></div>
<div class="lore-card__body">
<h3 class="lore-card__title">Locations</h3>
<p class="lore-card__desc">From Thornwood's canopy halls to the drowned vaults of Mire.</p>
<span class="lore-card__count">237 pages</span>
</div>
</a>
<a class="lore-card" href="#characters" data-glow="violet">
<div class="lore-card__thumb thumb--items" aria-hidden="true"></div>
<div class="lore-card__body">
<h3 class="lore-card__title">Relics & Items</h3>
<p class="lore-card__desc">The Glass Crown, the Ember Vow, and 400+ wieldable artifacts.</p>
<span class="lore-card__count">409 pages</span>
</div>
</a>
</div>
</section>
</div>
<!-- ============ RIGHT RAIL ============ -->
<aside class="layout__rail" aria-label="Wiki activity">
<section class="panel" id="trending">
<h2 class="panel__title"><span class="panel__icon">▲</span> Trending pages</h2>
<ol class="trend-list">
<li><a href="#" onclick="return false"><span class="trend-list__rank">1</span><span class="trend-list__name">The Glass Crown</span><span class="trend-list__hits">+412%</span></a></li>
<li><a href="#" onclick="return false"><span class="trend-list__rank">2</span><span class="trend-list__name">Seraphine Vale</span><span class="trend-list__hits">+188%</span></a></li>
<li><a href="#" onclick="return false"><span class="trend-list__rank">3</span><span class="trend-list__name">The Sundering</span><span class="trend-list__hits">+97%</span></a></li>
<li><a href="#" onclick="return false"><span class="trend-list__rank">4</span><span class="trend-list__name">House Vale</span><span class="trend-list__hits">+64%</span></a></li>
<li><a href="#" onclick="return false"><span class="trend-list__rank">5</span><span class="trend-list__name">Mire of Echoes</span><span class="trend-list__hits">+51%</span></a></li>
</ol>
</section>
<section class="panel">
<h2 class="panel__title"><span class="panel__icon">✦</span> Recent activity</h2>
<ul class="feed" id="activityFeed" aria-live="polite">
<li class="feed__item">
<span class="feed__avatar" data-c="a">L</span>
<div class="feed__body">
<p><strong>Lorekeeper_Vael</strong> edited <a href="#" onclick="return false">The Glass Crown</a></p>
<span class="feed__meta"><em class="feed__diff feed__diff--pos">+412</em> · 2m ago</span>
</div>
</li>
<li class="feed__item">
<span class="feed__avatar" data-c="b">M</span>
<div class="feed__body">
<p><strong>MireWalker</strong> created <a href="#" onclick="return false">Vault of Drowned Kings</a></p>
<span class="feed__meta"><em class="feed__diff feed__diff--pos">+1,840</em> · 14m ago</span>
</div>
</li>
<li class="feed__item">
<span class="feed__avatar" data-c="c">T</span>
<div class="feed__body">
<p><strong>ThornScribe</strong> reverted <a href="#" onclick="return false">House Vale</a></p>
<span class="feed__meta"><em class="feed__diff feed__diff--neg">−96</em> · 31m ago</span>
</div>
</li>
<li class="feed__item">
<span class="feed__avatar" data-c="d">A</span>
<div class="feed__body">
<p><strong>AshboundAria</strong> uploaded <a href="#" onclick="return false">map-thornwood-canopy.png</a></p>
<span class="feed__meta"><em class="feed__diff feed__diff--pos">+1 file</em> · 48m ago</span>
</div>
</li>
</ul>
</section>
</aside>
</div>
<!-- ============ COMMUNITY BAND ============ -->
<section class="community" id="community" aria-label="Join the community">
<div class="community__inner">
<div class="community__text">
<h2 class="community__title">Join 41,200 lorekeepers</h2>
<p class="community__desc">
Theorycraft the Reign of Glass, settle canon debates, and help map the Mire —
our editors and the dev team gather in the official Discord every weekend.
</p>
<div class="community__actions">
<button class="btn btn--discord" type="button" id="discordBtn">
<svg viewBox="0 0 24 18" width="22" height="16" aria-hidden="true"><path fill="currentColor" d="M20 1.5A18 18 0 0 0 15.4 0l-.3.5a14 14 0 0 1 4 2 17 17 0 0 0-14.3 0 14 14 0 0 1 4-2L8.6 0A18 18 0 0 0 4 1.5C1 6 .2 10.4.6 14.7A18 18 0 0 0 6 17.4l.7-1a12 12 0 0 1-1.9-.9l.5-.4a13 13 0 0 0 11.4 0l.5.4c-.6.4-1.2.7-1.9.9l.7 1a18 18 0 0 0 5.4-2.7c.5-5-.8-9.4-3.4-13.2ZM8.4 12c-.9 0-1.6-.8-1.6-1.8s.7-1.8 1.6-1.8 1.6.8 1.6 1.8S9.2 12 8.4 12Zm7.2 0c-.9 0-1.6-.8-1.6-1.8s.7-1.8 1.6-1.8 1.6.8 1.6 1.8-.7 1.8-1.6 1.8Z"/></svg>
Join the Discord
</button>
<button class="btn btn--ghost btn--on-dark" type="button" id="newsletterBtn">Get the lore digest</button>
</div>
</div>
<div class="community__stats">
<div class="cstat"><span class="cstat__num">41.2k</span><span class="cstat__label">members</span></div>
<div class="cstat"><span class="cstat__num">312</span><span class="cstat__label">online now</span></div>
<div class="cstat"><span class="cstat__num">88</span><span class="cstat__label">channels</span></div>
</div>
</div>
</section>
</main>
<footer class="footer">
<div class="footer__inner">
<p>Verdant Empire Wiki is a fan-built archive. Lore, names, and data are fictional.</p>
<nav class="footer__links" aria-label="Footer">
<a href="#" onclick="return false">About</a>
<a href="#" onclick="return false">Style guide</a>
<a href="#" onclick="return false">Recent changes</a>
<a href="#" onclick="return false">License (MIT)</a>
</nav>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<div class="nav-scrim" id="navScrim" hidden></div>
<script src="script.js"></script>
</body>
</html>Fandom / Game Wiki Landing
A lore-immersive, Fandom-style home page for the fictional Verdant Empire game universe. The dark hero stacks several CSS-drawn parallax layers — a star-flecked sky, a distant moon, mountain ridges, ruined spires, and a glowing forest canopy — behind a gradient-clipped display title and a large search bar. A wiki-stats bar reports pages, edits, active editors, and a live “reading now” indicator, with each number counting up as it scrolls into view.
The search bar is the centerpiece: typing live-filters a small index of characters, factions, locations, relics, and events and previews up to six matches in an ARIA combobox dropdown, highlighting the typed query in each title. Arrow keys move the active option, Enter opens it, Esc closes, and pressing / anywhere focuses the field. Popular chips drop a term in and run the query. Below, four featured-lore panels hover-glow in their own accent colour, an auto-rotating “Featured of the Day” spotlight cross-fades between three characters (with prev/next, dots, and hover-to-pause), and a right rail pairs a trending-pages ranking with a recent-activity feed that pushes new edits on a ticker.
Everything is vanilla HTML, CSS, and JavaScript with no dependencies. The hero parallaxes to pointer movement, semantic landmarks and visible focus rings keep it keyboard-usable, motion respects prefers-reduced-motion, and the layout collapses gracefully to ~360px — the nav becomes a drawer, the rail stacks under the main column, and the spotlight reflows to a single column.
Illustrative UI only — fictional articles, products, and data.