Polish frontend hero consistency and annual ranking layout

This commit is contained in:
devRaGonSa
2026-06-09 14:30:33 +02:00
parent 5355f84c5d
commit 88964d6126
11 changed files with 392 additions and 116 deletions

View File

@@ -1,10 +1,10 @@
@media (min-width: 1121px) {
.hero:not(.historical-hero) .hero__content {
.hero--public-page:not(.historical-hero) .hero__content {
padding-block: 54px 60px;
padding-inline: clamp(64px, 5.5vw, 104px);
}
.hero:not(.historical-hero) .hero__brand {
.hero--public-page:not(.historical-hero) .hero__brand {
width: 100%;
max-width: 1420px;
margin: 0 auto;
@@ -14,7 +14,7 @@
align-items: center;
}
.hero:not(.historical-hero) .logo-frame {
.hero--public-page:not(.historical-hero) .logo-frame {
grid-column: 1;
justify-self: start;
width: min(470px, 100%);
@@ -22,11 +22,11 @@
padding: 30px 34px;
}
.hero:not(.historical-hero) .logo-frame__image {
.hero--public-page:not(.historical-hero) .logo-frame__image {
max-height: 340px;
}
.hero:not(.historical-hero) .hero__copy {
.hero--public-page:not(.historical-hero) .hero__copy {
grid-column: 3;
justify-self: end;
width: 100%;
@@ -37,41 +37,41 @@
transform: none;
}
.hero:not(.historical-hero) .hero__title {
.hero--public-page:not(.historical-hero) .hero__title {
max-width: none;
font-size: clamp(4.2rem, 5.2vw, 6.35rem);
line-height: 1.02;
}
.hero:not(.historical-hero) .hero__title-accent {
.hero--public-page:not(.historical-hero) .hero__title-accent {
display: inline;
}
.hero:not(.historical-hero) .hero__text {
.hero--public-page:not(.historical-hero) .hero__text {
max-width: 68ch;
}
}
@media (min-width: 1121px) and (max-width: 1380px) {
.hero:not(.historical-hero) .hero__content {
.hero--public-page:not(.historical-hero) .hero__content {
padding-inline: clamp(44px, 4vw, 64px);
}
.hero:not(.historical-hero) .hero__brand {
.hero--public-page:not(.historical-hero) .hero__brand {
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
column-gap: clamp(20px, 2.5vw, 40px);
}
.hero:not(.historical-hero) .logo-frame {
.hero--public-page:not(.historical-hero) .logo-frame {
width: min(390px, 100%);
min-height: 316px;
}
.hero:not(.historical-hero) .logo-frame__image {
.hero--public-page:not(.historical-hero) .logo-frame__image {
max-height: 298px;
}
.hero:not(.historical-hero) .hero__copy {
.hero--public-page:not(.historical-hero) .hero__copy {
grid-column: 3;
justify-self: end;
width: 100%;
@@ -79,14 +79,14 @@
transform: none;
}
.hero:not(.historical-hero) .hero__title {
.hero--public-page:not(.historical-hero) .hero__title {
font-size: clamp(3.7rem, 5.35vw, 5.15rem);
}
}
@media (max-width: 1120px) {
.hero:not(.historical-hero) .hero__copy {
.hero--public-page:not(.historical-hero) .hero__copy {
grid-column: auto;
transform: none;
}
}
}

View File

@@ -7,8 +7,8 @@
}
.historical-hero__content {
padding-top: 44px;
padding-bottom: 46px;
padding-top: 58px;
padding-bottom: 64px;
}
.historical-hero__topline {
@@ -21,7 +21,7 @@
.historical-hero__copy {
display: grid;
gap: 24px;
gap: 18px;
min-width: 0;
}
@@ -34,7 +34,7 @@
.historical-hero__layout--registry {
grid-template-columns: minmax(220px, 330px) minmax(0, 1fr);
column-gap: clamp(72px, 7vw, 140px);
column-gap: 36px;
}
.historical-hero__layout > * {
@@ -42,8 +42,8 @@
}
.historical-logo-frame {
width: min(300px, 100%);
min-height: 220px;
width: min(340px, 100%);
min-height: 248px;
}
.historical-hero__title {
@@ -65,6 +65,11 @@
/* Registry/historico hero: logo left, real flexible spacer, copy anchored right. */
@media (min-width: 1121px) {
.hero--public-page.historical-hero .historical-hero__content {
padding-block: 54px 60px;
padding-inline: clamp(64px, 5.5vw, 104px);
}
.historical-hero__layout--registry {
width: 100%;
max-width: 1420px;
@@ -113,6 +118,10 @@
}
@media (min-width: 1121px) and (max-width: 1380px) {
.hero--public-page.historical-hero .historical-hero__content {
padding-inline: clamp(44px, 4vw, 64px);
}
.historical-hero__layout--registry {
grid-template-columns: minmax(320px, 390px) minmax(28px, 1fr) minmax(560px, 720px);
column-gap: clamp(20px, 2.5vw, 40px);

View File

@@ -112,7 +112,9 @@ a {
color: var(--text-soft);
font-size: 0.82rem;
font-weight: 800;
letter-spacing: 0.12em;
letter-spacing: 0.1em;
line-height: 1.2;
text-align: center;
text-transform: uppercase;
transition:
transform 160ms ease,
@@ -904,6 +906,15 @@ h2 {
padding-inline: 18px;
}
.clan-card--bxb .clan-card__brand {
grid-template-columns: minmax(112px, 138px) minmax(0, 1fr);
}
.clan-card__logo--bxb {
min-height: 108px;
padding: 10px;
}
.clan-card__logo img {
width: auto;
height: auto;
@@ -912,6 +923,10 @@ h2 {
object-fit: contain;
}
.clan-card__logo--bxb img {
max-height: 100px;
}
.clan-card__logo-placeholder {
width: 100%;
min-height: 84px;
@@ -972,11 +987,6 @@ h2 {
margin-top: 8px;
}
.stats-hero .hero__brand,
.ranking-hero .hero__brand {
align-items: center;
}
.stats-search-form {
margin: 0 0 14px;
display: grid;
@@ -1098,26 +1108,104 @@ h2 {
.stats-annual-table-wrap {
display: grid;
gap: 10px;
gap: 16px;
}
.stats-annual-meta {
display: grid;
gap: 4px;
margin: 0 0 8px;
gap: 12px;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
margin: 0;
}
.stats-annual-item {
padding: 10px 12px;
border: 1px solid rgba(159, 168, 141, 0.22);
border-radius: 14px;
margin-bottom: 8px;
.stats-annual-card {
border: 1px solid rgba(159, 168, 141, 0.2);
border-radius: 18px;
padding: 18px;
background:
linear-gradient(180deg, rgba(28, 34, 25, 0.82), rgba(12, 15, 11, 0.92));
linear-gradient(180deg, rgba(28, 34, 25, 0.88), rgba(12, 15, 11, 0.96));
}
.stats-annual-item p {
margin: 0 0 6px;
.stats-annual-meta-item {
margin: 0;
padding: 14px 16px;
border: 1px solid rgba(159, 168, 141, 0.18);
border-radius: 16px;
background: rgba(15, 18, 13, 0.62);
}
.stats-annual-meta-item p,
.stats-annual-meta-item strong {
margin: 0;
}
.stats-annual-meta-item p {
margin-bottom: 8px;
color: var(--muted);
font-size: 0.74rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.stats-annual-table-shell {
overflow-x: auto;
border: 1px solid rgba(159, 168, 141, 0.18);
border-radius: 18px;
background: linear-gradient(180deg, rgba(19, 24, 16, 0.88), rgba(10, 13, 9, 0.96));
}
.stats-annual-table {
width: 100%;
min-width: 860px;
border-collapse: collapse;
}
.stats-annual-table th,
.stats-annual-table td {
padding: 14px 16px;
text-align: left;
border-bottom: 1px solid rgba(159, 168, 141, 0.12);
}
.stats-annual-table thead th {
color: var(--accent-strong);
font-size: 0.76rem;
letter-spacing: 0.08em;
text-transform: uppercase;
background: rgba(14, 18, 12, 0.96);
}
.stats-annual-table tbody tr:hover {
background: rgba(183, 201, 125, 0.05);
}
.stats-annual-table tbody tr:last-child td {
border-bottom: 0;
}
.stats-annual-rank {
color: var(--accent-warm);
font-weight: 700;
}
.stats-annual-player {
display: grid;
gap: 4px;
}
.stats-annual-player strong {
font-size: 1rem;
line-height: 1.35;
}
.stats-annual-player__id {
color: var(--muted);
font-size: 0.8rem;
}
.stats-annual-metric {
color: var(--accent-warm);
font-weight: 700;
}
.stats-annual-sub {
@@ -1499,8 +1587,8 @@ h2 {
.public-nav__link {
min-height: 46px;
padding-inline: 12px;
font-size: 0.74rem;
letter-spacing: 0.08em;
font-size: 0.72rem;
letter-spacing: 0.06em;
}
.stats-search-form__fields {
@@ -1525,6 +1613,11 @@ h2 {
padding: 12px 10px;
}
.stats-annual-table th,
.stats-annual-table td {
padding: 12px 10px;
}
.logo-frame {
min-height: 180px;
padding: 16px 18px;

View File

@@ -33,7 +33,7 @@ const COMMUNITY_CLANS = Object.freeze([
logoAlt: "Logo de La 129",
logoClassName: "clan-card__logo--wide",
discordUrl: "",
discordLabel: "Proximamente",
discordLabel: "Pr\u00f3ximamente",
},
{
name: "250 Hispania",
@@ -65,9 +65,10 @@ const COMMUNITY_CLANS = Object.freeze([
"Clan activo de la comunidad.",
logoSrc: "./assets/img/clans/bxb.png",
logoAlt: "Logo de BxB",
logoClassName: "",
logoClassName: "clan-card__logo--bxb",
cardClassName: "clan-card--bxb",
discordUrl: "",
discordLabel: "Proximamente",
discordLabel: "Pr\u00f3ximamente",
},
{
name: "7dv",
@@ -209,7 +210,7 @@ async function hydrateServers(
if (serversData.items.length === 0) {
serversList.innerHTML =
'<p class="servers-empty">Informacion de servidores disponible mas adelante.</p>';
'<p class="servers-empty">Informaci\u00f3n de servidores disponible m\u00e1s adelante.</p>';
return;
}
@@ -283,7 +284,7 @@ function renderServerStatsCard(server) {
{ label: "Mapa", value: currentMap, valueClassName: "server-card__quickfact-value--map" },
];
if (region) {
quickFactItems.push({ label: "Region", value: region });
quickFactItems.push({ label: "Regi\u00f3n", value: region });
}
const quickFacts = renderQuickFacts(quickFactItems);
@@ -345,7 +346,7 @@ function renderServerAction(server) {
return `
<div class="server-card__actions">
<a class="server-action-link" href="${escapeHtml(actions.historicalUrl)}">
Historico
Hist\u00f3rico
</a>
<a class="server-action-link" href="${escapeHtml(actions.currentMatchUrl)}">
Partida actual
@@ -367,9 +368,10 @@ function hydrateCommunityClans(listNode) {
function renderCommunityClanCard(clan) {
const logoMarkup = renderClanLogo(clan);
const discordMarkup = renderClanDiscordLink(clan);
const cardClassName = clan.cardClassName ? ` ${escapeHtml(clan.cardClassName)}` : "";
return `
<article class="clan-card">
<article class="clan-card${cardClassName}">
<div class="clan-card__brand">
${logoMarkup}
<div class="clan-card__copy">
@@ -541,7 +543,7 @@ function deriveSnapshotState(serversData) {
return {
label: isFresh
? `Actualizado ${timestampLabel}`
: `Ultimo snapshot ${timestampLabel}`,
: `\u00daltimo snapshot ${timestampLabel}`,
isFresh,
};
}

View File

@@ -7,7 +7,6 @@
const limitSelect = document.getElementById("ranking-limit");
const yearWrap = document.getElementById("ranking-year-wrap");
const yearInput = document.getElementById("ranking-year");
const backendStateNode = document.getElementById("ranking-backend-state");
const stateNode = document.getElementById("ranking-state");
const titleNode = document.getElementById("ranking-title");
const metaNode = document.getElementById("ranking-meta");
@@ -48,7 +47,6 @@
applyInitialUrlState();
toggleYearField();
syncMetricState();
setBackendState("Comprobando disponibilidad del backend", false);
setRankingState("neutral", "Esperando filtros para cargar el ranking global.");
clearRankingSurface();
refreshBackendHealth();
@@ -90,16 +88,6 @@
});
}
function setBackendState(label, online) {
isBackendOnline = online;
if (!backendStateNode) {
return;
}
backendStateNode.textContent = label;
backendStateNode.classList.toggle("status-chip--ok", online);
backendStateNode.classList.toggle("status-chip--fallback", !online);
}
function setRankingState(state, message) {
if (!stateNode) {
return;
@@ -192,7 +180,7 @@
}
setFilterNote(
"Ranking compara top globales. Para buscar un jugador concreto usa Stats.",
"Ranking compara top globales. Para buscar un jugador concreto usa Estadísticas.",
"neutral",
);
}
@@ -245,7 +233,7 @@
if (!payload || payload.status !== "ok") {
throw new Error("Unexpected health payload");
}
setBackendState("Backend operativo", true);
isBackendOnline = true;
if (!String(stateNode?.textContent || "").includes("limite del URL")) {
setRankingState(
"neutral",
@@ -255,7 +243,7 @@
void loadRanking();
} catch (error) {
console.warn("Ranking health check failed", error);
setBackendState("Backend no disponible", false);
isBackendOnline = false;
setRankingState("error", "Backend no disponible. El ranking queda en estado offline.");
renderEmptyState(
"No fue posible contactar el backend. Cuando vuelva a estar disponible podras consultar semanal, mensual o anual.",
@@ -319,7 +307,7 @@
renderRanking(payload.data || {});
} catch (error) {
console.warn("Ranking request failed", error);
setBackendState("Backend no disponible", false);
isBackendOnline = false;
setRankingState("error", "Error controlado al cargar el ranking.");
renderEmptyState(
"La lectura del ranking fall\u00f3 en este intento. Revisa el backend o actualiza la p\u00e1gina.",

View File

@@ -5,7 +5,6 @@
const searchStateNode = document.getElementById("stats-search-state");
const searchHelpNode = document.getElementById("stats-search-help");
const resultListNode = document.getElementById("stats-result-list");
const backendStateNode = document.getElementById("stats-backend-state");
const profilePanel = document.getElementById("stats-profile-panel");
const profileTitle = document.getElementById("stats-profile-title");
const profileStateNode = document.getElementById("stats-profile-state");
@@ -25,9 +24,6 @@
const annualServerId = "all";
const messages = {
backendChecking: "Comprobando disponibilidad del backend",
backendOnline: "Backend operativo",
backendUnavailable: "Backend no disponible. Reintenta en unos segundos.",
backendUnavailableForSearch:
"Backend no disponible. No se pueden buscar jugadores ni cargar su perfil.",
backendUnavailableForAnnual:
@@ -48,14 +44,14 @@
profileError: "No fue posible cargar las estad\u00edsticas del jugador.",
annualLoading: "Cargando ranking anual...",
annualMissing:
"No hay snapshot generado para el a\u00f1o solicitado. Mostrar\u00e1 estado pending hasta que exista.",
"Todavia no hay ranking anual publicado para el a\u00f1o solicitado.",
annualReadyEmpty:
"Ranking anual listo, pero sin datos para el a\u00f1o y servidor seleccionados.",
"El ranking anual ya esta disponible, pero no muestra filas para ese filtro.",
annualUnsupportedMetric:
"La m\u00e9trica anual solicitada no est\u00e1 soportada en V1.",
annualMetricInvalid:
"Par\u00e1metro de ranking anual inv\u00e1lido. Usa metric=kills en V1.",
annualReadyPrefix: "Ranking anual listo para",
annualReadyPrefix: "Ranking anual disponible para",
weeklyPlaceholder:
"Sin datos semanales. El ranking semanal se actualiza al cargar un jugador.",
monthlyPlaceholder:
@@ -82,7 +78,6 @@
"El ranking anual se consulta por separado con el a\u00f1o indicado.";
}
setBackendState(messages.backendChecking, false);
if (annualYearInput) {
annualYearInput.value = String(annualDefaultYear);
}
@@ -108,18 +103,8 @@
});
}
function setBackendState(label, isOnline) {
isBackendOnline = isOnline;
if (!backendStateNode) {
return;
}
backendStateNode.textContent = label;
backendStateNode.classList.toggle("status-chip--ok", isOnline);
backendStateNode.classList.toggle("status-chip--fallback", !isOnline);
}
function markAsBackendUnavailable() {
setBackendState(messages.backendUnavailable, false);
isBackendOnline = false;
if (searchStateNode) {
searchStateNode.textContent = messages.backendUnavailableForSearch;
searchStateNode.className = "stats-state stats-state--error";
@@ -173,10 +158,10 @@
throw new Error("Unexpected health payload");
}
setBackendState(messages.backendOnline, true);
isBackendOnline = true;
setAnnualState(
"neutral",
"Backend disponible. Selecciona un a\u00f1o para cargar el ranking anual (kills).",
"Backend disponible. Selecciona un a\u00f1o para cargar el ranking anual.",
);
void loadAnnualRanking();
} catch (error) {
@@ -345,17 +330,33 @@
setAnnualState(
"neutral",
`${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}, m\u00e9trica ${metric}`,
`${messages.annualReadyPrefix} ${serverId}, a\u00f1o ${year}.`,
);
annualContentNode.innerHTML = `
<article class="stats-summary-card">
<article class="stats-annual-card">
<p class="stats-summary-title">Top ${limit} anual</p>
<div class="stats-annual-meta">
<p><strong>Servidor:</strong> ${escapeHtml(serverId)}</p>
<p><strong>M\u00e9trica:</strong> ${escapeHtml(metric)}</p>
<p><strong>Partidas fuente:</strong> ${safeInt(sourceMatches, 0)}</p>
<p><strong>Actualizado:</strong> ${escapeHtml(generatedAt || "No disponible")}</p>
<article class="stats-annual-meta-item">
<p>Servidor</p>
<strong>${escapeHtml(serverId)}</strong>
</article>
<article class="stats-annual-meta-item">
<p>A\u00f1o</p>
<strong>${year}</strong>
</article>
<article class="stats-annual-meta-item">
<p>Lectura</p>
<strong>${escapeHtml(metric)}</strong>
</article>
<article class="stats-annual-meta-item">
<p>Partidas base</p>
<strong>${safeInt(sourceMatches, 0)}</strong>
</article>
<article class="stats-annual-meta-item">
<p>Actualizado</p>
<strong>${escapeHtml(generatedAt || "No disponible")}</strong>
</article>
</div>
${renderAnnualRows(items)}
</article>
@@ -363,27 +364,54 @@
}
function renderAnnualRows(items) {
return items
const rowsMarkup = items
.map((item) => {
const rank = safeInt(item.ranking_position, 0);
const playerId = escapeHtml(String(item.player_id || ""));
const playerName = escapeHtml(String(item.player_name || "Jugador sin nombre"));
const metricValue = safeInt(item.metric_value, 0);
const matches = safeInt(item.matches_considered, 0);
const kills = safeInt(item.kills, 0);
const deaths = safeInt(item.deaths, 0);
const teamkills = safeInt(item.teamkills, 0);
const kd = safeDecimal(item.kd_ratio, 2, "0.00");
return `
<article class="stats-annual-item">
<p><strong>#${rank}</strong> ${playerName} <span class="stats-annual-sub">(ID: ${playerId})</span></p>
<p><strong>Valor:</strong> ${metricValue} - <strong>Partidas:</strong> ${matches}</p>
<p><strong>Kills:</strong> ${kills} - <strong>Deaths:</strong> ${deaths} - <strong>Teamkills:</strong> ${teamkills} - <strong>K/D:</strong> ${kd}</p>
</article>
<tr>
<td class="stats-annual-rank">#${rank}</td>
<td>
<div class="stats-annual-player">
<strong>${playerName}</strong>
<span class="stats-annual-player__id">ID ${playerId}</span>
</div>
</td>
<td class="stats-annual-metric">${metricValue}</td>
<td>${matches}</td>
<td>${deaths}</td>
<td>${teamkills}</td>
<td>${kd}</td>
</tr>
`;
})
.join("");
return `
<div class="stats-annual-table-shell">
<table class="stats-annual-table">
<thead>
<tr>
<th>Posici\u00f3n</th>
<th>Jugador</th>
<th>Valor / Kills</th>
<th>Partidas</th>
<th>Muertes</th>
<th>Teamkills</th>
<th>K/D</th>
</tr>
</thead>
<tbody>${rowsMarkup}</tbody>
</table>
</div>
`;
}
function renderResultItem(item) {
@@ -508,7 +536,6 @@
);
}
setBackendState(messages.backendOnline, true);
isBackendOnline = true;
} catch (error) {
console.warn("Player profile failed", error);

View File

@@ -21,13 +21,13 @@
Hist&oacute;rico
</a>
<a class="public-nav__link" href="./stats.html">
Stats
Estad&iacute;sticas
</a>
<a class="public-nav__link" href="./ranking.html">
Ranking
</a>
</nav>
<header class="hero historical-hero">
<header class="hero hero--public-page historical-hero">
<div class="hero__overlay"></div>
<div class="hero__content historical-hero__content">
<div class="historical-hero__layout historical-hero__layout--registry">
@@ -43,7 +43,6 @@
</div>
<div class="historical-hero__copy">
<div>
<p class="eyebrow eyebrow--section">Hist&oacute;rico propio</p>
<h1 class="historical-hero__title">
Registro de la
<span class="hero__title-accent">Comunidad Hispana</span>

View File

@@ -24,13 +24,13 @@
Histórico
</a>
<a class="public-nav__link" href="./stats.html">
Stats
Estadísticas
</a>
<a class="public-nav__link" href="./ranking.html">
Ranking
</a>
</nav>
<header class="hero">
<header class="hero hero--public-page">
<div class="hero__overlay"></div>
<div class="hero__content">
<div class="hero__brand">

View File

@@ -9,6 +9,7 @@
/>
<title>Ranking - HLL Vietnam</title>
<link rel="stylesheet" href="./assets/css/styles.css" />
<link rel="stylesheet" href="./assets/css/hero-header-compact.css" />
</head>
<body data-backend-base-url="http://127.0.0.1:8000">
<div class="page-shell stats-page-shell">
@@ -20,13 +21,13 @@
Hist&oacute;rico
</a>
<a class="public-nav__link" href="./stats.html">
Stats
Estad&iacute;sticas
</a>
<a class="public-nav__link is-active" href="./ranking.html" aria-current="page">
Ranking
</a>
</nav>
<header class="hero stats-hero ranking-hero">
<header class="hero hero--public-page stats-hero ranking-hero">
<div class="hero__overlay"></div>
<div class="hero__content">
<div class="hero__brand">
@@ -41,7 +42,6 @@
/>
</div>
<div class="hero__copy">
<p class="eyebrow eyebrow--section">Secci&oacute;n Ranking</p>
<h1 class="hero__title">
Ranking
<span class="hero__title-accent">Global</span>
@@ -62,7 +62,7 @@
<h2>Top p&uacute;blico por ventana</h2>
</div>
<p class="panel__intro panel__intro--tight">
El ranking expone la lectura p&uacute;blica de l&iacute;deres. Para b&uacute;squeda individual usa la secci&oacute;n Stats.
El ranking expone la lectura p&uacute;blica de l&iacute;deres. Para b&uacute;squeda individual usa Estad&iacute;sticas.
</p>
<form class="ranking-form" id="ranking-form">
<div class="ranking-form__grid">
@@ -123,7 +123,7 @@
<div class="hero__actions ranking-form__actions">
<button class="discord-button" type="submit">Actualizar ranking</button>
<a class="secondary-button secondary-button--compact" href="./stats.html">
Buscar jugador en Stats
Buscar jugador en Estad&iacute;sticas
</a>
</div>
</form>

View File

@@ -5,10 +5,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="Consulta estad&iacute;sticas personales de jugadores en el panel Stats de HLL Vietnam."
content="Consulta estad&iacute;sticas personales de jugadores en la zona p&uacute;blica de HLL Vietnam."
/>
<title>Stats - HLL Vietnam</title>
<title>Estad&iacute;sticas - HLL Vietnam</title>
<link rel="stylesheet" href="./assets/css/styles.css" />
<link rel="stylesheet" href="./assets/css/hero-header-compact.css" />
</head>
<body data-backend-base-url="http://127.0.0.1:8000">
<div class="page-shell stats-page-shell">
@@ -20,13 +21,13 @@
Hist&oacute;rico
</a>
<a class="public-nav__link is-active" href="./stats.html" aria-current="page">
Stats
Estad&iacute;sticas
</a>
<a class="public-nav__link" href="./ranking.html">
Ranking
</a>
</nav>
<header class="hero stats-hero">
<header class="hero hero--public-page stats-hero">
<div class="hero__overlay"></div>
<div class="hero__content">
<div class="hero__brand">
@@ -41,7 +42,6 @@
/>
</div>
<div class="hero__copy">
<p class="eyebrow eyebrow--section">Secci&oacute;n Stats</p>
<h1 class="hero__title">
Estad&iacute;sticas
<span class="hero__title-accent">Personales</span>
@@ -126,7 +126,7 @@
<h2>Top 20 anual</h2>
</div>
<p class="panel__intro panel__intro--tight">
El ranking anual usa snapshots pre-computados para evitar recalcular toda la temporada en cada consulta.
El ranking anual usa un corte ya preparado de la temporada para cargar el Top 20 sin recalcular toda la campa&ntilde;a en cada consulta.
</p>
<form class="stats-annual-form" id="stats-annual-form">
<label class="stats-search-form__label" for="stats-annual-year">A&ntilde;o</label>