This commit is contained in:
oliver
2026-06-24 06:55:38 -03:00
parent 84d8513db1
commit fa3694bd58
10 changed files with 2198 additions and 0 deletions
+283
View File
@@ -638,6 +638,186 @@
transform: rotate(360deg);
}
}
/* ---- Blog Section ---- */
.blog-section {
padding: 3rem 0;
background: #08080c;
color: #e8e8f0;
}
.blog-section h2 {
color: #e8e8f0;
}
.blog-search-wrap {
max-width: 500px;
margin: 0 auto 2rem;
}
.blog-search {
width: 100%;
padding: 0.75rem 1rem;
font-size: 0.85rem;
background: #12121c;
border: 1px solid #1a1a2e;
border-radius: 0.5rem;
color: #e8e8f0;
font-family: Inter, sans-serif;
transition: border-color 0.2s;
outline: none;
}
.blog-search:focus {
border-color: #00f5ff;
box-shadow: 0 0 0 2px rgba(0,245,255,0.15);
}
.blog-search::placeholder {
color: rgba(232,232,240,0.35);
}
.blog-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.25rem;
margin-bottom: 2rem;
}
@media (max-width: 992px) {
.blog-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 576px) {
.blog-grid {
grid-template-columns: 1fr;
}
}
.blog-card {
background: #12121c;
border: 1px solid #1a1a2e;
border-radius: 0.6rem;
padding: 1.25rem;
display: flex;
flex-direction: column;
transition: transform 0.2s, border-color 0.2s, box-shadow 0.2s;
}
.blog-card:hover {
transform: translateY(-2px);
border-color: #00f5ff;
box-shadow: 0 4px 20px rgba(0,245,255,0.08);
}
.blog-card-top {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 0.75rem;
flex-wrap: wrap;
}
.blog-badge {
display: inline-block;
padding: 0.15rem 0.5rem;
border-radius: 3rem;
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: #08080c;
}
.blog-badge-speed-run {
background: #f472b6;
}
.blog-badge-guide {
background: #a78bfa;
}
.blog-badge-howto {
background: #00f5ff;
}
.blog-badge-news {
background: #ffaa00;
}
.blog-badge-default {
background: #a78bfa;
}
.blog-agent {
display: inline-block;
padding: 0.15rem 0.45rem;
border-radius: 3rem;
font-size: 0.56rem;
font-weight: 600;
background: rgba(0,245,255,0.15);
color: #00f5ff;
border: 1px solid rgba(0,245,255,0.25);
}
.blog-date {
font-size: 0.7rem;
color: rgba(232,232,240,0.45);
margin-bottom: 0.4rem;
}
.blog-card h3 {
font-family: 'Inter Tight', sans-serif;
font-size: 0.95rem;
font-weight: 600;
margin: 0 0 0.5rem;
line-height: 1.35;
}
.blog-card h3 a {
color: #e8e8f0;
text-decoration: none;
transition: color 0.2s;
}
.blog-card h3 a:hover {
color: #00f5ff;
}
.blog-teaser {
font-size: 0.78rem;
color: rgba(232,232,240,0.65);
line-height: 1.55;
margin-bottom: 1rem;
flex: 1;
}
.blog-readmore {
font-size: 0.75rem;
color: #00f5ff;
text-decoration: none;
font-weight: 500;
transition: opacity 0.2s;
margin-top: auto;
}
.blog-readmore:hover {
opacity: 0.75;
}
.blog-empty {
grid-column: 1 / -1;
text-align: center;
padding: 2rem;
color: rgba(232,232,240,0.5);
font-size: 0.9rem;
}
.blog-pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 1rem 0;
}
.blog-page-btn {
padding: 0.4rem 1rem;
font-size: 0.78rem;
background: #12121c;
border: 1px solid #1a1a2e;
border-radius: 0.4rem;
color: #e8e8f0;
cursor: pointer;
font-family: Inter, sans-serif;
transition: border-color 0.2s, background 0.2s;
}
.blog-page-btn:hover:not(:disabled) {
border-color: #00f5ff;
background: rgba(0,245,255,0.08);
}
.blog-page-btn:disabled {
opacity: 0.3;
cursor: default;
}
.blog-page-info {
font-size: 0.78rem;
color: rgba(232,232,240,0.55);
}
</style>
</head>
<body>
@@ -1224,6 +1404,20 @@
</div>
</section>
<section class="blog-section" id="blog">
<div class="container">
<div class="text-center mb-4">
<div class="hr"></div>
<h2>Latest from the Blog</h2>
</div>
<div class="blog-search-wrap">
<input type="text" class="blog-search" id="blogSearch" placeholder="Search posts..." />
</div>
<div class="blog-grid" id="blogGrid"></div>
<div class="blog-pagination" id="blogPagination"></div>
</div>
</section>
<footer class="footer">
<div class="container">
<div class="fgrid">
@@ -1463,6 +1657,95 @@
.classList.add("active");
});
});
/* ---- Blog Module ---- */
var blogState = {
allPosts: [],
filteredPosts: [],
currentPage: 1,
perPage: 6,
searchTerm: ""
};
function formatDate(dateStr) {
var d = new Date(dateStr + "T00:00:00Z");
var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
return months[d.getUTCMonth()] + " " + d.getUTCDate() + ", " + d.getUTCFullYear();
}
function badgeClass(area) {
var m = {"speed-run":"speed-run","guide":"guide","howto":"howto","news":"news"};
return m[area] || "default";
}
function renderBlog() {
var grid = document.getElementById("blogGrid");
var search = (blogState.searchTerm || "").toLowerCase();
blogState.filteredPosts = blogState.allPosts.filter(function(p) {
if (!search) return true;
return (p.headline + " " + p.teaser).toLowerCase().indexOf(search) !== -1;
});
var total = blogState.filteredPosts.length;
var totalPages = Math.max(1, Math.ceil(total / blogState.perPage));
if (blogState.currentPage > totalPages) blogState.currentPage = totalPages;
var start = (blogState.currentPage - 1) * blogState.perPage;
var pagePosts = blogState.filteredPosts.slice(start, start + blogState.perPage);
var html = "";
if (pagePosts.length === 0) {
html = "<div class=\"blog-empty\">No posts match your search.</div>";
} else {
for (var i = 0; i < pagePosts.length; i++) {
var p = pagePosts[i];
var bc = badgeClass(p.area);
var agentHtml = "";
if (p.agent) agentHtml = "<span class=\"blog-agent\">" + p.agent + "</span>";
html += "<div class=\"blog-card\">" +
"<div class=\"blog-card-top\">" +
"<span class=\"blog-badge blog-badge-" + bc + "\">" + p.area + "</span>" +
agentHtml +
"</div>" +
"<div class=\"blog-date\">" + formatDate(p.date) + "</div>" +
"<h3><a href=\"" + p.link + "\">" + p.headline + "</a></h3>" +
"<p class=\"blog-teaser\">" + p.teaser + "</p>" +
"<a href=\"" + p.link + "\" class=\"blog-readmore\">Read more &rarr;</a>" +
"</div>";
}
}
grid.innerHTML = html;
var pag = document.getElementById("blogPagination");
var pagHtml = "";
if (totalPages > 1) {
pagHtml += "<button class=\"blog-page-btn\" onclick=\"blogGo(1)\"" + (blogState.currentPage <= 1 ? " disabled\"" : "\"") + ">&laquo;</button>";
pagHtml += "<button class=\"blog-page-btn\" onclick=\"blogGo(" + (blogState.currentPage - 1) + ")\"" + (blogState.currentPage <= 1 ? " disabled\"" : "\"") + ">&lsaquo; Prev</button>";
pagHtml += "<span class=\"blog-page-info\">Page " + blogState.currentPage + " of " + totalPages + "</span>";
pagHtml += "<button class=\"blog-page-btn\" onclick=\"blogGo(" + (blogState.currentPage + 1) + ")\"" + (blogState.currentPage >= totalPages ? " disabled\"" : "\"") + ">Next &rsaquo;</button>";
pagHtml += "<button class=\"blog-page-btn\" onclick=\"blogGo(" + totalPages + ")\"" + (blogState.currentPage >= totalPages ? " disabled\"" : "\"") + ">&raquo;</button>";
}
pag.innerHTML = pagHtml;
}
function blogGo(page) {
blogState.currentPage = page;
renderBlog();
}
fetch("blog/index.json?_=" + Date.now())
.then(function(r) { return r.json(); })
.then(function(data) {
blogState.allPosts = data.posts;
renderBlog();
})
.catch(function() {
document.getElementById("blogGrid").innerHTML = "<div class=\"blog-empty\">Could not load blog posts.</div>";
});
document.getElementById("blogSearch").addEventListener("input", function() {
blogState.searchTerm = this.value;
blogState.currentPage = 1;
renderBlog();
});
</script>
</body>
</html>