diff --git a/blog/ETL-pipeline-tutorial/index.md b/blog/ETL-pipeline-tutorial/index.md
index e32aa1e8e..2270f992b 100644
--- a/blog/ETL-pipeline-tutorial/index.md
+++ b/blog/ETL-pipeline-tutorial/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Azure Data Factory — Build Your First ETL"
tags: [azure-data-factory, adf, etl, data-pipeline, data-engineering, azure, blob-storage, adls, copy-activity, linked-service, dataset, trigger]
date: 2026-05-06
+image: /img/blogs/adf-cover_image.png
+category: TUTORIAL
description: Azure Data Factory is Microsoft's cloud-native ETL service — a visual, no-code platform for moving and transforming data at scale. This step-by-step guide walks you through building your first real pipeline in under 10 minutes, explaining every concept along the way.
diff --git a/blog/authors.yml b/blog/authors.yml
index b69d8bcaf..9d904aa1f 100644
--- a/blog/authors.yml
+++ b/blog/authors.yml
@@ -1,5 +1,5 @@
sanjay-kv:
- name: Sanjay Viswanthan
+ name: Sanjay Viswanathan
title: Founder at recode hive
url: https://nomadbrains.dev
image_url: https://avatars.githubusercontent.com/u/30715153?v=4
diff --git a/blog/azure-cost-optimization/index.md b/blog/azure-cost-optimization/index.md
index 7b71bd49d..3a57c4d30 100644
--- a/blog/azure-cost-optimization/index.md
+++ b/blog/azure-cost-optimization/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Azure Data Pipeline Cost Optimization"
tags: [azure, cost-optimization, data-pipeline, adls-gen2, azure-synapse, apache-spark, azure-data-factory, delta-lake, data-engineering, cloud-cost]
date: 2026-05-15
+image: /img/blogs/cost_optimzation_cover.png
+category: CLOUD COST
description: Six costly mistakes in a 2GB Azure data pipeline drove a $4,200 monthly bill. Here's every mistake, root cause, and fix — with before-and-after numbers for each.
diff --git a/blog/azure-storage-options/index.md b/blog/azure-storage-options/index.md
index 6172ae85a..70be4e643 100644
--- a/blog/azure-storage-options/index.md
+++ b/blog/azure-storage-options/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Azure Storage & ADLS Gen2: Complete Guide"
tags: [azure-storage, blob-storage, adls-gen2, azure-data-lake, queue-storage, table-storage, file-storage, data-engineering, azure, big-data, medallion-architecture]
date: 2026-05-06
+image: /img/blogs/azure-storage-cover.png
+category: AZURE
description: Every Azure data pipeline needs a place to store data. But Azure gives you four different storage types and choosing the wrong one is easier than you think. This guide explains all four, shows how they work together in a real pipeline, and goes deep on ADLS Gen2, the storage layer that powers modern Azure data engineering.
diff --git a/blog/azure-synapse-analytics/index.md b/blog/azure-synapse-analytics/index.md
index 6f6b31d91..8d98e030f 100644
--- a/blog/azure-synapse-analytics/index.md
+++ b/blog/azure-synapse-analytics/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Azure Synapse Analytics"
tags: [azure-synapse-analytics, data-engineering, sql-pools, apache-spark, microsoft-fabric, data-warehouse, adls-gen2, azure, big-data, etl]
date: 2026-05-06
+image: /img/blogs/azure-synapse-cover.png
+category: AZURE
description: Azure Synapse Analytics is one of the most powerful tools in the Azure data stack. But in 2026, with Microsoft Fabric growing fast, the question isn't just "what is Synapse?" — it's "when should you still use it, and when should you move to Fabric?" Here's the honest answer.
diff --git a/blog/batch-vs-stream-processing/index.md b/blog/batch-vs-stream-processing/index.md
index bac495823..c42144724 100644
--- a/blog/batch-vs-stream-processing/index.md
+++ b/blog/batch-vs-stream-processing/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Why We Rolled Back Our Kafka Pipeline to Batch After 6 Monthss"
tags: [batch-processing, stream-processing, data-engineering, apache-kafka, apache-flink, apache-spark, data-pipeline, real-time, azure, medallion-architecture, data-architecture]
date: 2026-05-06
+image: /img/blogs/batch-vs-stream-cover.png
+category: STREAMING
description: Everyone talks about the benefits of streaming pipelines — real-time insights, millisecond latency, live dashboards. Nobody talks about what it actually costs you. I rebuilt a working batch pipeline as a streaming system. Here's what I learned the hard way.
diff --git a/blog/data-engineers-vs-mbas/index.md b/blog/data-engineers-vs-mbas/index.md
index 9e9757946..05d0d8de5 100644
--- a/blog/data-engineers-vs-mbas/index.md
+++ b/blog/data-engineers-vs-mbas/index.md
@@ -4,6 +4,9 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Data Engineers vs MBAs as Business Analysts"
tags: [data-engineering, career, business-analysis, data-driven, analytics, data-strategy, opinion, leadership]
date: 2026-05-19
+image: /img/blogs/ba_de_cover_image.png
+category: CAREER
+featured: true
description: MBAs learn to analyze businesses. Data engineers live inside the data those businesses actually run on. Here's why that difference matters more than any credential — and what both sides should learn from each other.
diff --git a/blog/deltalake-data-storage/index.md b/blog/deltalake-data-storage/index.md
index 1f0469095..f02a5bb71 100644
--- a/blog/deltalake-data-storage/index.md
+++ b/blog/deltalake-data-storage/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Delta lake Storage"
tags: [deltalake,storage, Big Data, cloud, Data Engineering, fabric]
date: 2026-05-01
+image: /img/blogs/delta-lake-logo.png
+category: STORAGE
description: Delta Lake is an open-source storage framework that enables building a format agnostic Lakehouse architecture with compute engines including Spark, PrestoDB, Flink, Trino, Hive, Snowflake, Google BigQuery, Athena, Redshift, Databricks, Azure Fabric and APIs for Scala, Java, Rust, and Python. With Delta Universal Format aka UniForm, you can read now Delta tables with Iceberg and Hudi clients.
diff --git a/blog/fabric-data-engineer/index.md b/blog/fabric-data-engineer/index.md
index 63ee6e89a..7e5d9b7a9 100644
--- a/blog/fabric-data-engineer/index.md
+++ b/blog/fabric-data-engineer/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Fabric Data Engineer"
tags: [DP-700,azure, Big Data, cloud,certification,Data Engineering, fabric,experience]
date: 2026-05-01
+image: /img/blogs/microsoft-certified-associate-badge.png
+category: CERTIFICATION
description: "A comprehensive guide to clearing the Microsoft Fabric Data Engineer Associate (DP-700) certification. Learn the preparation strategy, key concepts, hands-on practice tips, and real exam experience from someone who passed it. Discover why Lakehouse, Delta Tables, Dataflows, and DirectLake mode matter, and how to approach scenario-based questions effectively."
diff --git a/blog/git-coding-agent/index.md b/blog/git-coding-agent/index.md
index 517320d90..8185946e8 100644
--- a/blog/git-coding-agent/index.md
+++ b/blog/git-coding-agent/index.md
@@ -4,6 +4,8 @@ sidebar_label: GitHub Copilot Coding Agent
authors: [sanjay-kv]
tags: [GitHub, SEO, Coding agent,Copilot, AI, Automation]
date: 2025-07-04
+image: /img/blogs/06-github-agent.png
+category: AI TOOLS
description: An overview of the GitHub Copilot Coding Agent, an AI-powered tool that automates software engineering tasks by taking GitHub Issues as input to write code, run tests, and create pull requests.
draft: false
diff --git a/blog/github-cli-agent/index.md b/blog/github-cli-agent/index.md
index 312c8873e..3c9af7a57 100644
--- a/blog/github-cli-agent/index.md
+++ b/blog/github-cli-agent/index.md
@@ -4,6 +4,8 @@ authors: [sanjay-kv]
sidebar_label: "GitHub Copilot CLI"
tags: [GitHub, CLI, tech, updates, Copilot, Coding, Assistant]
date: 2025-09-17
+image: /img/blogs/github-cli-agent-cover.png
+category: GITHUB
description: GitHub bought power of GitHub Copilot coding agent directly to your terminal, with GitHub Copilot CLI, you can work locally and synchronously with an AI agent.
diff --git a/blog/google-icon-update/index.md b/blog/google-icon-update/index.md
index d425f10dc..ea9de127d 100644
--- a/blog/google-icon-update/index.md
+++ b/blog/google-icon-update/index.md
@@ -4,6 +4,7 @@ description: "Google redesigned its Workspace icons in 2026, dropping the four-c
authors: [sanjay-kv]
tags: [google-workspace, ui-design, ux]
date: 2026-05-21
+category: DESIGN
image: /img/blogs/cover-google-icon.jpg
---
diff --git a/blog/lakehouse-vs-warehouse/index.md b/blog/lakehouse-vs-warehouse/index.md
index 6d76f41e4..aa08eb508 100644
--- a/blog/lakehouse-vs-warehouse/index.md
+++ b/blog/lakehouse-vs-warehouse/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Lakehouse vs Data Warehouse"
tags: [lakehouse, data-warehouse, data-engineering, big-data, delta-lake, spark, analytics, snowflake, databricks]
date: 2026-05-01
+image: /img/blogs/datalake_vs_warehouse.png
+category: ARCHITECTURE
description: Lakehouse and Data Warehouse are two of the most debated architectures in modern data engineering. This article breaks down how they differ, where each fits in the data lifecycle, and how to choose between them, without the platform bias.
diff --git a/blog/medallion-architecture/index.md b/blog/medallion-architecture/index.md
index 736da68d3..158a00706 100644
--- a/blog/medallion-architecture/index.md
+++ b/blog/medallion-architecture/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Medallion Architecture Explained"
tags: [medallion-architecture, data-engineering, bronze-silver-gold, data-pipeline, delta-lake, spark, databricks, microsoft-fabric, data-quality]
date: 2026-05-07
+image: /img/blogs/medallion_architecture.png
+category: ARCHITECTURE
description: Most data pipelines don't fail because of bad technology. They fail because raw data flows directly into reports with no checkpoints, no validation, and no clear ownership. Medallion Architecture fixes exactly this — here's how it works, why it matters, and how to implement it in practice.
diff --git a/blog/microsoft-data-purview/index.md b/blog/microsoft-data-purview/index.md
index 947015b81..80d65f622 100644
--- a/blog/microsoft-data-purview/index.md
+++ b/blog/microsoft-data-purview/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Microsoft Purview Data Catalog"
tags: [azure, microsoft-purview, data-catalog, data-governance, onboarding, data-lineage, metadata, data-engineering, azure-data-factory, delta-lake]
date: 2026-05-19
+image: /img/blogs/purview_cover.png
+category: DATA GOVERNANCE
description: "Learn how Microsoft Purview Data Catalog improves data discovery, lineage, ownership, and governance. This practical case study shows how we reduced data engineer onboarding from 2 weeks to 3 days — including the configuration that made it work."
diff --git a/blog/microsoft-fabric-explained/index.md b/blog/microsoft-fabric-explained/index.md
index 8a84d8d1e..ded4425dd 100644
--- a/blog/microsoft-fabric-explained/index.md
+++ b/blog/microsoft-fabric-explained/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "Microsoft Fabric"
tags: [microsoft-fabric, onelake, data-engineering, lakehouse, delta-lake, big-data, cloud, power-bi]
date: 2026-05-01
+image: /img/blogs/microsoft-farbic-blog-image.png
+category: MICROSOFT FABRIC
description: Microsoft Fabric is a unified analytics platform that brings together data engineering, data science, real-time analytics, and business intelligence under a single roof — all built on OneLake. Learn how Fabric is architected, how data flows through it, and why it matters for modern data teams.
diff --git a/blog/n8n-workflow-automation/index.md b/blog/n8n-workflow-automation/index.md
index c14847fb5..c9da57031 100644
--- a/blog/n8n-workflow-automation/index.md
+++ b/blog/n8n-workflow-automation/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "N8N AI Workflows Explained"
tags: [N8N, AI Automation, Workflow Automation, No-Code, Integration, Machine Learning, API Integration]
date: 2025-09-17
+image: /img/blogs/n8n-logo.png
+category: AUTOMATION
description: N8N revolutionizes automation by integrating AI capabilities into visual workflows. Learn how to build intelligent automation pipelines that can process data, make decisions, and interact with multiple services seamlessly.
diff --git a/blog/netflix-data-engineering/index.md b/blog/netflix-data-engineering/index.md
index d253498aa..286712ed6 100644
--- a/blog/netflix-data-engineering/index.md
+++ b/blog/netflix-data-engineering/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "How Netflix Handles 2 Trillion Events Every Day"
tags: [netflix, data-engineering, kafka, apache-flink, real-time, event-streaming, data-pipeline, cassandra, avro, iceberg, keystone]
date: 2026-05-04
+image: /img/blogs/thumbnail.png
+category: CASE STUDY
description: Every click, pause, search, and scroll on Netflix generates an event. With 300 million subscribers across 190 countries, Netflix processes 2 trillion events every single day through a pipeline called Keystone. Here's a deep dive into how Kafka, Flink, Cassandra, and Iceberg make it all work in real time.
diff --git a/blog/open-ai-agent-builder/index.md b/blog/open-ai-agent-builder/index.md
index 4e2455bcb..455708d6c 100644
--- a/blog/open-ai-agent-builder/index.md
+++ b/blog/open-ai-agent-builder/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "OpenAI AgentKit Explained"
tags: [OpenAI, AgentKit, AI Agents, Agent Builder, Agentic AI, Developer Tools]
date: 2025-10-15
+image: /img/blogs/Agent_Builder.png
+category: AI AGENTS
description: OpenAI's AgentKit revolutionizes how developers build AI agents with its visual Agent Builder, integrated ChatKit, comprehensive evaluation tools, and seamless third-party integrations. Learn how this complete toolkit takes agents from prototype to production with minimal friction.
diff --git a/blog/single-sign-on/index.md b/blog/single-sign-on/index.md
index 467b38845..b0723c14c 100644
--- a/blog/single-sign-on/index.md
+++ b/blog/single-sign-on/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore, sanjay-kv]
sidebar_label: "How SSO Works - Case Study"
tags: [sso, single-sign-on, authentication, identity-provider, oauth, openid-connect, saml, security, web]
date: 2026-05-04
+image: /img/blogs/sso_cover.png
+category: SECURITY
description: SSO lets you log into dozens of apps with a single set of credentials. But how does it actually work under the hood? This beginner-friendly guide walks through the full flow — from clicking "Sign in with Google" to getting access — step by step.
diff --git a/blog/spark-architecture/index.md b/blog/spark-architecture/index.md
index 119d4d86e..711389633 100644
--- a/blog/spark-architecture/index.md
+++ b/blog/spark-architecture/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore,sanjay-kv]
sidebar_label: "Spark Architecture Explained"
tags: [Apache Spark, Spark Architecture, Big Data, Distributed Computing, Data Engineering]
date: 2025-08-22
+image: /img/blogs/07-spark-blog-banner.png
+category: ARCHITECTURE
description: Apache Spark is a fast, open-source big data framework that leverages in-memory computing for high performance. Its architecture powers scalable distributed processing across clusters, making it essential for analytics and machine learning.
diff --git a/blog/spark-performance-optimizations/index.md b/blog/spark-performance-optimizations/index.md
index dde81cb40..5b60c5ce6 100644
--- a/blog/spark-performance-optimizations/index.md
+++ b/blog/spark-performance-optimizations/index.md
@@ -4,6 +4,8 @@ authors: [Aditya-Singh-Rathore]
sidebar_label: "PySpark Optimization Techniques"
tags: [pyspark, spark, optimization, data-engineering, delta-lake, performance, partitioning, joins, caching, cluster-config]
date: 2026-05-16
+image: /img/blogs/pyspark_optimization_cover.png
+category: PERFORMANCE
description: Six PySpark mistakes that silently kill pipeline performance and how to fix every one of them. Covering partitioning, shuffle tuning, caching, join strategies, UDFs, predicate pushdown, and cluster config. Explained from scratch.
diff --git a/blog/ux-ui-design-job/index.md b/blog/ux-ui-design-job/index.md
index 73236ad60..b4d219c7b 100644
--- a/blog/ux-ui-design-job/index.md
+++ b/blog/ux-ui-design-job/index.md
@@ -7,6 +7,8 @@ tags:
- AI
date: 2025-06-05 10:32:00
+image: /img/blogs/04-ux-job-design.png
+category: CAREER
description: Are you passionate about design and dreaming of a career in it? Or maybe you’re already in the design space and looking to pivot into UI/UX? .
draft: false
# meta:
diff --git a/community/contributing-guidelines.md b/community/contributing-guidelines.md
index eec55117b..8476f4601 100644
--- a/community/contributing-guidelines.md
+++ b/community/contributing-guidelines.md
@@ -161,6 +161,15 @@ We welcome contributions! Follow these steps to get started.
> Tip: If your branch falls behind main, you can fetch and merge updates from upstream again before pushing.
+## Blog banners
+
+Every blog post needs a banner so it looks right on the Blogs page.
+
+- Set it in the post's front matter with `image: /img/blogs/your-banner.png`, and add a `category:` (a short label such as ARCHITECTURE or TUTORIAL).
+- Banners should be **1600 x 900 pixels (16:9)**, with **one big headline of 3 to 5 words**. The Blogs page shows the whole banner without cropping, so a banner in another shape gets empty bars on its sides.
+- Keep important text away from the edges and keep the file small (under 300 KB where you can).
+- Add `featured: true` only to the post the editors pick for the Featured spot.
+
## Formatting
To ensure consistent code style and catch errors before committing, please follow these steps:
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 18cb18ded..511fabb7e 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -91,7 +91,7 @@ Replace my name with your name and respective website links of yours.
```yaml
sanjay-kv:
- name: Sanjay Viswanthan
+ name: Sanjay Viswanathan
title: Founder at recode hive
url: https://nomadbrains.dev
image_url: https://avatars.githubusercontent.com/u/30715153?v=4
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index bfafece75..22b9ede45 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -297,6 +297,8 @@ const config: Config = {
"./plugins/learn-words",
// Publish dates for the Broadcasts page from YouTube public RSS feeds (no API key)
"./plugins/broadcasts-feed",
+ // Exposes every blog post to the /blogs listing page
+ "./plugins/blog-listing",
// Real GitHub Discussions, fetched at build time (token never reaches the browser)
"./plugins/github-discussions",
[
diff --git a/plugins/blog-listing/index.js b/plugins/blog-listing/index.js
new file mode 100644
index 000000000..16ca1ff0c
--- /dev/null
+++ b/plugins/blog-listing/index.js
@@ -0,0 +1,49 @@
+// Exposes every blog post to the /blogs listing page as global data, so the page never needs a
+// hardcoded post list. Reads the blog plugin's content after it has loaded.
+const path = require('path');
+
+function githubOf(author) {
+ const gh = author.socials && author.socials.github;
+ if (gh) return `https://github.com/${gh}`;
+ if (author.url && /github\.com/i.test(author.url)) return author.url;
+ return undefined;
+}
+
+module.exports = function blogListingPlugin() {
+ return {
+ name: 'blog-listing',
+ async allContentLoaded({ allContent, actions }) {
+ const blog = allContent['docusaurus-plugin-content-blog'];
+ const posts = (blog && blog.default && blog.default.blogPosts) || [];
+
+ const out = posts
+ .filter((p) => !p.metadata.unlisted)
+ .map((p) => {
+ const m = p.metadata;
+ const fm = m.frontMatter || {};
+ return {
+ id: p.id,
+ folder: path.basename(path.dirname(m.source.replace(/^@site[\\/]/, ''))),
+ title: m.title,
+ permalink: m.permalink,
+ description: m.description || '',
+ date: m.date instanceof Date ? m.date.toISOString() : String(m.date),
+ readingTime: Math.max(1, Math.ceil(m.readingTime || 1)),
+ image: typeof fm.image === 'string' ? fm.image : undefined,
+ category: typeof fm.category === 'string' ? fm.category : undefined,
+ featured: fm.featured === true,
+ tags: (m.tags || []).map((t) => ({ label: t.label, permalink: t.permalink })),
+ authors: (m.authors || []).map((a) => ({
+ key: a.key || a.name,
+ name: a.name,
+ imageURL: a.imageURL,
+ github: githubOf(a),
+ })),
+ };
+ })
+ .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
+
+ actions.setGlobalData({ posts: out });
+ },
+ };
+};
diff --git a/src/components/blogsPage/Hero.tsx b/src/components/blogsPage/Hero.tsx
new file mode 100644
index 000000000..e6b53f9e8
--- /dev/null
+++ b/src/components/blogsPage/Hero.tsx
@@ -0,0 +1,102 @@
+import React, { useMemo } from 'react';
+import { Search } from 'lucide-react';
+import { TOPICS, uniqueAuthors, type Post } from './blogData';
+import { AuthorAvatar } from './PostCard';
+import styles from './blogsPage.module.css';
+
+const COLS = 32;
+const ROWS = 5;
+
+// Seeded so server and client agree. Levels rise toward the right.
+const CELLS = Array.from({ length: COLS * ROWS }, (_, i) => {
+ const col = Math.floor(i / ROWS);
+ const row = i % ROWS;
+ const noise = Math.abs(Math.sin((col + 1) * 12.9898 + (row + 3) * 78.233) * 43758.5453) % 1;
+ const base = (col / (COLS - 1)) * 3.2;
+ const level = Math.max(0, Math.min(4, Math.round(base + noise * 1.8 - 0.6)));
+ return { level, pulse: i % 9 === 0 && level >= 3, delay: (i % 7) * 0.45 };
+});
+
+function GithubTile() {
+ return (
+
+
+ {CELLS.map((c, i) => (
+
+ ))}
+
+
+
+ github.com/recodehive
+ (opens in a new tab)
+
+
+ );
+}
+
+function AuthorsStrip({ posts }: { posts: Post[] }) {
+ const authors = useMemo(() => uniqueAuthors(posts), [posts]);
+ if (authors.length === 0) return null;
+ return (
+
+
+ {authors.map((a) => {
+ const face = ;
+ return (
+
+ {a.github ? (
+ {face}
+ ) : (
+ {face}
+ )}
+ {a.name}
+
+ );
+ })}
+
+
+ {authors.length} authors have written {posts.length} posts
+
+
+ Become one {'→'} (opens in a new tab)
+
+
+ );
+}
+
+interface Props {
+ posts: Post[];
+ query: string;
+ onQuery: (q: string) => void;
+ topic: string;
+ onTopic: (label: string) => void;
+}
+
+export default function Hero({ posts, query, onQuery, topic, onTopic }: Props) {
+ return (
+
+ );
+}
diff --git a/src/components/blogsPage/PostCard.tsx b/src/components/blogsPage/PostCard.tsx
new file mode 100644
index 000000000..e33482342
--- /dev/null
+++ b/src/components/blogsPage/PostCard.tsx
@@ -0,0 +1,100 @@
+import React from 'react';
+import Link from '@docusaurus/Link';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import { categoryOf, formatDate, initials, tagText, type Post, type PostAuthor } from './blogData';
+import styles from './blogsPage.module.css';
+
+const PILLS = ['pillGreen', 'pillTeal', 'pillOrange', 'pillBlue', 'pillRose'] as const;
+const pillClass = (label: string): string => {
+ let h = 0;
+ for (let i = 0; i < label.length; i++) h = label.charCodeAt(i) + ((h << 5) - h);
+ return styles[PILLS[Math.abs(h) % PILLS.length]];
+};
+
+export function Pills({ post, max = 3 }: { post: Post; max?: number }) {
+ if (post.tags.length === 0) return null;
+ return (
+
+ {post.tags.slice(0, max).map((t) => (
+
+ {tagText(t.label)}
+
+ ))}
+
+ );
+}
+
+export function AuthorAvatar({ author, size }: { author: PostAuthor; size: number }) {
+ if (author.imageURL) {
+ return ;
+ }
+ return (
+
+ {initials(author.name)}
+
+ );
+}
+
+/** 16:9 frame. The whole banner stays visible (object-fit: contain), so banner text is never cropped. */
+export function Thumb({ post, className = '' }: { post: Post; className?: string }) {
+ const src = useBaseUrl(post.image ?? '');
+ const tag = post.tags[0] ? tagText(post.tags[0].label) : 'Article';
+ return (
+
+ {post.image ? (
+
+ ) : (
+
{tag}
+ )}
+
+ );
+}
+
+export function Byline({ post, stacked = false }: { post: Post; stacked?: boolean }) {
+ const first = post.authors[0];
+ return (
+
+ {first &&
}
+
+ {post.authors.map((a) => a.name).join(', ') || 'recode hive'}
+ {formatDate(post.date)} {'·'} {post.readingTime} min read
+
+
+ );
+}
+
+export function PostCard({ post }: { post: Post }) {
+ return (
+
+
+
+
{categoryOf(post)}
+
+ {post.title}
+
+
{post.description}
+
+
+
+ Read {'→'}
+
+
+
+ );
+}
+
+export function SideItem({ post }: { post: Post }) {
+ return (
+
+
+
+
{categoryOf(post)}
+
+ {post.title}
+
+
+
{post.authors[0]?.name} {'·'} {formatDate(post.date)} {'·'} {post.readingTime} min read
+
+
+ );
+}
diff --git a/src/components/blogsPage/Sections.tsx b/src/components/blogsPage/Sections.tsx
new file mode 100644
index 000000000..000e289d8
--- /dev/null
+++ b/src/components/blogsPage/Sections.tsx
@@ -0,0 +1,192 @@
+import React from 'react';
+import Link from '@docusaurus/Link';
+import { BLOG_CHANGELOG } from '../../data/blogChangelog';
+import { categoryOf, formatDate, hasTag, type Post } from './blogData';
+import useBaseUrl from '@docusaurus/useBaseUrl';
+import { Byline, Pills, PostCard, SideItem, Thumb } from './PostCard';
+import styles from './blogsPage.module.css';
+
+const REPO = 'https://github.com/recodehive/recode-website';
+export const PAGE_SIZE = 12;
+
+export function Spotlight({ posts }: { posts: Post[] }) {
+ const picks = posts.filter((p) => hasTag(p, ['big-data'])).slice(0, 2);
+ if (picks.length === 0) return null;
+ const [lead, second] = picks;
+ return (
+
+
+
+
TOPIC SPOTLIGHT
+
Big data at scale
+
+
All Big Data posts {'→'}
+
+
+
+
+
+
{categoryOf(lead)}
+
{lead.title}
+
{lead.description}
+
+
+
+
+ {second && (
+
+ {categoryOf(second)}
+ {second.title}
+ {second.description}
+
+
+
+ )}
+
+
+ );
+}
+
+interface LatestProps {
+ posts: Post[];
+ page: number;
+ onPage: (n: number) => void;
+}
+
+export function Latest({ posts, page, onPage }: LatestProps) {
+ const pages = Math.max(1, Math.ceil(posts.length / PAGE_SIZE));
+ const start = posts.length === 0 ? 0 : (page - 1) * PAGE_SIZE + 1;
+ const end = Math.min(page * PAGE_SIZE, posts.length);
+ const slice = posts.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);
+ return (
+
+ EXPLORE ARTICLES
+
+
+
Latest
+ View all {'→'}
+
+ {slice.length === 0 ? (
+ No articles match your search. Try a different word or topic.
+ ) : (
+
+ )}
+
+ Showing {start} - {end} of {posts.length} posts
+ {pages > 1 && (
+
+ onPage(page - 1)}>Previous
+ {Array.from({ length: pages }, (_, i) => i + 1).map((n) => (
+ onPage(n)}>{n}
+ ))}
+ onPage(page + 1)}>Next
+
+ )}
+
+
+ );
+}
+
+/** The featured image fills the space above the text and may crop, so it should be a picture without text. */
+function FeaturedImage({ post }: { post: Post }) {
+ const src = useBaseUrl(post.image ?? '');
+ if (!post.image) return {post.tags[0]?.label ?? 'Article'}
;
+ return ;
+}
+
+export function Featured({ posts, total, onBackToAll }: { posts: Post[]; total: number; onBackToAll: () => void }) {
+ if (posts.length === 0) return null;
+ const main = posts.find((p) => p.featured) ?? posts[0];
+ const rest = posts.filter((p) => p.id !== main.id);
+ // Other editor picks first, then the newest posts.
+ const sides = [...rest.filter((p) => p.featured), ...rest.filter((p) => !p.featured)].slice(0, 3);
+ return (
+
+
+
+
Featured
+
EDITOR’S PICK
+
+
+
+
+
+
{categoryOf(main)}
+
{main.title}
+
{main.description}
+
+
+
+ Read {'→'}
+
+
+
+
+ {sides.map((p) => )}
+ Back to all articles {'↑'}
+ {total} articles in total
+
+
+
+ );
+}
+
+export function ChangelogStrip({ posts }: { posts: Post[] }) {
+ const items = BLOG_CHANGELOG.slice(0, 4);
+ const permalinkOf = (folder?: string) => posts.find((p) => p.folder === folder)?.permalink;
+ return (
+
+
+
+
+ {items.map((e, i) => {
+ const link = permalinkOf(e.post);
+ return (
+
+
+ {link ? {e.title} : e.title}
+ {e.summary}
+
+ {formatDate(e.date)}
+
+
+ {e.prs.map((n, k) => (
+
+ {k > 0 && ', '}
+ #{n}
+
+ ))}{' '}
+ @{e.author}
+
+
+ );
+ })}
+
+
+ );
+}
+
+export function WriteBanner() {
+ return (
+
+
+
{'✦'} WRITE FOR THE HIVE
+
Write technical blogs on recode hive and get featured.
+
Share what you built or learned. The strongest posts get the Featured spot on this page.
+
+
+
+ );
+}
diff --git a/src/components/blogsPage/blogData.ts b/src/components/blogsPage/blogData.ts
new file mode 100644
index 000000000..ccf65daf1
--- /dev/null
+++ b/src/components/blogsPage/blogData.ts
@@ -0,0 +1,85 @@
+import { usePluginData } from '@docusaurus/useGlobalData';
+
+export interface PostAuthor {
+ key: string;
+ name: string;
+ imageURL?: string;
+ github?: string;
+}
+export interface PostTag {
+ label: string;
+ permalink: string;
+}
+export interface Post {
+ id: string;
+ folder: string;
+ title: string;
+ permalink: string;
+ description: string;
+ date: string;
+ readingTime: number;
+ image?: string;
+ category?: string;
+ featured: boolean;
+ tags: PostTag[];
+ authors: PostAuthor[];
+}
+
+/** Every post, newest first. Comes from the blog plugin, so nothing here is hardcoded. */
+export function usePosts(): Post[] {
+ const data = usePluginData('blog-listing') as { posts?: Post[] } | undefined;
+ return data?.posts ?? [];
+}
+
+export const slugify = (s: string): string => s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
+
+export interface Topic {
+ label: string;
+ /** Tag slugs that put a post under this chip. */
+ tags: string[];
+}
+
+export const TOPICS: Topic[] = [
+ { label: 'Data Engineering', tags: ['data-engineering'] },
+ { label: 'Azure', tags: ['azure'] },
+ { label: 'Big Data', tags: ['big-data'] },
+ { label: 'Career', tags: ['career'] },
+ { label: 'Design & UX', tags: ['ux', 'ui-design', 'ui', 'design'] },
+];
+
+export const hasTag = (post: Post, tags: string[]): boolean => post.tags.some((t) => tags.includes(slugify(t.label)));
+
+const ACRONYMS = new Set(['adf', 'etl', 'adls', 'sso', 'ai', 'api', 'cli', 'sql', 'ui', 'ux', 'seo', 'dp', 'saml', 'oauth', 'iceberg']);
+/** "azure-data-factory" becomes "Azure Data Factory". Known acronyms stay upper case. */
+export function tagText(label: string): string {
+ return label
+ .replace(/[-_]+/g, ' ')
+ .trim()
+ .split(/\s+/)
+ .map((w) => (ACRONYMS.has(w.toLowerCase()) && w.toLowerCase() !== 'iceberg' ? w.toUpperCase() : w[0].toUpperCase() + w.slice(1)))
+ .join(' ');
+}
+
+export const formatDate = (iso: string): string =>
+ new Date(iso).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' });
+
+export const initials = (name: string): string =>
+ name.trim().split(/\s+/).map((w) => w[0] ?? '').join('').slice(0, 2).toUpperCase();
+
+/** The category label above a title: the front matter value, or the first tag. */
+export const categoryOf = (post: Post): string => (post.category ?? (post.tags[0] ? tagText(post.tags[0].label) : '')).toUpperCase();
+
+export interface AuthorSummary extends PostAuthor {
+ posts: number;
+}
+export function uniqueAuthors(posts: Post[]): AuthorSummary[] {
+ const map = new Map();
+ for (const p of posts) {
+ for (const a of p.authors) {
+ const cur = map.get(a.key);
+ if (cur) cur.posts += 1;
+ else map.set(a.key, { ...a, posts: 1 });
+ }
+ }
+ return [...map.values()].sort((a, b) => b.posts - a.posts);
+}
diff --git a/src/components/blogsPage/blogsPage.module.css b/src/components/blogsPage/blogsPage.module.css
new file mode 100644
index 000000000..bcb811724
--- /dev/null
+++ b/src/components/blogsPage/blogsPage.module.css
@@ -0,0 +1,219 @@
+.root {
+ --accent: #2e8555; --accent-dark: #205d3b; --accent-tint: #f3faf6; --accent-border: #d3ecdf;
+ --text: #0f172a; --text-2: #59636e; --muted: #6b7684; --border: #e2e8f0; --line: #e5e9ef; --hair: #f1f5f9;
+ --bg: #ffffff; --hero-bg: #f8fafc; --card: #ffffff; --grid: #e7edf3; --chip-on: #1f2328;
+ --shadow: 0 8px 24px rgba(15, 23, 42, 0.06); --shadow-h: 0 14px 30px rgba(15, 23, 42, 0.12);
+ --panel: #f6f8fa; --panel-border: #d8dee4;
+ --p-green-bg: #ecfdf5; --p-green-fg: #166534; --p-teal-bg: #f0fdfa; --p-teal-fg: #115e59;
+ --p-orange-bg: #fff7ed; --p-orange-fg: #9a3412; --p-blue-bg: #eff6ff; --p-blue-fg: #1e40af; --p-rose-bg: #fff1f2; --p-rose-fg: #9f1239;
+ background: var(--bg); color: var(--text); font-family: "Inter", sans-serif; overflow-x: clip;
+}
+:global(html[data-theme='dark']) .root {
+ --accent: #4cc184; --accent-dark: #34d399; --accent-tint: #0f1f17; --accent-border: #1d3a2a;
+ --text: #f1f5f9; --text-2: #b6c2d1; --muted: #94a3b8; --border: #1e293b; --line: #1f2937; --hair: #1a2332;
+ --bg: #0b1120; --hero-bg: #0f172a; --card: #111827; --grid: #1a2538; --chip-on: #f1f5f9;
+ --shadow: 0 8px 24px rgba(0, 0, 0, 0.35); --shadow-h: 0 14px 30px rgba(0, 0, 0, 0.5);
+ --panel: #111827; --panel-border: #263244;
+ --p-green-bg: #0f2a1c; --p-green-fg: #86efac; --p-teal-bg: #0b2a2a; --p-teal-fg: #5eead4;
+ --p-orange-bg: #2f1a0c; --p-orange-fg: #fdba74; --p-blue-bg: #0f2038; --p-blue-fg: #93c5fd; --p-rose-bg: #34111b; --p-rose-fg: #fda4af;
+}
+.srOnly { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
+.root a:focus-visible, .root button:focus-visible, .root input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
+main.root h1, main.root h2, main.root h3 { margin: 0; padding: 0; text-align: left; max-width: none; }
+.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px 64px; }
+.mono { margin: 0; font: 400 11.5px "JetBrains Mono", ui-monospace, monospace; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent); }
+main.root .h2 { font-family: "Space Grotesk", "Inter", sans-serif; font-size: 28px; line-height: 1.15; font-weight: 700; letter-spacing: -0.02em; color: var(--text); }
+.greenLink, .greenLinkBtn { color: var(--accent); font: 600 14px "Inter", sans-serif; text-decoration: none; background: none; border: 0; padding: 0; cursor: pointer; }
+.greenLink:hover, .greenLinkBtn:hover { color: var(--accent-dark); text-decoration: underline; }
+.rule { height: 1px; margin: 10px 0 18px; background: var(--text); opacity: 0.85; }
+
+/* Hero */
+.hero { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 600px); gap: 40px; align-items: center; max-width: none; padding: 56px max(24px, calc((100% - 1132px) / 2)) 48px; background: var(--hero-bg); border-bottom: 1px solid var(--line); overflow: hidden; }
+.heroGridBg { position: absolute; inset: 0; pointer-events: none; background-image: linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px); background-size: 40px 40px; -webkit-mask-image: radial-gradient(ellipse at 100% 0%, #000 0%, transparent 65%); mask-image: radial-gradient(ellipse at 100% 0%, #000 0%, transparent 65%); }
+.heroText, .heroRight { position: relative; z-index: 1; min-width: 0; }
+.eyebrow { margin: 0 0 14px; font: 700 12px/1 "Inter", sans-serif; letter-spacing: 0.12em; text-transform: uppercase; color: #2e8555; }
+main.root .h1 { font-family: "Space Grotesk", "Inter", sans-serif; font-size: 60px; line-height: 1.05; font-weight: 700; letter-spacing: -0.03em; color: var(--text); }
+.accent { color: #2e8555; }
+.subtitle { margin: 14px 0 26px; font-size: 20px; color: var(--text-2); }
+.search { display: flex; align-items: center; gap: 12px; max-width: 520px; height: 56px; padding: 0 18px; border: 1px solid #d6dde6; border-radius: 12px; background: var(--card); color: var(--muted); box-shadow: 0 4px 14px rgba(15, 23, 42, 0.05); }
+:global(html[data-theme='dark']) .search { border-color: var(--border); }
+.search input { flex: 1; min-width: 0; border: 0; outline: 0; background: transparent; color: var(--text); font: 400 15px "Inter", sans-serif; }
+.search input::placeholder { color: var(--muted); }
+.search:focus-within { border-color: var(--accent); }
+.chips { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
+.chip { height: 40px; padding: 0 18px; border: 1px solid var(--border); border-radius: 999px; background: var(--card); color: var(--text); font: 500 14px "Inter", sans-serif; cursor: pointer; }
+.chip:hover { border-color: var(--accent); }
+.chipOn { background: var(--chip-on); border-color: var(--chip-on); color: #fff; }
+:global(html[data-theme='dark']) .chipOn { color: #0f172a; }
+
+.heroRight { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
+.ghTile { display: block; width: 100%; padding: 22px; border: 1px solid var(--accent-border); border-radius: 1rem; background: var(--accent-tint); box-shadow: var(--shadow); text-decoration: none; color: var(--text); transition: transform 0.2s, box-shadow 0.2s; }
+.ghTile:hover { transform: translateY(-4px); box-shadow: var(--shadow-h); text-decoration: none; color: var(--text); }
+:global(html[data-theme='dark']) .ghTile { background: #0d1117; border-color: #1d3a2a; }
+.ghGrid { display: grid; grid-auto-flow: column; grid-template-rows: repeat(5, 1fr); grid-template-columns: repeat(32, 1fr); gap: 4px; }
+.ghGrid i { display: block; aspect-ratio: 1; border-radius: 3px; background: #e1ebe5; }
+.lv0 { background: #e1ebe5 !important; } .lv1 { background: #bfe3cd !important; } .lv2 { background: #7cc79f !important; } .lv3 { background: #3cad6e !important; } .lv4 { background: #2e8555 !important; }
+:global(html[data-theme='dark']) .lv0 { background: #161b22 !important; } :global(html[data-theme='dark']) .lv1 { background: #12321f !important; } :global(html[data-theme='dark']) .lv2 { background: #1d5c3a !important; } :global(html[data-theme='dark']) .lv3 { background: #2e8555 !important; } :global(html[data-theme='dark']) .lv4 { background: #4cc184 !important; }
+.cellPulse { animation: cellFade 3.2s ease-in-out infinite; }
+@keyframes cellFade { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
+.ghLine { margin: 16px 0 0; display: flex; align-items: center; gap: 10px; font: 400 13px "JetBrains Mono", ui-monospace, monospace; color: var(--text-2); }
+.liveDot { position: relative; width: 8px; height: 8px; border-radius: 50%; background: #22c55e; }
+.liveDot::after { content: ""; position: absolute; inset: 0; border-radius: 50%; border: 2px solid #22c55e; animation: ping 1.8s ease-out infinite; }
+@keyframes ping { 0% { transform: scale(1); opacity: 0.7; } 100% { transform: scale(3); opacity: 0; } }
+
+.strip { display: inline-flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 8px 18px 8px 10px; border: 1px solid var(--border); border-radius: 999px; background: var(--card); box-shadow: var(--shadow); }
+.avatarRow { list-style: none; margin: 0; padding: 0; display: flex; align-items: center; }
+.av { position: relative; margin-left: -8px; transition: margin 0.2s; }
+.av:first-child { margin-left: 0; }
+.av > a, .av > span { display: block; border-radius: 50%; border: 2px solid var(--card); transition: transform 0.2s, box-shadow 0.2s; }
+.av > a:hover, .av > a:focus-visible, .av > span:focus-visible { transform: translateY(-6px) scale(1.2); box-shadow: 0 0 0 3px rgba(46, 133, 85, 0.35); position: relative; z-index: 3; }
+.av:hover + .av, .av:has(+ .av:hover) { margin-left: 4px; }
+.av:hover { margin-left: 4px; }
+.tip { position: absolute; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%) translateY(4px); padding: 4px 9px; border-radius: 6px; background: #0f172a; color: #fff; font: 500 11px "Inter", sans-serif; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity 0.15s, transform 0.15s; z-index: 5; }
+.av:hover .tip, .av:focus-within .tip { opacity: 1; transform: translateX(-50%) translateY(0); }
+.avatar { display: block; border-radius: 50%; object-fit: cover; background: var(--hair); }
+.avatarFallback { display: inline-flex; align-items: center; justify-content: center; border-radius: 50%; background: linear-gradient(135deg, #bfe3cd, #7cc79f); color: #14532d; font-weight: 700; }
+.stripText { margin: 0; font-size: 13px; color: var(--text-2); }
+.stripText strong { color: var(--text); }
+.stripLink { color: var(--accent); font: 600 13px "Inter", sans-serif; text-decoration: none; }
+.stripLink:hover { text-decoration: underline; }
+
+/* Spotlight */
+.spotlight { margin-top: 40px; padding: 24px; border: 1px solid var(--accent-border); border-radius: 1rem; background: var(--accent-tint); }
+.spotHead { display: flex; justify-content: space-between; align-items: flex-end; gap: 12px; margin-bottom: 18px; }
+.spotHead .mono { margin-bottom: 6px; }
+.spotGrid { display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr); gap: 18px; }
+.spotCard { position: relative; border: 1px solid var(--line); border-radius: 12px; background: var(--card); transition: box-shadow 0.2s, transform 0.2s; }
+.spotCard:hover { box-shadow: var(--shadow-h); transform: translateY(-2px); }
+.spotLead { display: grid; grid-template-columns: 300px minmax(0, 1fr); overflow: hidden; }
+.spotThumb { align-self: start; border-bottom: 0 !important; border-right: 1px solid var(--line); }
+.spotBody { display: flex; flex-direction: column; gap: 10px; padding: 22px; min-width: 0; }
+.spotText { display: flex; flex-direction: column; gap: 10px; padding: 22px; }
+main.root .spotTitle { font-family: "Space Grotesk", sans-serif; font-size: 20px; line-height: 1.25; font-weight: 700; color: var(--text); }
+main.root .spotTitleSm { font-family: "Space Grotesk", sans-serif; font-size: 20px; line-height: 1.25; font-weight: 700; color: var(--text); }
+
+/* Shared card pieces */
+.stretched { color: inherit; text-decoration: none; }
+.stretched::after { content: ""; position: absolute; inset: 0; }
+.stretched:hover { color: var(--accent); text-decoration: none; }
+.category { margin: 0; font: 500 11px "JetBrains Mono", ui-monospace, monospace; letter-spacing: 0.1em; color: var(--accent); }
+.excerpt { margin: 0; font-size: 14px; line-height: 1.55; color: var(--text-2); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
+.clamp2 { -webkit-line-clamp: 2; }
+.spotLead .excerpt, .spotText .excerpt { -webkit-line-clamp: 3; }
+.pills { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
+.pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; font: 500 12px "Inter", sans-serif; }
+.pill i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
+.pillGreen { background: var(--p-green-bg); color: var(--p-green-fg); } .pillTeal { background: var(--p-teal-bg); color: var(--p-teal-fg); }
+.pillOrange { background: var(--p-orange-bg); color: var(--p-orange-fg); } .pillBlue { background: var(--p-blue-bg); color: var(--p-blue-fg); } .pillRose { background: var(--p-rose-bg); color: var(--p-rose-fg); }
+.byline { display: flex; align-items: center; gap: 10px; min-width: 0; }
+.bylineText { display: flex; flex-direction: column; min-width: 0; font-size: 12px; color: var(--muted); }
+.bylineText strong { color: var(--text); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.bylineStacked .avatar, .bylineStacked .avatarFallback { display: none; }
+.bylineStacked .bylineText { flex-direction: row; flex-wrap: wrap; gap: 4px; font-size: 13px; }
+.bylineStacked .bylineText strong { font-weight: 500; color: var(--text-2); }
+
+/* Thumbnails: whole banner always visible */
+.thumb { aspect-ratio: 16 / 9; overflow: hidden; background: #f8fafc; border-bottom: 1px solid #e5e9ef; }
+:global(html[data-theme='dark']) .thumb { background: #0f172a; border-color: var(--line); }
+.thumb img { display: block; width: 100%; height: 100%; object-fit: contain; object-position: center; transition: transform 0.35s; }
+.card:hover .thumb img, .spotCard:hover .thumb img, .side:hover .thumb img { transform: scale(1.025); }
+.thumbFallback { display: flex; width: 100%; height: 100%; align-items: center; justify-content: center; background: #f3faf6; color: var(--accent); font: 500 13px "JetBrains Mono", ui-monospace, monospace; text-transform: uppercase; letter-spacing: 0.08em; }
+:global(html[data-theme='dark']) .thumbFallback { background: #0f1f17; }
+
+/* Latest */
+.latest { margin-top: 56px; scroll-margin-top: 80px; }
+.latestHead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 20px; }
+.cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 24px; }
+.card { position: relative; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: var(--card); transition: transform 0.2s, box-shadow 0.2s; }
+.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-h); }
+.cardBody { display: flex; flex-direction: column; flex: 1; gap: 10px; padding: 18px 20px 16px; }
+main.root .cardTitle { font-family: "Space Grotesk", sans-serif; font-size: 18px; line-height: 1.3; font-weight: 700; color: var(--text); }
+.cardFoot { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: auto; padding-top: 12px; border-top: 1px solid var(--hair); }
+.readLink { color: var(--accent); font: 600 14px "Inter", sans-serif; white-space: nowrap; }
+.empty { padding: 40px 0; text-align: center; color: var(--text-2); }
+.pager { display: flex; justify-content: space-between; align-items: center; gap: 16px; flex-wrap: wrap; margin-top: 28px; padding-top: 22px; border-top: 1px solid var(--line); font-size: 13px; color: var(--muted); }
+.pager p { margin: 0; }
+.pagerBtns { display: flex; gap: 8px; }
+.pagerBtns button { min-width: 40px; height: 40px; padding: 0 14px; border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--text); font: 500 14px "Inter", sans-serif; cursor: pointer; }
+.pagerBtns button:disabled { color: var(--muted); cursor: default; opacity: 0.6; }
+.pagerBtns button:not(:disabled):hover { border-color: var(--accent); }
+.pagerBtns .pagerOn { background: var(--chip-on); border-color: var(--chip-on); color: #fff; }
+:global(html[data-theme='dark']) .pagerBtns .pagerOn { color: #0f172a; }
+
+/* Featured */
+.featured { margin-top: 56px; }
+.featHead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 16px; }
+.featGrid { display: grid; grid-template-columns: minmax(0, 58fr) minmax(0, 42fr); gap: 28px; align-items: stretch; }
+.featMain { position: relative; display: flex; flex-direction: column; overflow: hidden; border: 1px solid var(--line); border-radius: 12px; background: var(--card); box-shadow: var(--shadow); }
+.featImg { flex: 1 1 0; min-height: 200px; width: 100%; height: 0; object-fit: cover; display: block; }
+.featFallback { flex: 1 1 0; min-height: 200px; display: flex; align-items: center; justify-content: center; background: #f3faf6; color: var(--accent); font: 500 13px "JetBrains Mono", monospace; text-transform: uppercase; }
+.featBody { display: flex; flex-direction: column; gap: 10px; padding: 20px 22px 18px; }
+main.root .featTitle { font-family: "Space Grotesk", sans-serif; font-size: 26px; line-height: 1.2; font-weight: 700; color: var(--text); }
+.featSide { display: flex; flex-direction: column; justify-content: space-between; gap: 4px; min-width: 0; }
+.side { position: relative; display: grid; grid-template-columns: 144px minmax(0, 1fr); gap: 16px; padding: 14px 0; border-bottom: 1px solid var(--line); }
+.side:first-child { padding-top: 0; }
+.sideThumb { width: 144px; height: 81px; aspect-ratio: auto; border: 1px solid var(--line); border-radius: 6px; }
+main.root .sideTitle { margin: 4px 0 8px; font-family: "Space Grotesk", sans-serif; font-size: 16px; line-height: 1.3; font-weight: 700; color: var(--text); }
+.sideMeta { margin: 8px 0 0; font-size: 12px; color: var(--muted); }
+.greenLinkBtn { align-self: flex-start; margin-top: 14px; }
+
+/* Changelog */
+.changelog { margin-top: 48px; padding: 24px 28px 28px; border: 1px solid var(--panel-border); border-radius: 8px; background: var(--panel); }
+.clHead { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 22px; }
+main.root .clTitle { font-family: "Space Grotesk", sans-serif; font-size: 24px; font-weight: 700; color: var(--text); }
+.clAll { color: var(--text); font: 500 14px "Inter", sans-serif; text-decoration: none; }
+.clAll:hover { color: var(--accent); text-decoration: underline; }
+.timeline { position: relative; list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 24px; }
+.tlLine { position: absolute; left: 6px; right: 0; top: 6px; height: 2px; background: linear-gradient(90deg, #2e8555, #34d399, #60a5fa); transform-origin: left; animation: drawLine 1.6s ease-out both; }
+@keyframes drawLine { from { transform: scaleX(0); } to { transform: scaleX(1); } }
+.tlItem { position: relative; padding-top: 26px; min-width: 0; }
+.tlDot { position: absolute; top: 0; left: 0; width: 14px; height: 14px; border: 2px solid #2e8555; border-radius: 50%; background: var(--panel); animation: dotPop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
+.tlItem:first-of-type .tlDot { background: #2e8555; }
+@keyframes dotPop { from { transform: scale(0); } to { transform: scale(1); } }
+.tlDotNew::after { content: ""; position: absolute; inset: -2px; border-radius: 50%; border: 2px solid #2e8555; animation: ping 2s ease-out 1.6s infinite; }
+main.root .tlName { margin: 0 0 6px; font-family: "Space Grotesk", "Inter", sans-serif; font-size: 16px; line-height: 1.3; font-weight: 600; color: var(--text); }
+.tlName a { color: inherit; text-decoration: none; }
+.tlName a:hover { color: var(--accent); text-decoration: underline; }
+.tlText { margin: 0 0 8px; font-size: 13px; line-height: 1.5; color: var(--text-2); }
+.tlMeta { margin: 0 0 6px; font: 400 12px "JetBrains Mono", ui-monospace, monospace; color: var(--text); }
+.tlPrs { margin: 0; display: inline-flex; flex-wrap: wrap; gap: 4px; padding: 2px 10px; border: 1px solid var(--panel-border); border-radius: 999px; font: 400 11px "JetBrains Mono", ui-monospace, monospace; color: var(--muted); }
+.tlPrs a { color: var(--muted); }
+.tlPrs a:hover { color: var(--accent); }
+
+/* Write for the hive */
+.write { display: flex; align-items: center; justify-content: space-between; gap: 24px; flex-wrap: wrap; margin-top: 48px; padding: 28px 32px; border-radius: 1rem; background-color: #205d3b; color: #fff;
+ background-image: linear-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px); background-size: 22px 22px; }
+.writeEyebrow { margin: 0 0 8px; font: 400 11px "JetBrains Mono", ui-monospace, monospace; letter-spacing: 0.12em; color: #6fd39a; }
+main.root .writeTitle { margin: 0 0 6px; max-width: 520px; font-family: "Space Grotesk", sans-serif; font-size: 24px; line-height: 1.25; font-weight: 700; color: #fff; }
+.writeText { margin: 0; font-size: 14.5px; color: #b7dcc6; }
+.writeBtns { display: flex; gap: 10px; flex-wrap: wrap; }
+.btnGhost, .btnWhite { display: inline-flex; align-items: center; height: 44px; padding: 0 18px; border-radius: 10px; font: 600 14px "Inter", sans-serif; text-decoration: none; white-space: nowrap; }
+.btnGhost { border: 1px solid rgba(255, 255, 255, 0.5); color: #fff; }
+.btnGhost:hover { background: rgba(255, 255, 255, 0.1); color: #fff; text-decoration: none; }
+.btnWhite { background: #fff; color: #205d3b; border: 1px solid #fff; }
+.btnWhite:hover { background: #f0fdf4; color: #205d3b; text-decoration: none; }
+
+/* Responsive */
+@media (max-width: 1100px) {
+ .hero { grid-template-columns: minmax(0, 1fr); }
+ .spotGrid, .featGrid { grid-template-columns: minmax(0, 1fr); }
+ .timeline { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 32px; }
+ .tlLine { display: none; }
+}
+@media (max-width: 720px) {
+ main.root .h1 { font-size: 42px; }
+ .spotLead { grid-template-columns: minmax(0, 1fr); }
+ .spotThumb { border-right: 0; border-bottom: 1px solid var(--line) !important; }
+ .cards { grid-template-columns: minmax(0, 1fr); }
+ .wrap { padding: 0 16px 48px; }
+ .timeline { grid-template-columns: minmax(0, 1fr); }
+ .side { grid-template-columns: 120px minmax(0, 1fr); }
+ .sideThumb { width: 120px; height: 68px; }
+ .write { padding: 24px; }
+ .ghTile { padding: 14px; }
+}
+@media (prefers-reduced-motion: reduce) {
+ .cellPulse, .liveDot::after, .tlDotNew::after, .tlLine, .tlDot { animation: none !important; }
+ .card, .ghTile, .spotCard, .thumb img, .av, .av > a { transition: none; }
+ .card:hover, .ghTile:hover, .spotCard:hover { transform: none; }
+}
diff --git a/src/data/blogChangelog.ts b/src/data/blogChangelog.ts
new file mode 100644
index 000000000..e6a92d6a2
--- /dev/null
+++ b/src/data/blogChangelog.ts
@@ -0,0 +1,50 @@
+// Content changelog shown on /blogs, newest first. The page shows the latest 4.
+//
+// Every future content PR (a new post update, a fix, a refresh) should add one entry here with a
+// one-line summary of what changed. `post` is the folder name under /blog, or omitted for changes
+// that touch every post.
+
+export interface ChangelogEntry {
+ title: string;
+ summary: string;
+ /** ISO date, YYYY-MM-DD. */
+ date: string;
+ /** Pull request numbers on github.com/recodehive/recode-website. */
+ prs: number[];
+ author: string;
+ post?: string;
+}
+
+export const BLOG_CHANGELOG: ChangelogEntry[] = [
+ {
+ title: 'SEO metadata refreshed on every blog post',
+ summary: 'Titles, descriptions and social previews improved across the blog.',
+ date: '2026-10-19',
+ prs: [2296],
+ author: 'Adez017',
+ },
+ {
+ title: 'How We Used Purview Data Catalog to Reduce Onboarding Time',
+ summary: 'Post content updated after publishing.',
+ date: '2026-10-08',
+ prs: [2201],
+ author: 'Adez017',
+ post: 'microsoft-data-purview',
+ },
+ {
+ title: 'Google Changed Workspace Icon after 6 years',
+ summary: 'Duplicate line removed and text readability fixed.',
+ date: '2026-07-19',
+ prs: [1821, 1841],
+ author: 'hrx01-dev',
+ post: 'google-icon-update',
+ },
+ {
+ title: 'Azure Data Pipeline Cost Optimization: How We Cut a $4,200 Bill by 73%',
+ summary: 'Post refactored and restructured.',
+ date: '2026-05-19',
+ prs: [1556],
+ author: 'Adez017',
+ post: 'azure-cost-optimization',
+ },
+];
diff --git a/src/pages/blogs/blogs-new.css b/src/pages/blogs/blogs-new.css
deleted file mode 100644
index 754d3badd..000000000
--- a/src/pages/blogs/blogs-new.css
+++ /dev/null
@@ -1,1290 +0,0 @@
-/* Blog Page Styles - recode hive Design */
-* {
- box-sizing: border-box;
-}
-
-.blog-page {
- font-family: "Inter", sans-serif;
- background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 50%, #e8ebff 100%);
- min-height: 100vh;
- animation: pageSlideIn 0.8s ease-out;
- position: relative;
- overflow-x: hidden;
-}
-
-[data-theme="dark"] .blog-page {
- background: linear-gradient(135deg, #1b1b1d 0%, #121212 50%, #1a202c 100%);
-}
-
-@keyframes pageSlideIn {
- 0% {
- opacity: 0;
- transform: translateY(20px);
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* Navbar */
-:global(.navbar) {
- backdrop-filter: blur(20px) !important;
- background: rgba(255, 255, 255, 0.95) !important;
- border-bottom: 1px solid rgba(0, 0, 0, 0.05) !important;
- transition: all 0.3s ease !important;
- height: 60px !important;
- min-height: 60px !important;
-}
-
-:global(.navbar--fixed-top) {
- animation: navbarSlideDown 0.6s ease-out;
-}
-
-@keyframes navbarSlideDown {
- 0% {
- transform: translateY(-100%);
- opacity: 0;
- }
-
- 100% {
- transform: translateY(0);
- opacity: 1;
- }
-}
-
-/* =====================================================
- HERO SECTION
- ===================================================== */
-.blog-hero-section {
- padding: 48px 40px;
- text-align: center;
- background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 30%, #e8ebff 70%, #f0f4ff 100%);
- position: relative;
- z-index: 10;
- min-height: 150px;
- display: flex;
- align-items: center;
- margin-top: -10px;
-}
-
-[data-theme="dark"] .blog-hero-section {
- background: linear-gradient(135deg, #1b1b1d 0%, #121212 30%, #1a202c 70%, #242526 100%);
-}
-
-.blog-hero-section::before {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background:
- radial-gradient(circle at 20% 30%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),
- radial-gradient(circle at 80% 20%, rgba(168, 85, 247, 0.12) 0%, transparent 45%),
- radial-gradient(circle at 40% 80%, rgba(236, 72, 153, 0.1) 0%, transparent 50%);
- pointer-events: none;
- animation: gradientShift 8s ease-in-out infinite;
-}
-
-.blog-hero-section::after {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%236366f1' fill-opacity='0.03'%3E%3Ccircle cx='30' cy='30' r='2'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
- pointer-events: none;
- animation: float 20s linear infinite;
-}
-
-@keyframes gradientShift {
-
- 0%,
- 100% {
- background:
- radial-gradient(circle at 20% 30%, rgba(99, 102, 241, 0.15) 0%, transparent 40%),
- radial-gradient(circle at 80% 20%, rgba(168, 85, 247, 0.12) 0%, transparent 45%),
- radial-gradient(circle at 40% 80%, rgba(236, 72, 153, 0.1) 0%, transparent 50%);
- }
-
- 50% {
- background:
- radial-gradient(circle at 80% 70%, rgba(99, 102, 241, 0.12) 0%, transparent 45%),
- radial-gradient(circle at 20% 80%, rgba(168, 85, 247, 0.15) 0%, transparent 40%),
- radial-gradient(circle at 60% 20%, rgba(236, 72, 153, 0.1) 0%, transparent 50%);
- }
-}
-
-@keyframes float {
- 0% {
- transform: translateY(0px) translateX(0px);
- }
-
- 33% {
- transform: translateY(-10px) translateX(5px);
- }
-
- 66% {
- transform: translateY(5px) translateX(-5px);
- }
-
- 100% {
- transform: translateY(0px) translateX(0px);
- }
-}
-
-/* Floating Particles */
-.floating-particles {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- overflow: hidden;
- pointer-events: none;
- z-index: 0;
-}
-
-.particle {
- position: absolute;
- border-radius: 50%;
- opacity: 0.6;
- animation-timing-function: ease-in-out;
- animation-iteration-count: infinite;
-}
-
-.particle-1 {
- width: 8px;
- height: 8px;
- background: linear-gradient(45deg, #6366f1, #8b5cf6);
- top: 20%;
- left: 10%;
- animation: particleFloat1 8s infinite;
-}
-
-.particle-2 {
- width: 12px;
- height: 12px;
- background: linear-gradient(45deg, #a855f7, #ec4899);
- top: 60%;
- left: 80%;
- animation: particleFloat2 10s infinite;
-}
-
-.particle-3 {
- width: 6px;
- height: 6px;
- background: linear-gradient(45deg, #ec4899, #f59e0b);
- top: 30%;
- left: 70%;
- animation: particleFloat3 12s infinite;
-}
-
-.particle-4 {
- width: 10px;
- height: 10px;
- background: linear-gradient(45deg, #f59e0b, #10b981);
- top: 80%;
- left: 20%;
- animation: particleFloat4 9s infinite;
-}
-
-.particle-5 {
- width: 14px;
- height: 14px;
- background: linear-gradient(45deg, #10b981, #06b6d4);
- top: 15%;
- left: 90%;
- animation: particleFloat5 11s infinite;
-}
-
-.particle-6 {
- width: 7px;
- height: 7px;
- background: linear-gradient(45deg, #06b6d4, #6366f1);
- top: 70%;
- left: 5%;
- animation: particleFloat6 13s infinite;
-}
-
-@keyframes particleFloat1 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 25% {
- transform: translateY(-30px) translateX(20px) rotate(90deg);
- }
-
- 50% {
- transform: translateY(-10px) translateX(-15px) rotate(180deg);
- }
-
- 75% {
- transform: translateY(20px) translateX(10px) rotate(270deg);
- }
-}
-
-@keyframes particleFloat2 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 33% {
- transform: translateY(25px) translateX(-20px) rotate(120deg);
- }
-
- 66% {
- transform: translateY(-15px) translateX(25px) rotate(240deg);
- }
-}
-
-@keyframes particleFloat3 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 50% {
- transform: translateY(-40px) translateX(-30px) rotate(180deg);
- }
-}
-
-@keyframes particleFloat4 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 25% {
- transform: translateY(-20px) translateX(30px) rotate(90deg);
- }
-
- 50% {
- transform: translateY(15px) translateX(-10px) rotate(180deg);
- }
-
- 75% {
- transform: translateY(-5px) translateX(-25px) rotate(270deg);
- }
-}
-
-@keyframes particleFloat5 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 40% {
- transform: translateY(30px) translateX(-35px) rotate(144deg);
- }
-
- 80% {
- transform: translateY(-25px) translateX(15px) rotate(288deg);
- }
-}
-
-@keyframes particleFloat6 {
-
- 0%,
- 100% {
- transform: translateY(0px) translateX(0px) rotate(0deg);
- }
-
- 30% {
- transform: translateY(-35px) translateX(25px) rotate(108deg);
- }
-
- 60% {
- transform: translateY(20px) translateX(-20px) rotate(216deg);
- }
-
- 90% {
- transform: translateY(-10px) translateX(35px) rotate(324deg);
- }
-}
-
-.blog-hero-container {
- max-width: 900px;
- margin: 0 auto;
- position: relative;
- z-index: 1;
-}
-
-.hero-content {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 24px;
- position: relative;
-}
-
-.hero-content::before {
- content: "";
- position: absolute;
- top: -60px;
- left: -80px;
- width: 120px;
- height: 120px;
- background: linear-gradient(45deg, #6366f1, #a855f7);
- border-radius: 50%;
- opacity: 0.08;
- animation: floatLeft 6s ease-in-out infinite;
- z-index: -1;
-}
-
-.hero-content::after {
- content: "";
- position: absolute;
- top: 100px;
- right: -60px;
- width: 80px;
- height: 80px;
- background: linear-gradient(45deg, #ec4899, #f59e0b);
- border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%;
- opacity: 0.08;
- animation: floatRight 8s ease-in-out infinite;
- z-index: -1;
-}
-
-@keyframes floatLeft {
-
- 0%,
- 100% {
- transform: translateY(0px) rotate(0deg);
- }
-
- 50% {
- transform: translateY(-25px) rotate(180deg);
- }
-}
-
-@keyframes floatRight {
-
- 0%,
- 100% {
- transform: translateY(0px) rotate(0deg);
- }
-
- 50% {
- transform: translateY(20px) rotate(-180deg);
- }
-}
-
-.blog-main-title {
- font-size: 64px;
- font-weight: 900;
- margin: 0;
- line-height: 1.1;
- color: #1e293b;
- letter-spacing: -0.02em;
- animation: titleSlideUp 1s ease-out;
-}
-
-[data-theme="dark"] .blog-main-title {
- color: #f1f5f9;
-}
-
-@keyframes titleSlideUp {
- 0% {
- opacity: 0;
- transform: translateY(30px);
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-.gradient-text {
- background: linear-gradient(135deg, #6366f1 0%, #a855f7 30%, #ec4899 70%, #f59e0b 100%);
- background-size: 300% 300%;
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- animation: gradientMove 4s ease-in-out infinite;
- position: relative;
-}
-
-@keyframes gradientMove {
-
- 0%,
- 100% {
- background-position: 0% 50%;
- }
-
- 50% {
- background-position: 100% 50%;
- }
-}
-
-.blog-main-subtitle {
- font-size: 18px;
- color: #64748b;
- margin: 0px 0 0;
- line-height: 1.0;
- max-width: 700px;
- font-weight: 400;
-}
-
-[data-theme="dark"] .blog-main-subtitle {
- color: #94a3b8;
-}
-
-/* =====================================================
- LATEST ARTICLES SECTION
- ===================================================== */
-.latest-articles-section {
- padding: 60px 20px 80px;
- background: #F3F3F3;
- position: relative;
- overflow: hidden;
- margin-top: -20px;
-}
-
-[data-theme="dark"] .latest-articles-section {
- background: linear-gradient(135deg, #121212 0%, #1a202c 100%);
-}
-
-.latest-articles-section::before {
- display: none;
-}
-
-.articles-container-wrapper {
- max-width: 1400px;
- margin: 0 auto;
- display: flex;
- flex-direction: column;
- gap: 40px;
- position: relative;
- z-index: 1;
- animation: contentSlideUp 1s ease-out 0.3s both;
-}
-
-@keyframes contentSlideUp {
- 0% {
- opacity: 0;
- transform: translateY(30px);
- }
-
- 100% {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-.articles-main-content {
- flex: 1;
- min-width: 0;
- width: 100%;
-}
-
-/* =====================================================
- SEARCH PANEL
- ===================================================== */
-.blog-search-panel {
- width: min(100%, 980px);
- margin: 0 auto 8px;
- padding: 36px;
- text-align: center;
- background:
- linear-gradient(135deg, rgba(255, 255, 255, 0.95), rgba(248, 250, 252, 0.9)),
- radial-gradient(circle at 15% 20%, rgba(99, 102, 241, 0.12), transparent 36%),
- radial-gradient(circle at 85% 0%, rgba(236, 72, 153, 0.1), transparent 34%);
- border: 1px solid rgba(99, 102, 241, 0.12);
- border-radius: 24px;
- box-shadow: 0 24px 70px rgba(15, 23, 42, 0.08), 0 10px 30px rgba(99, 102, 241, 0.12);
-}
-
-[data-theme="dark"] .blog-search-panel {
- background:
- linear-gradient(135deg, rgba(36, 37, 38, 0.96), rgba(26, 32, 44, 0.94)),
- radial-gradient(circle at 15% 20%, rgba(167, 139, 250, 0.16), transparent 36%),
- radial-gradient(circle at 85% 0%, rgba(236, 72, 153, 0.1), transparent 34%);
- border-color: rgba(167, 139, 250, 0.2);
- box-shadow: 0 24px 70px rgba(0, 0, 0, 0.38), 0 10px 30px rgba(99, 102, 241, 0.18);
-}
-
-.blog-search-eyebrow {
- margin: 0 0 8px;
- color: #6366f1;
- font-size: 13px;
- font-weight: 800;
- letter-spacing: 0.12em;
- text-transform: uppercase;
-}
-
-[data-theme="dark"] .blog-search-eyebrow {
- color: #a78bfa;
-}
-
-.blog-search-title {
- margin: 0 0 24px;
- color: #0f172a;
- font-size: 34px;
- font-weight: 850;
- line-height: 1.15;
-}
-
-[data-theme="dark"] .blog-search-title {
- color: #f8fafc;
-}
-
-
-
-/* Search Results Info */
-.search-results-info {
- text-align: center;
- margin: 0 0 24px;
- padding: 16px 24px;
- background: linear-gradient(135deg, rgba(99, 102, 241, 0.05) 0%, rgba(168, 85, 247, 0.05) 100%);
- border-radius: 12px;
- border: 1px solid rgba(99, 102, 241, 0.1);
-}
-
-.search-results-info p {
- font-size: 15px;
- color: #475569;
- margin: 0;
- font-weight: 600;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8px;
-}
-
-.search-results-info p::before {
- content: "🔍";
- font-size: 16px;
-}
-
-/* No Results */
-.no-results {
- grid-column: 1 / -1;
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 300px;
- text-align: center;
-}
-
-.no-results-content {
- max-width: 400px;
-}
-
-.no-results-icon {
- font-size: 48px;
- margin-bottom: 16px;
- opacity: 0.6;
-}
-
-.no-results h3 {
- font-size: 24px;
- font-weight: 600;
- color: #1e293b;
- margin: 0 0 8px 0;
-}
-
-.no-results p {
- font-size: 16px;
- color: #64748b;
- margin: 0 0 24px 0;
- line-height: 1.5;
-}
-
-.clear-search-btn {
- background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
- color: white;
- border: none;
- padding: 12px 24px;
- border-radius: 8px;
- font-size: 14px;
- font-weight: 600;
- cursor: pointer;
- transition: all 0.2s ease;
- box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
-}
-
-.clear-search-btn:hover {
- transform: translateY(-1px);
- box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4);
-}
-
-/* =====================================================
- ARTICLES GRID
- ===================================================== */
-.articles-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 24px;
- width: 100%;
- max-width: 1300px;
- margin: 0 auto;
- padding: 0 24px;
-}
-
-@media (max-width: 1100px) {
- .articles-grid {
- grid-template-columns: repeat(2, 1fr);
- gap: 20px;
- }
-}
-
-@media (max-width: 600px) {
- .articles-grid {
- grid-template-columns: 1fr;
- gap: 18px;
- padding: 0 16px;
- }
-}
-
-/* =====================================================
- BLOG CARD — Reference layout
- Image → Title → Description → Tags → Footer
- ===================================================== */
-
-/* Card shell */
-.article-card {
- background: #ffffff;
- border: 1px solid #e2e8f0;
- border-radius: 14px;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- cursor: pointer;
- position: relative;
- animation: cardSlideIn 0.5s ease-out both;
- transition: box-shadow 0.25s ease, border-color 0.25s ease, transform 0.25s ease;
-}
-
-.article-card::before {
- display: none !important;
-}
-
-.article-card:hover {
- box-shadow: 0 10px 36px rgba(0, 0, 0, 0.11);
- border-color: #cbd5e1;
- transform: translateY(-3px);
-}
-
-[data-theme="dark"] .article-card {
- background: #1e293b;
- border-color: #2d3f55;
-}
-
-[data-theme="dark"] .article-card:hover {
- box-shadow: 0 10px 36px rgba(0, 0, 0, 0.4);
- border-color: #3d5068;
-}
-
-/* Card image */
-.card-image {
- width: 100%;
- aspect-ratio: 16 / 9;
- overflow: hidden;
- background: #f1f5f9;
- flex-shrink: 0;
- position: relative;
-}
-
-.card-image::before {
- display: none !important;
-}
-
-.card-image img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- display: block;
- transition: transform 0.4s ease;
-}
-
-.article-card:hover .card-image img {
- transform: scale(1.04);
-}
-
-[data-theme="dark"] .card-image {
- background: #0f172a;
-}
-
-/* Card content area */
-.card-content {
- padding: 20px 20px 18px;
- display: flex;
- flex-direction: column;
- gap: 10px;
- flex: 1;
-}
-
-[data-theme="dark"] .card-content {
- background: transparent;
-}
-
-/* Title */
-.card-title {
- font-size: 16px;
- font-weight: 700;
- color: #0f172a;
- margin: 0;
- line-height: 1.4;
- letter-spacing: -0.01em;
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- transition: color 0.2s ease;
-}
-
-.article-card:hover .card-title {
- color: #6366f1;
-}
-
-[data-theme="dark"] .card-title {
- color: #f1f5f9;
-}
-
-[data-theme="dark"] .article-card:hover .card-title {
- color: #a78bfa;
-}
-
-.card-title-link {
- color: inherit;
- text-decoration: none;
-}
-
-/* Stretch link overlay — makes entire card clickable */
-.card-title-link::after {
- content: "";
- position: absolute;
- inset: 0;
- z-index: 1;
-}
-
-.card-title-link:hover {
- color: inherit;
- text-decoration: none;
-}
-
-/* Description */
-.card-description {
- font-size: 13.5px;
- color: #64748b;
- line-height: 1.6;
- margin: 0;
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
- font-weight: 400;
-}
-
-[data-theme="dark"] .card-description {
- color: #94a3b8;
-}
-
-/* ── Tag pills ── */
-.card-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
- margin-top: 2px;
-}
-
-.card-tag {
- display: inline-flex;
- align-items: center;
- gap: 5px;
- padding: 3px 10px 3px 8px;
- border-radius: 20px;
- font-size: 11.5px;
- font-weight: 500;
- background: var(--tag-bg);
- color: var(--tag-text);
- border: 1px solid var(--tag-border);
- white-space: nowrap;
- position: relative;
- z-index: 2;
-}
-
-.card-tag-dot {
- width: 6px;
- height: 6px;
- border-radius: 50%;
- background: var(--tag-dot);
- flex-shrink: 0;
-}
-
-[data-theme="dark"] .card-tag {
- opacity: 0.85;
-}
-
-/* ── Card footer: avatar + author/date + Read → ── */
-.card-footer {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 10px;
- margin-top: auto;
- padding-top: 14px;
- border-top: 1px solid #f1f5f9;
-}
-
-[data-theme="dark"] .card-footer {
- border-top-color: #2d3f55;
-}
-
-.card-author-row {
- display: flex;
- align-items: center;
- gap: 9px;
- min-width: 0;
- position: relative;
- z-index: 2;
-}
-
-/* Avatar stack */
-.card-avatar-stack {
- display: flex;
- align-items: center;
- flex-direction: row;
- flex-shrink: 0;
-}
-
-.card-avatar-stack .card-avatar {
- margin-left: -8px;
- transition: transform 0.2s ease;
-}
-
-.card-avatar-stack .card-avatar:first-child {
- margin-left: 0;
-}
-
-.card-avatar-stack .card-avatar:hover {
- transform: scale(1.1);
- z-index: 10 !important;
-}
-
-/* Avatar */
-.card-avatar {
- width: 34px;
- height: 34px;
- border-radius: 50%;
- overflow: hidden;
- flex-shrink: 0;
- background: #e2e8f0;
- display: flex;
- align-items: center;
- justify-content: center;
- border: 2px solid #ffffff;
- box-shadow: 0 0 0 1px #e2e8f0;
-}
-
-[data-theme="dark"] .card-avatar {
- background: #334155;
- border-color: #1e293b;
- box-shadow: 0 0 0 1px #2d3f55;
-}
-
-.card-avatar-img {
- width: 100%;
- height: 100%;
- object-fit: cover;
- display: block;
-}
-
-.card-avatar-fallback {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(135deg, #6366f1, #8b5cf6);
- color: #ffffff;
- font-size: 13px;
- font-weight: 700;
-}
-
-/* Author name + date stacked */
-.card-author-info {
- display: flex;
- flex-direction: column;
- gap: 1px;
- min-width: 0;
-}
-
-.card-author-names {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 0;
- min-width: 0;
- overflow: hidden;
-}
-
-.card-author-sep {
- color: #94a3b8;
- font-size: 12px;
- white-space: pre;
-}
-
-[data-theme="dark"] .card-author-sep {
- color: #64748b;
-}
-
-.card-author-handle {
- font-size: 12px;
- font-weight: 600;
- color: #374151;
- text-decoration: none;
- white-space: nowrap;
- transition: color 0.2s ease;
- position: relative;
- z-index: 2;
-}
-
-.card-author-handle:hover {
- color: #6366f1;
-}
-
-[data-theme="dark"] .card-author-handle {
- color: #cbd5e1;
-}
-
-[data-theme="dark"] .card-author-handle:hover {
- color: #a78bfa;
-}
-
-.card-date {
- font-size: 11px;
- color: #94a3b8;
- font-weight: 400;
- white-space: nowrap;
-}
-
-[data-theme="dark"] .card-date {
- color: #64748b;
-}
-
-/* Read → link */
-.card-read-link {
- display: inline-flex;
- align-items: center;
- gap: 3px;
- font-size: 13px;
- font-weight: 600;
- color: #6366f1;
- text-decoration: none;
- white-space: nowrap;
- flex-shrink: 0;
- position: relative;
- z-index: 2;
- transition: color 0.2s ease, gap 0.2s ease;
-}
-
-.card-read-link:hover {
- color: #4f46e5;
- gap: 6px;
-}
-
-[data-theme="dark"] .card-read-link {
- color: #a78bfa;
-}
-
-[data-theme="dark"] .card-read-link:hover {
- color: #c084fc;
-}
-
-/* Hide old card elements that are no longer in JSX */
-.card-category {
- display: none !important;
-}
-
-.card-meta {
- display: none !important;
-}
-
-.card-read-more {
- display: none !important;
-}
-
-/* Card slide-in animation */
-@keyframes cardSlideIn {
- from {
- opacity: 0;
- transform: translateY(18px);
- }
-
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-.article-card:nth-child(1) {
- animation-delay: 0.05s;
-}
-
-.article-card:nth-child(2) {
- animation-delay: 0.10s;
-}
-
-.article-card:nth-child(3) {
- animation-delay: 0.15s;
-}
-
-.article-card:nth-child(4) {
- animation-delay: 0.20s;
-}
-
-.article-card:nth-child(5) {
- animation-delay: 0.25s;
-}
-
-.article-card:nth-child(6) {
- animation-delay: 0.30s;
-}
-
-/* =====================================================
- PAGINATION
- ===================================================== */
-.pagination-wrapper {
- margin-top: 3rem;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-
-.pagination-container {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 12px;
- flex-wrap: wrap;
-}
-
-.pagination-pages {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.pagination-number {
- height: 42px;
- width: 42px;
- padding: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 8px;
- border: 1px solid #d1d5db;
- background-color: #ffffff;
- box-shadow: none;
- appearance: none;
- -webkit-appearance: none;
- color: #111827;
- font-size: 14px;
- font-weight: 600;
- cursor: pointer;
- transition: all 0.2s ease;
-}
-
-.pagination-btn {
- height: 42px;
- min-width: 90px;
- padding: 0 14px;
- display: inline-flex;
- align-items: center;
- justify-content: center;
- border: 1px solid #d1d5db !important;
- border-radius: 8px !important;
- background: #ffffff !important;
- box-shadow: none !important;
- appearance: none;
- -webkit-appearance: none;
- color: #111827;
- font-size: 14px;
- font-weight: 600;
- line-height: 1;
- cursor: pointer;
- transition: all 0.2s ease;
-}
-
-.pagination-btn:hover,
-.pagination-number:hover {
- border-color: #9ca3af;
- transform: translateY(-1px);
-}
-
-.active-page {
- background: #000000 !important;
- color: #ffffff !important;
- border-color: #000000 !important;
- box-shadow: 0 6px 18px rgba(15, 23, 42, 0.16);
- transform: translateY(-1px);
-}
-
-.active-page:hover {
- background: #111111 !important;
- box-shadow: 0 8px 20px rgba(15, 23, 42, 0.22);
-}
-
-.pagination-btn:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- transform: none;
-}
-
-.pagination-summary {
- margin-top: 12px;
- font-size: 13px;
- color: #6b7280;
- text-align: center;
-}
-
-.pagination-ellipsis {
- font-size: 14px;
- color: #94a3b8;
- padding: 0 4px;
-}
-
-/* =====================================================
- RESPONSIVE
- ===================================================== */
-
-/* Search panel */
-@media (max-width: 767px) {
- .blog-search-panel {
- padding: 28px;
- }
-
- .blog-search-title {
- font-size: 28px;
- }
-
- .blog-search-form {
- grid-template-columns: 1fr;
- }
-
- .blog-search-button,
- .blog-search-clear-button {
- width: 100%;
- }
-}
-
-@media (max-width: 575px) {
- .blog-search-panel {
- padding: 24px 18px;
- border-radius: 18px;
- }
-
- .blog-search-title {
- font-size: 24px;
- margin-bottom: 18px;
- }
-
- .blog-search-form {
- gap: 10px;
- }
-
- .blog-search-field input,
- .blog-search-button,
- .blog-search-clear-button {
- min-height: 50px;
- }
-}
-
-/* Hero */
-@media (max-width: 991px) {
- .blog-hero-section {
- padding: 60px 20px 50px;
- }
-
- .blog-main-title {
- font-size: 40px;
- }
-}
-
-@media (max-width: 767px) {
- .blog-hero-section {
- padding: 50px 16px 40px;
- }
-
- .blog-main-title {
- font-size: 32px;
- }
-
- .blog-main-subtitle {
- font-size: 16px;
- }
-}
-
-@media (max-width: 575px) {
- .blog-hero-section {
- padding: 40px 16px 32px;
- }
-
- .blog-main-title {
- font-size: 28px;
- line-height: 1.2;
- }
-
- .blog-main-subtitle {
- font-size: 15px;
- }
-}
-
-/* Card responsive tweaks */
-@media (max-width: 767px) {
- .card-title {
- font-size: 15px;
- }
-
- .card-description {
- font-size: 13px;
- }
-}
-
-/* Pagination */
-@media (max-width: 640px) {
- .pagination-container {
- gap: 8px;
- }
-
- .pagination-pages {
- gap: 6px;
- }
-
- .pagination-number {
- height: 36px;
- width: 36px;
- font-size: 13px;
- }
-
- .pagination-btn {
- height: 36px;
- min-width: 104px;
- padding: 0 18px;
- font-size: 13px;
- }
-}
-
-/* ── Reading time ── */
-.card-reading-time {
- display: inline-flex;
- align-items: center;
- gap: 3px;
- font-size: 0.75rem;
- color: var(--ifm-color-emphasis-600);
- margin-top: 2px;
-}
\ No newline at end of file
diff --git a/src/pages/blogs/blogs.css b/src/pages/blogs/blogs.css
deleted file mode 100644
index 76ad521e1..000000000
--- a/src/pages/blogs/blogs.css
+++ /dev/null
@@ -1,1186 +0,0 @@
-/* Modern Blog Page Styles */
-@import url("https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap");
-
-/* CSS Variables for Design System */
-:root {
- --blog-primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- --blog-secondary-gradient: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
- --blog-accent-gradient: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
- --blog-purple-gradient: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
- --blog-blue-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
- --blog-pink-gradient: linear-gradient(135deg, #ffecd2 0%, #fcb69f 100%);
-
- --blog-text-primary: #1a202c;
- --blog-text-secondary: #4a5568;
- --blog-text-muted: #718096;
- --blog-bg-primary: #ffffff;
- --blog-bg-secondary: #f7fafc;
- --blog-bg-card: #ffffff;
- --blog-border: #e2e8f0;
- --blog-shadow:
- 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
- --blog-shadow-lg:
- 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
- --blog-shadow-xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
-
- --blog-spacing-xs: 4px;
- --blog-spacing-sm: 8px;
- --blog-spacing-md: 16px;
- --blog-spacing-lg: 24px;
- --blog-spacing-xl: 32px;
- --blog-spacing-2xl: 48px;
- --blog-spacing-3xl: 64px;
-
- --blog-radius-sm: 8px;
- --blog-radius-md: 12px;
- --blog-radius-lg: 16px;
- --blog-radius-xl: 24px;
-
- --blog-transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- --blog-transition-fast: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
- --blog-transition-slow: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-/* Dark Mode Variables - Using standardized colors */
-[data-theme="dark"] {
- --blog-text-primary: var(--dark-text-primary, #f7fafc);
- --blog-text-secondary: var(--dark-text-secondary, #cbd5e0);
- --blog-text-muted: var(--dark-text-muted, #a0aec0);
-
- --blog-bg-primary: var(--dark-bg-primary, #0f172a);
- --blog-bg-secondary: var(--dark-bg-secondary, #111827);
- --blog-bg-card: var(--dark-card-bg, #1f2937);
-
- --blog-border: var(--dark-border, #2d3748);
-
- --blog-shadow: var(--dark-shadow, 0 4px 6px rgba(0,0,0,0.4));
- --blog-shadow-lg: var(--dark-shadow-lg, 0 20px 25px rgba(0,0,0,0.5));
- --blog-shadow-xl: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
-
-/* Global Styles */
-* {
- box-sizing: border-box;
-}
-
-body {
- font-family:
- "Inter",
- -apple-system,
- BlinkMacSystemFont,
- "Segoe UI",
- Roboto,
- sans-serif;
- line-height: 1.6;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-/* Custom Scrollbar */
-::-webkit-scrollbar {
- width: 8px;
-}
-
-::-webkit-scrollbar-track {
- background: var(--blog-bg-secondary);
-}
-
-::-webkit-scrollbar-thumb {
- background: var(--blog-primary-gradient);
- border-radius: 4px;
-}
-
-::-webkit-scrollbar-thumb:hover {
- background: var(--blog-secondary-gradient);
-}
-
-/* Animated Background */
-.blog-background {
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -1;
- overflow: hidden;
-}
-
-.blog-background-gradient {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: var(--blog-bg-primary);
- background-image:
- radial-gradient(
- circle at 20% 80%,
- rgba(120, 119, 198, 0.1) 0%,
- transparent 50%
- ),
- radial-gradient(
- circle at 80% 20%,
- rgba(255, 119, 198, 0.1) 0%,
- transparent 50%
- ),
- radial-gradient(
- circle at 40% 40%,
- rgba(120, 219, 255, 0.1) 0%,
- transparent 50%
- );
-}
-
-.blog-background-shapes {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
-}
-
-.shape {
- position: absolute;
- border-radius: 50%;
- opacity: 0.1;
- animation: float 6s ease-in-out infinite;
-}
-
-.shape-1 {
- width: 200px;
- height: 200px;
- background: var(--blog-primary-gradient);
- top: 10%;
- left: 10%;
- animation-delay: 0s;
-}
-
-.shape-2 {
- width: 150px;
- height: 150px;
- background: var(--blog-secondary-gradient);
- top: 60%;
- right: 10%;
- animation-delay: 2s;
-}
-
-.shape-3 {
- width: 100px;
- height: 100px;
- background: var(--blog-accent-gradient);
- bottom: 20%;
- left: 50%;
- animation-delay: 4s;
-}
-
-@keyframes float {
- 0%,
- 100% {
- transform: translateY(0px) rotate(0deg);
- }
- 50% {
- transform: translateY(-20px) rotate(180deg);
- }
-}
-
-/* Hero Section */
-.blog-hero {
- position: relative;
- padding: var(--blog-spacing-3xl) var(--blog-spacing-lg);
- min-height: 70vh;
- display: flex;
- align-items: center;
- justify-content: center;
- text-align: center;
- overflow: hidden;
-}
-
-.blog-hero::before {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: var(--blog-purple-gradient);
- opacity: 0.05;
- z-index: -1;
-}
-
-.blog-hero-content {
- max-width: 800px;
- margin: 0 auto;
- z-index: 1;
-}
-
-.blog-hero-badge {
- display: inline-flex;
- align-items: center;
- padding: var(--blog-spacing-sm) var(--blog-spacing-md);
- background: rgba(255, 255, 255, 0.1);
- backdrop-filter: blur(10px);
- border: 1px solid rgba(255, 255, 255, 0.2);
- border-radius: 50px;
- margin-bottom: var(--blog-spacing-lg);
- animation: fadeInUp 0.6s ease-out;
-}
-
-.blog-hero-badge-text {
- font-size: 14px;
- font-weight: 500;
- color: var(--blog-text-primary);
-}
-
-.blog-hero-title {
- font-size: clamp(2.5rem, 5vw, 4rem);
- font-weight: 800;
- line-height: 1.1;
- margin-bottom: var(--blog-spacing-lg);
- color: var(--blog-text-primary);
- animation: fadeInUp 0.6s ease-out 0.1s both;
-}
-
-.blog-hero-highlight {
- background: var(--blog-primary-gradient);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
- position: relative;
-}
-
-.blog-hero-description {
- font-size: 1.25rem;
- color: var(--blog-text-secondary);
- margin-bottom: var(--blog-spacing-2xl);
- max-width: 600px;
- margin-left: auto;
- margin-right: auto;
- animation: fadeInUp 0.6s ease-out 0.2s both;
-}
-
-/* Search Container */
-.blog-search-container {
- margin-bottom: var(--blog-spacing-xl);
- animation: fadeInUp 0.6s ease-out 0.3s both;
-}
-
-.blog-search-wrapper {
- position: relative;
- max-width: 500px;
- margin: 0 auto;
-}
-
-.blog-search-icon {
- position: absolute;
- left: var(--blog-spacing-md);
- top: 50%;
- transform: translateY(-50%);
- width: 20px;
- height: 20px;
- color: var(--blog-text-muted);
- z-index: 2;
-}
-
-.blog-search-input {
- width: 100%;
- padding: var(--blog-spacing-md) var(--blog-spacing-md) var(--blog-spacing-md)
- 48px;
- font-size: 16px;
- border: 2px solid var(--blog-border);
- border-radius: var(--blog-radius-lg);
- background: var(--blog-bg-card);
- color: var(--blog-text-primary);
- transition: var(--blog-transition);
- backdrop-filter: blur(10px);
-}
-
-.blog-search-input:focus {
- outline: none;
- border-color: transparent;
- box-shadow:
- 0 0 0 3px rgba(102, 126, 234, 0.1),
- var(--blog-shadow-lg);
- background: var(--blog-bg-card);
-}
-
-.blog-search-input::placeholder {
- color: var(--blog-text-muted);
-}
-
-/* Category Icons */
-.blog-category-icons {
- display: flex;
- justify-content: center;
- gap: var(--blog-spacing-xl);
- margin-bottom: var(--blog-spacing-2xl);
- animation: fadeInUp 0.6s ease-out 0.4s both;
-}
-
-.category-icon {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: var(--blog-spacing-md);
- cursor: pointer;
- transition: var(--blog-transition);
-}
-
-.category-icon:hover {
- transform: translateY(-4px);
-}
-
-.icon-wrapper {
- width: 64px;
- height: 64px;
- background: white;
- border-radius: 20px;
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
- transition: var(--blog-transition);
-}
-
-.icon-wrapper svg {
- width: 28px;
- height: 28px;
-}
-
-.icon-wrapper.development svg {
- color: #4f46e5;
-}
-
-.icon-wrapper.design svg {
- color: #ec4899;
-}
-
-.icon-wrapper.ai-tech svg {
- color: #ef4444;
-}
-
-.icon-wrapper.innovation svg {
- color: #8b5cf6;
-}
-
-.category-icon span {
- font-size: 14px;
- font-weight: 600;
- color: var(--blog-text-primary);
- text-align: center;
-}
-
-.category-icon:hover .icon-wrapper {
- transform: scale(1.1);
- box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
-}
-
-/* Explore Articles Button */
-.explore-articles-btn {
- display: inline-flex;
- align-items: center;
- gap: var(--blog-spacing-sm);
- padding: var(--blog-spacing-md) var(--blog-spacing-xl);
- background: var(--blog-primary-gradient);
- color: white;
- border: none;
- border-radius: 50px;
- font-size: 16px;
- font-weight: 600;
- cursor: pointer;
- transition: var(--blog-transition);
- box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
- margin-top: var(--blog-spacing-xl);
- animation: fadeInUp 0.6s ease-out 0.5s both;
-}
-
-.explore-articles-btn:hover {
- transform: translateY(-2px);
- box-shadow: 0 12px 40px rgba(102, 126, 234, 0.4);
-}
-
-/* Category Filter */
-.blog-categories {
- display: flex;
- flex-wrap: wrap;
- gap: var(--blog-spacing-sm);
- justify-content: center;
- animation: fadeInUp 0.6s ease-out 0.4s both;
-}
-
-.blog-category-btn {
- padding: var(--blog-spacing-sm) var(--blog-spacing-md);
- border: 2px solid var(--blog-border);
- background: var(--blog-bg-card);
- color: var(--blog-text-secondary);
- border-radius: var(--blog-radius-lg);
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: var(--blog-transition);
- backdrop-filter: blur(10px);
-}
-
-.blog-category-btn:hover {
- border-color: rgba(102, 126, 234, 0.3);
- background: rgba(102, 126, 234, 0.05);
- transform: translateY(-2px);
-}
-
-.blog-category-btn.active {
- background: var(--blog-primary-gradient);
- border-color: transparent;
- color: white;
- box-shadow: var(--blog-shadow);
-}
-
-/* Animation Keyframes */
-@keyframes fadeInUp {
- from {
- opacity: 0;
- transform: translateY(30px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-@keyframes fadeIn {
- from {
- opacity: 0;
- }
- to {
- opacity: 1;
- }
-}
-
-@keyframes slideInUp {
- from {
- opacity: 0;
- transform: translateY(50px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-@keyframes pulse {
- 0%,
- 100% {
- opacity: 1;
- }
- 50% {
- opacity: 0.5;
- }
-}
-
-/* Blog Grid Section */
-.blog-grid-section {
- padding: var(--blog-spacing-3xl) var(--blog-spacing-lg);
- position: relative;
-}
-
-.blog-grid-container {
- max-width: 1200px;
- margin: 0 auto;
-}
-
-.section-header {
- text-align: center;
- margin-bottom: var(--blog-spacing-3xl);
-}
-
-.section-title {
- font-size: clamp(2rem, 4vw, 3rem);
- font-weight: 800;
- color: var(--blog-text-primary);
- margin-bottom: var(--blog-spacing-md);
- animation: fadeInUp 0.6s ease-out;
-}
-
-.section-subtitle {
- font-size: 1.125rem;
- color: var(--blog-text-secondary);
- margin-bottom: var(--blog-spacing-xl);
- line-height: 1.6;
- animation: fadeInUp 0.6s ease-out 0.1s both;
-}
-
-/* Search Container for Latest Articles */
-.search-container {
- position: relative;
- max-width: 500px;
- margin: 0 auto;
- animation: fadeInUp 0.6s ease-out 0.2s both;
-}
-
-.search-wrapper {
- position: relative;
-}
-
-.search-icon {
- position: absolute;
- left: var(--blog-spacing-md);
- top: 50%;
- transform: translateY(-50%);
- width: 20px;
- height: 20px;
- color: var(--blog-text-muted);
- z-index: 2;
-}
-
-.search-input {
- width: 100%;
- padding: var(--blog-spacing-md) var(--blog-spacing-md) var(--blog-spacing-md)
- 48px;
- font-size: 16px;
- border: 2px solid var(--blog-border);
- border-radius: var(--blog-radius-lg);
- background: var(--blog-bg-card);
- color: var(--blog-text-primary);
- transition: var(--blog-transition);
- backdrop-filter: blur(10px);
-}
-
-.search-input:focus {
- outline: none;
- border-color: transparent;
- box-shadow:
- 0 0 0 3px rgba(102, 126, 234, 0.1),
- var(--blog-shadow-lg);
- background: var(--blog-bg-card);
-}
-
-.search-input::placeholder {
- color: var(--blog-text-muted);
-}
-
-/* Search Decoration */
-.search-decoration {
- position: absolute;
- right: -60px;
- top: 50%;
- transform: translateY(-50%);
- display: flex;
- align-items: center;
- gap: 8px;
-}
-
-.search-line {
- width: 40px;
- height: 2px;
- background: linear-gradient(90deg, #667eea, #764ba2);
- border-radius: 1px;
-}
-
-.search-dot {
- width: 8px;
- height: 8px;
- background: #ec4899;
- border-radius: 50%;
-}
-
-.search-circle {
- width: 12px;
- height: 12px;
- border: 2px solid #8b5cf6;
- border-radius: 50%;
- background: transparent;
-}
-
-.blog-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
- gap: var(--blog-spacing-xl);
- margin-bottom: var(--blog-spacing-2xl);
-}
-
-/* Blog Card Styles */
-.blog-card {
- position: relative;
- opacity: 0;
- transform: translateY(30px);
- animation: slideInUp 0.6s ease-out forwards;
-}
-
-.blog-card-animate {
- animation-delay: var(--animation-delay, 0ms);
-}
-
-.blog-card-inner {
- display: flex;
- flex-direction: column;
- height: 100%;
- border-radius: var(--blog-radius-xl);
- overflow: hidden;
- background: var(--blog-bg-card);
- border: 1px solid var(--blog-border);
- transition: var(--blog-transition);
-
-}
-
-.blog-card:hover .blog-card-inner {
- transform: translateY(-8px);
- box-shadow: var(--blog-shadow-xl);
- border-color: rgba(102, 126, 234, 0.2);
-}
-
-.blog-card-link {
- display: block;
- height: 100%;
- text-decoration: none;
- color: inherit;
-}
-
-.blog-card-image-container {
- position: relative;
- height: 240px;
- overflow: hidden;
- flex-shrink: 0;
-}
-
-.blog-card-image {
- width: 100%;
- height: 100%;
- object-fit: cover;
- transition: var(--blog-transition-slow);
-}
-
-.blog-card:hover .blog-card-image {
- transform: scale(1.05);
-}
-
-.blog-card-overlay {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: linear-gradient(
- 180deg,
- transparent 0%,
- transparent 60%,
- rgba(0, 0, 0, 0.1) 100%
- );
- opacity: 0;
- transition: var(--blog-transition);
-}
-
-.blog-card:hover .blog-card-overlay {
- opacity: 1;
-}
-
-.blog-card-hover-icon {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%) scale(0);
- width: 48px;
- height: 48px;
- background: rgba(255, 255, 255, 0.9);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: var(--blog-transition);
- backdrop-filter: blur(10px);
-}
-
-.blog-card:hover .blog-card-hover-icon {
- transform: translate(-50%, -50%) scale(1);
-}
-
-.blog-card-hover-icon svg {
- width: 20px;
- height: 20px;
- color: var(--blog-text-primary);
-}
-
-.blog-card-content {
- padding: var(--blog-spacing-xl);
- height: calc(100% - 240px);
- display: flex;
- flex-direction: column;
- flex: 1;
-}
-
-.blog-card-category {
- margin-bottom: var(--blog-spacing-md);
-}
-
-.blog-card-category-badge {
- display: inline-block;
- padding: var(--blog-spacing-xs) var(--blog-spacing-sm);
- background: var(--blog-primary-gradient);
- color: white;
- font-size: 12px;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- border-radius: var(--blog-radius-sm);
-}
-
-.blog-card-title {
- font-size: 1.5rem;
- font-weight: 700;
- line-height: 1.3;
- color: var(--blog-text-primary);
- margin-bottom: var(--blog-spacing-md);
- transition: var(--blog-transition);
-}
-
-.blog-card:hover .blog-card-title {
- color: #667eea;
-}
-
-.blog-card-description {
- color: var(--blog-text-secondary);
- line-height: 1.6;
- margin-bottom: var(--blog-spacing-lg);
- flex-grow: 1;
-}
-
-.blog-card-footer {
- margin-top: auto;
-}
-
-.blog-card-read-more {
- display: inline-flex;
- align-items: center;
- gap: var(--blog-spacing-sm);
- color: #667eea;
- font-weight: 600;
- font-size: 14px;
- text-transform: uppercase;
- letter-spacing: 0.5px;
- transition: var(--blog-transition);
-}
-
-.blog-card:hover .blog-card-read-more {
- gap: var(--blog-spacing-md);
-}
-
-.blog-card-arrow {
- width: 16px;
- height: 16px;
- transition: var(--blog-transition);
-}
-
-.blog-card:hover .blog-card-arrow {
- transform: translateX(4px);
-}
-
-/* No Results */
-.blog-no-results {
- text-align: center;
- padding: var(--blog-spacing-3xl);
- animation: fadeIn 0.6s ease-out;
-}
-
-.blog-no-results-icon {
- font-size: 4rem;
- margin-bottom: var(--blog-spacing-lg);
- opacity: 0.5;
-}
-
-.blog-no-results-title {
- font-size: 1.5rem;
- font-weight: 600;
- color: var(--blog-text-primary);
- margin-bottom: var(--blog-spacing-sm);
-}
-
-.blog-no-results-text {
- color: var(--blog-text-secondary);
- font-size: 1rem;
-}
-
-/* CTA Section */
-.blog-cta-section {
- padding: var(--blog-spacing-3xl) var(--blog-spacing-lg);
- position: relative;
- overflow: hidden;
-}
-
-.blog-cta-section::before {
- content: "";
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: var(--blog-blue-gradient);
- opacity: 0.05;
- z-index: -1;
-}
-
-.blog-cta-container {
- max-width: 800px;
- margin: 0 auto;
- text-align: center;
-}
-
-.blog-cta-content {
- padding: var(--blog-spacing-2xl);
- background: var(--blog-bg-card);
- border-radius: var(--blog-radius-xl);
- border: 1px solid var(--blog-border);
- box-shadow: var(--blog-shadow-lg);
- backdrop-filter: blur(10px);
-}
-
-.blog-cta-title {
- font-size: 2.5rem;
- font-weight: 800;
- color: var(--blog-text-primary);
- margin-bottom: var(--blog-spacing-md);
- background: var(--blog-primary-gradient);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-.blog-cta-description {
- font-size: 1.125rem;
- color: var(--blog-text-secondary);
- margin-bottom: var(--blog-spacing-xl);
- line-height: 1.6;
-}
-
-.blog-cta-buttons {
- display: flex;
- gap: var(--blog-spacing-md);
- justify-content: center;
- flex-wrap: wrap;
-}
-
-.blog-cta-btn {
- display: inline-flex;
- align-items: center;
- gap: var(--blog-spacing-sm);
- padding: var(--blog-spacing-md) var(--blog-spacing-xl);
- border-radius: var(--blog-radius-lg);
- font-weight: 600;
- text-decoration: none;
- transition: var(--blog-transition);
- position: relative;
- overflow: hidden;
-}
-
-.blog-cta-btn::before {
- content: "";
- position: absolute;
- top: 0;
- left: -100%;
- width: 100%;
- height: 100%;
- background: linear-gradient(
- 90deg,
- transparent,
- rgba(255, 255, 255, 0.2),
- transparent
- );
- transition: left 0.5s;
-}
-
-.blog-cta-btn:hover::before {
- left: 100%;
-}
-
-.blog-cta-btn-primary {
- background: var(--blog-primary-gradient);
- color: white;
- box-shadow: var(--blog-shadow);
-}
-
-.blog-cta-btn-primary:hover {
- transform: translateY(-2px);
- box-shadow: var(--blog-shadow-lg);
- color: white;
-}
-
-.blog-cta-btn-secondary {
- background: var(--blog-bg-card);
- color: var(--blog-text-primary);
- border: 2px solid var(--blog-border);
-}
-
-.blog-cta-btn-secondary:hover {
- background: var(--blog-bg-secondary);
- border-color: rgba(102, 126, 234, 0.3);
- transform: translateY(-2px);
- color: var(--blog-text-primary);
-}
-
-.blog-cta-btn-icon {
- width: 16px;
- height: 16px;
- transition: var(--blog-transition);
-}
-
-.blog-cta-btn:hover .blog-cta-btn-icon {
- transform: translateX(4px);
-}
-
-/* Responsive Design */
-@media (max-width: 1024px) {
- .blog-grid {
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: var(--blog-spacing-lg);
- }
-
- .blog-hero {
- padding: var(--blog-spacing-2xl) var(--blog-spacing-md);
- min-height: 60vh;
- }
-
- .blog-hero-title {
- font-size: clamp(2rem, 4vw, 3rem);
- }
-
- .blog-cta-title {
- font-size: 2rem;
- }
-}
-
-@media (max-width: 768px) {
- .blog-grid {
- grid-template-columns: 1fr;
- gap: var(--blog-spacing-md);
- }
-
- .blog-hero {
- padding: var(--blog-spacing-xl) var(--blog-spacing-md);
- min-height: 50vh;
- }
-
- .blog-hero-title {
- font-size: clamp(1.75rem, 6vw, 2.5rem);
- }
-
- .blog-hero-description {
- font-size: 1rem;
- margin-bottom: var(--blog-spacing-xl);
- }
-
- .blog-search-input {
- font-size: 16px; /* Prevent zoom on iOS */
- }
-
- .blog-categories {
- gap: var(--blog-spacing-xs);
- }
-
- .blog-category-btn {
- padding: var(--blog-spacing-xs) var(--blog-spacing-sm);
- font-size: 12px;
- }
-
- /* Category Icons Mobile */
- .blog-category-icons {
- gap: var(--blog-spacing-lg);
- flex-wrap: wrap;
- }
-
- .icon-wrapper {
- width: 56px;
- height: 56px;
- }
-
- .icon-wrapper svg {
- width: 24px;
- height: 24px;
- }
-
- .category-icon span {
- font-size: 12px;
- }
-
- /* Search Container Mobile */
- .search-decoration {
- display: none;
- }
-
- .search-input {
- font-size: 16px; /* Prevent zoom on iOS */
- }
-
- .blog-card-content {
- padding: var(--blog-spacing-lg);
- }
-
- .blog-card-title {
- font-size: 1.25rem;
- }
-
- .blog-cta-section {
- padding: var(--blog-spacing-2xl) var(--blog-spacing-md);
- }
-
- .blog-cta-content {
- padding: var(--blog-spacing-xl);
- }
-
- .blog-cta-title {
- font-size: 1.75rem;
- }
-
- .blog-cta-description {
- font-size: 1rem;
- }
-
- .blog-cta-buttons {
- flex-direction: column;
- align-items: center;
- }
-
- .blog-cta-btn {
- width: 100%;
- max-width: 280px;
- justify-content: center;
- }
-}
-
-@media (max-width: 480px) {
- .blog-hero {
- padding: var(--blog-spacing-lg) var(--blog-spacing-sm);
- }
-
- .blog-grid-section {
- padding: var(--blog-spacing-2xl) var(--blog-spacing-sm);
- }
-
- .blog-grid {
- gap: var(--blog-spacing-sm);
- }
-
- /* Category Icons Small Mobile */
- .blog-category-icons {
- gap: var(--blog-spacing-md);
- }
-
- .icon-wrapper {
- width: 48px;
- height: 48px;
- border-radius: 16px;
- }
-
- .icon-wrapper svg {
- width: 20px;
- height: 20px;
- }
-
- .category-icon span {
- font-size: 11px;
- }
-
- .explore-articles-btn {
- padding: var(--blog-spacing-sm) var(--blog-spacing-lg);
- font-size: 14px;
- }
-
- .blog-card-image-container {
- height: 200px;
- }
-
- .blog-card-content {
- padding: var(--blog-spacing-md);
- height: calc(100% - 200px);
- }
-
- .blog-cta-section {
- padding: var(--blog-spacing-xl) var(--blog-spacing-sm);
- }
-
- .blog-cta-content {
- padding: var(--blog-spacing-lg);
- }
-
- .shape-1 {
- width: 120px;
- height: 120px;
- }
-
- .shape-2 {
- width: 100px;
- height: 100px;
- }
-
- .shape-3 {
- width: 80px;
- height: 80px;
- }
-}
-
-/* Accessibility Improvements */
-@media (prefers-reduced-motion: reduce) {
- *,
- *::before,
- *::after {
- animation-duration: 0.01ms !important;
- animation-iteration-count: 1 !important;
- transition-duration: 0.01ms !important;
- }
-
- .shape {
- animation: none;
- }
-}
-
-/* Focus States for Accessibility */
-.blog-search-input:focus,
-.blog-category-btn:focus,
-.blog-cta-btn:focus {
- outline: 2px solid #667eea;
- outline-offset: 2px;
-}
-
-.blog-card-link:focus {
- outline: 2px solid #667eea;
- outline-offset: 4px;
- border-radius: var(--blog-radius-xl);
-}
-
-/* High Contrast Mode Support */
-@media (prefers-contrast: high) {
- :root {
- --blog-border: #000000;
- --blog-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);
- }
-
- [data-theme="dark"] {
- --blog-border: #ffffff;
- }
-
- .blog-card-inner {
- border-width: 2px;
- }
-
- .blog-category-btn {
- border-width: 2px;
- }
-}
-
-/* Print Styles */
-@media print {
- .blog-background,
- .blog-search-container,
- .blog-categories,
- .blog-cta-section {
- display: none;
- }
-
- .blog-hero {
- padding: 1rem 0;
- min-height: auto;
- }
-
- .blog-card {
- break-inside: avoid;
- margin-bottom: 1rem;
- }
-
- .blog-card-inner {
- box-shadow: none;
- border: 1px solid #000;
- }
-}
diff --git a/src/pages/blogs/index.tsx b/src/pages/blogs/index.tsx
index 2f6ddfc65..d79b2c04c 100644
--- a/src/pages/blogs/index.tsx
+++ b/src/pages/blogs/index.tsx
@@ -1,419 +1,75 @@
-import React from "react";
-import useDocusaurusContext from "@docusaurus/useDocusaurusContext";
-import Layout from "@theme/Layout";
-import Link from "@docusaurus/Link";
-import { useHistory } from "@docusaurus/router";
-import blogs from "../../database/blogs/index";
-import Head from "@docusaurus/Head";
-import { getAuthorProfiles } from "../../utils/authors";
-import { filterBlogsBySearchTerm } from "../../utils/blogFilters";
-import BlogSearch from "../../components/BlogSearch";
-
-import "./blogs-new.css";
-
-const POSTS_PER_PAGE = 12;
-const BLOG_DESCRIPTION =
- "Practical guides and honest field notes on data engineering, cloud platforms, GitHub, AI tools, and modern software development from the recode hive community.";
-
-// Stable color per tag label (cycles through palette)
-const TAG_COLORS = [
- { dot: "#f59e0b", border: "#fde68a", bg: "#fffbeb", text: "#92400e" },
- { dot: "#6366f1", border: "#c7d2fe", bg: "#eef2ff", text: "#3730a3" },
- { dot: "#ec4899", border: "#fbcfe8", bg: "#fdf2f8", text: "#9d174d" },
- { dot: "#10b981", border: "#a7f3d0", bg: "#ecfdf5", text: "#065f46" },
- { dot: "#3b82f6", border: "#bfdbfe", bg: "#eff6ff", text: "#1e40af" },
- { dot: "#8b5cf6", border: "#ddd6fe", bg: "#f5f3ff", text: "#5b21b6" },
- { dot: "#f97316", border: "#fed7aa", bg: "#fff7ed", text: "#9a3412" },
- { dot: "#14b8a6", border: "#99f6e4", bg: "#f0fdfa", text: "#134e4a" },
-];
-
-function tagColor(label: string) {
- let hash = 0;
- for (let i = 0; i < label.length; i++)
- hash = label.charCodeAt(i) + ((hash << 5) - hash);
- return TAG_COLORS[Math.abs(hash) % TAG_COLORS.length];
-}
-
-function formatDate(dateStr?: string) {
- if (!dateStr) return "";
- const d = new Date(dateStr);
- if (isNaN(d.getTime())) return dateStr;
- return d.toLocaleDateString("en-US", {
- month: "short",
- day: "2-digit",
- year: "numeric",
- });
-}
-
-export default function Blogs() {
+import React, { useEffect, useMemo, useState } from 'react';
+import Layout from '@theme/Layout';
+import Head from '@docusaurus/Head';
+import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
+import { filterBlogsBySearchTerm } from '../../utils/blogFilters';
+import { TOPICS, hasTag, usePosts, type Post } from '../../components/blogsPage/blogData';
+import Hero from '../../components/blogsPage/Hero';
+import { ChangelogStrip, Featured, Latest, Spotlight, WriteBanner } from '../../components/blogsPage/Sections';
+import styles from '../../components/blogsPage/blogsPage.module.css';
+
+const DESCRIPTION =
+ 'Practical guides and honest field notes on data engineering, cloud platforms, GitHub, AI tools, and modern software development from the recode hive community.';
+
+export default function Blogs(): React.JSX.Element {
const { siteConfig } = useDocusaurusContext();
- const [searchTerm, setSearchTerm] = React.useState("");
- const [currentPage, setCurrentPage] = React.useState(1);
-
- const filteredBlogs = React.useMemo(() => {
- const sortedBlogs = [...blogs].sort((a, b) => b.id - a.id);
- return filterBlogsBySearchTerm(sortedBlogs, searchTerm);
- }, [searchTerm, blogs]);
-
- React.useEffect(() => {
- setCurrentPage(1);
- }, [searchTerm]);
-
- const totalPages = Math.max(1, Math.ceil(filteredBlogs.length / POSTS_PER_PAGE));
- const showingStart = filteredBlogs.length === 0 ? 0 : (currentPage - 1) * POSTS_PER_PAGE + 1;
- const showingEnd = Math.min(currentPage * POSTS_PER_PAGE, filteredBlogs.length);
- const paginatedBlogs = filteredBlogs.slice(
- (currentPage - 1) * POSTS_PER_PAGE,
- currentPage * POSTS_PER_PAGE
- );
-
- const visiblePages = (() => {
- const pages: number[] = [];
- const maxVisible = 5;
- let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
- let end = start + maxVisible - 1;
- if (end > totalPages) {
- end = totalPages;
- start = Math.max(1, end - maxVisible + 1);
- }
- for (let i = start; i <= end; i++) pages.push(i);
- return pages;
- })();
-
- const showLastPage = visiblePages[visiblePages.length - 1] < totalPages;
-
- const handleClearFilters = () => {
- setSearchTerm("");
+ const posts = usePosts();
+ const [query, setQuery] = useState('');
+ const [topic, setTopic] = useState('All');
+ const [page, setPage] = useState(1);
+
+ const filtered = useMemo(() => {
+ const t = TOPICS.find((x) => x.label === topic);
+ const byTopic = t ? posts.filter((p) => hasTag(p, t.tags)) : posts;
+ // Reuse the site's search filter. It looks at title, description and tags.
+ const searchable = byTopic.map((p) => ({ ...p, tags: p.tags.map((x) => x.label), category: p.category }));
+ const ids = new Set(filterBlogsBySearchTerm(searchable as never[], query).map((p: Post) => p.id));
+ return byTopic.filter((p) => ids.has(p.id));
+ }, [posts, topic, query]);
+
+ useEffect(() => setPage(1), [topic, query]);
+
+ const goPage = (n: number) => {
+ setPage(n);
+ document.getElementById('latest')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
+ };
+ const backToAll = () => {
+ setTopic('All');
+ setQuery('');
+ setPage(1);
+ document.getElementById('latest')?.scrollIntoView({ behavior: 'smooth', block: 'start' });
};
+ const title = `Engineering Blog | ${siteConfig.title}`;
return (
-
+
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
- {/* Background Geometric Shapes */}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
- {/* Hero Section */}
-
-
-
-
-
- Engineering insights {" "}
-
-
Practical notes from the recode hive community.
-
-
-
-
-
-
-
- {/* Latest Articles Section */}
-
-
-
-
-
- {searchTerm && (
-
-
- {filteredBlogs.length > 0
- ? `Found ${filteredBlogs.length} article${filteredBlogs.length !== 1 ? "s" : ""}`
- : `No articles found`}
- {` for "${searchTerm}"`}
-
-
- )}
-
-
- {paginatedBlogs.length > 0 ? (
- paginatedBlogs.map((blog) => (
-
-
-
- ))
- ) : (
-
-
-
🔍
-
No articles found
-
Try adjusting your search terms or browse all articles.
-
- Clear Filters
-
-
-
- )}
-
-
- {filteredBlogs.length > POSTS_PER_PAGE && (
-
-
-
setCurrentPage((prev) => prev - 1)}
- >
- Previous
-
-
-
- {visiblePages.map((page) => (
- setCurrentPage(page)}
- >
- {page}
-
- ))}
- {showLastPage && (
- <>
- ...
- setCurrentPage(totalPages)}
- >
- {totalPages}
-
- >
- )}
-
-
-
setCurrentPage((prev) => prev + 1)}
- >
- Next
-
-
-
- Showing {showingStart} - {showingEnd} of {filteredBlogs.length} posts
-
-
- )}
-
-
-
-
+
);
}
-
-// ─── BlogCard ────────────────────────────────────────────────────────────────
-
-const BlogCard = ({ blog }: { blog: (typeof blogs)[number] }) => {
- const readingTime = blog.readingTime ?? 1;
- const authors = getAuthorProfiles(blog.authors || []);
- const history = useHistory();
- const blogPath = `/blog/${blog.slug}`;
-
- // Tags — use blog.tags if present, fallback to blog.category as single tag
- const tags: string[] =
- Array.isArray((blog as any).tags) && (blog as any).tags.length > 0
- ? (blog as any).tags
- : blog.category
- ? [blog.category]
- : [];
-
- return (
-
history.push(blogPath)}
- onKeyDown={(event) => {
- if (event.key === "Enter" || event.key === " ") {
- event.preventDefault();
- history.push(blogPath);
- }
- }}
- >
- {/* ── Image ── */}
-
-
-
-
- {/* ── Content ── */}
-
- {/* Title */}
-
- {blog.title}
-
-
- {/* Description */}
- {blog.description && (
-
{blog.description}
- )}
-
- {/* Tag pills */}
- {tags.length > 0 && (
-
- {tags.slice(0, 6).map((tag) => {
- const c = tagColor(tag);
- return (
-
-
- {tag}
-
- );
- })}
-
- )}
-
- {/* Bottom row: avatar stack + author names + date ··· Read → */}
-
-
- {/* Avatar stack */}
- {authors.length > 0 && (
-
- {authors.slice(0, 3).map((author, idx) => (
-
event.stopPropagation()}
- >
- {author.imageUrl ? (
-
{
- const t = e.currentTarget;
- t.style.display = "none";
- const fb = t.nextElementSibling as HTMLElement | null;
- if (fb) fb.style.display = "flex";
- }}
- />
- ) : null}
-
- {author.name.charAt(0).toUpperCase()}
-
-
- ))}
-
- )}
-
- {/* Author names + date stacked */}
-
- {authors.length > 0 && (
-
- {authors.map((author, idx) => (
-
- {idx > 0 && , }
- event.stopPropagation()}
- >
- {author.name}
-
-
- ))}
-
- )}
- {(blog as any).date && (
-
{formatDate((blog as any).date)}
- )}
-
-
-
-
-
- {readingTime} min read
-
-
-
-
- {/* Read link */}
-
- Read →
-
-
-
-
- );
-};
diff --git a/src/pages/privacy-policy/index.tsx b/src/pages/privacy-policy/index.tsx
index 2dd30264c..c05f1bb08 100644
--- a/src/pages/privacy-policy/index.tsx
+++ b/src/pages/privacy-policy/index.tsx
@@ -36,7 +36,7 @@ const PrivacyPolicy: React.FC = () => {
target="_blank"
rel="noopener noreferrer"
>
- Sanjay Viswanthan
+ Sanjay Viswanathan
. We are committed to protecting your privacy and ensuring the
security of your personal information. This Privacy Policy
diff --git a/src/utils/authors.ts b/src/utils/authors.ts
index 2d77a6fee..03efe9703 100644
--- a/src/utils/authors.ts
+++ b/src/utils/authors.ts
@@ -1,6 +1,6 @@
export const authorsMap = {
"ajay-dhangar": "Ajay Dhangar",
- "sanjay-kv": "Sanjay Viswanthan",
+ "sanjay-kv": "Sanjay Viswanathan",
"hitesh-gahanolia": "Hitesh Gahanolia",
"sowmiya-v": "Sowmiya Venkatesan",
"abhijith-m-s": "Abhijith M S",
@@ -25,7 +25,7 @@ export interface AuthorProfile {
const authorProfiles: Record
= {
"sanjay-kv": {
id: "sanjay-kv",
- name: "Sanjay Viswanthan",
+ name: "Sanjay Viswanathan",
title: "Founder at recode hive",
description:
"Software Engineer turned Data Engineer and Program Manager. Google ML Facilitator and former GitHub CE.",
diff --git a/static/img/blogs/github-cli-agent-cover.png b/static/img/blogs/github-cli-agent-cover.png
new file mode 100644
index 000000000..c73944500
Binary files /dev/null and b/static/img/blogs/github-cli-agent-cover.png differ