Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .astro/content.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ declare module 'astro:content' {
rendered?: RenderedContent;
filePath?: string;
}>;
"research": Record<string, {
id: string;
body?: string;
collection: "research";
data: InferEntrySchema<"research">;
rendered?: RenderedContent;
filePath?: string;
}>;

};

Expand Down Expand Up @@ -177,6 +185,6 @@ declare module 'astro:content' {
LiveContentConfig['collections'][C]['loader']
>;

export type ContentConfig = typeof import("../src/content.config.js");
export type ContentConfig = typeof import("./../src/content.config.js");
export type LiveContentConfig = never;
}
20 changes: 19 additions & 1 deletion src/content.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,25 @@ const blog = defineCollection({
})
});

const research = defineCollection({
loader: glob({
pattern: "**/*.{md,mdx}",
base: "./src/content/research"
}),
schema: z.object({
title: z.string(),
description: z.string(),
author: z.string(),
date: z.date(),
tags: z.array(z.string()).default([]),
category: z.string(),
featured: z.boolean().default(false),
image: z.string().optional()
})
});

export const collections = {
articles,
blog
blog,
research
};
23 changes: 23 additions & 0 deletions src/content/research/future-trends-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: "Future Trends Report"
description: "Market growth and AI adoption trends."
author: "Recursive Zero"
date: 2025-08-17
tags:
- ai
- future
category: "AI & Technology"
featured: true
---

## Introduction

Placeholder content.

## Market Growth

Placeholder content.

## AI Adoption

Placeholder content.
23 changes: 23 additions & 0 deletions src/content/research/hidden-cost-of-manual-processes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
title: "Hidden Cost of Manual Processes"
description: "Understanding inefficiencies caused by manual workflows."
author: "Recursive Zero"
date: 2025-08-16
tags:
- research
- operations
category: "Case Studies"
featured: false
---

## Introduction

Placeholder content.

## Time Loss

Placeholder content.

## Industry Statistics

Placeholder content.
27 changes: 27 additions & 0 deletions src/content/research/why-we-built-this-product.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
title: "Why We Built This Product"
description: "Research behind our product vision."
author: "Recursive Zero"
date: 2025-08-15
tags:
- research
- product
category: "Product Vision"
featured: false
---

## Introduction

Placeholder content.

## Industry Problem

Placeholder content.

## Research Findings

Placeholder content.

## Our Vision

Placeholder content.
3 changes: 2 additions & 1 deletion src/data/navLinks.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@
"name": "Work",
"href": "/work",
"isActive": true
}
},
{ "name": "Research", "href": "/research", "isActive": true }
]
135 changes: 135 additions & 0 deletions src/pages/research/[slug].astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
import CalendarIcon from "@/assets/icons/calendar.svg";
import ProfileIcon from "@/assets/icons/profile.svg";
import MarkdownContent from "@/components/MarkdownContent.astro";
import BaseLayout from "@/layouts/BaseLayout";
import BlogCard from "@/components/BlogCard.astro";
import { type CollectionEntry, getCollection, render } from "astro:content";

export const prerender = true;

export async function getStaticPaths() {
const posts = await getCollection("research");

return posts.map((blog) => ({
params: { slug: blog.id },
props: blog
}));
}

type Props = CollectionEntry<"research">;

const blog = Astro.props;

// BLOG NOT FOUND REDIRECT
if (!Astro.props?.id) {
return Astro.redirect("/research");
}

const { Content } = await render(blog);

const { title, description, date, author, image, tags } = blog.data;
const allResearch = await getCollection("research");

const relatedPosts = allResearch
.filter((post) => post.id !== blog.id)
.slice(0, 3);
const readingTime = Math.ceil((blog.body?.split(/\s+/).length || 0) / 200);

if (!Content) {
throw new Error(`Content could not be rendered for blog: ${blog.id}`);
}
---

<BaseLayout meta={{ title: title, description: description, ogImage: image, articleDate: date }}>
<div class="mx-auto flex max-w-6xl flex-col items-center justify-center px-4 sm:px-6 lg:px-8">
<h1 class="mb-4 text-4xl font-bold">{title}</h1>
{description && <p class="mb-6 text-xl text-gray-500">{description}</p>}
<div class="mb-8 flex items-center gap-4 text-gray-400">
<!-- META -->
<div
class="text-foreground/60 mt-8 flex flex-col gap-4 text-sm sm:flex-row sm:flex-wrap sm:items-center sm:gap-6"
>
{
author && (
<span class="flex items-center gap-2">
<span class="text-foreground">
<ProfileIcon />
</span>

{author}
</span>
)
}

{
date && (
<span class="flex items-center gap-2">
<span class="text-foreground">
<CalendarIcon />
</span>

<time datetime={date.toISOString()}>{date.toLocaleDateString()}</time>
</span>
)
}
{readingTime && <span class="flex items-center gap-2">⏱️ {readingTime} min read</span>}
</div>

<!-- TAGS -->
{
tags?.length > 0 && (
<div class="mt-6 flex flex-wrap gap-2 sm:gap-3">
{tags.map((tag: string) => (
<a
href="/research"
class="rounded-full border border-cyan-500/20 bg-brandBlue/10 px-3 py-2 text-xs font-medium text-blue-600 transition duration-300 hover:bg-brandBlue hover:text-white sm:px-4 sm:text-sm"
>
#{tag}
</a>
))}
</div>
)
}
</div>

<!-- BLOG CONTENT -->
<section class="mx-auto max-w-4xl px-5 pb-20 sm:px-6 lg:px-10 lg:pb-24">
<div class="border-foreground/10 bg-background rounded-3xl border p-5 sm:p-6 md:p-10">
<MarkdownContent>
<article
class="text-foreground [&_h1]:text-foreground [&_h2]:text-foreground [&_h3]:text-foreground [&_p]:text-foreground/80 [&_li]:text-foreground/80 [&_strong]:text-foreground [&_hr]:border-foreground/10 [&_blockquote]:text-foreground/70 max-w-none [&_a]:font-medium [&_a]:text-blue-600 hover:[&_a]:underline [&_blockquote]:border-l-4 [&_blockquote]:border-cyan-500 [&_blockquote]:pl-4 [&_blockquote]:italic [&_h1]:mb-6 [&_h1]:text-4xl [&_h1]:font-bold [&_h2]:mb-5 [&_h2]:mt-12 [&_h2]:text-3xl [&_h2]:font-bold [&_h3]:mb-4 [&_h3]:mt-8 [&_h3]:text-2xl [&_h3]:font-semibold [&_hr]:my-10 [&_img]:rounded-2xl [&_li]:mb-2 [&_li]:leading-8 [&_ol]:my-5 [&_p]:mb-5 [&_p]:text-base [&_p]:leading-8 [&_ul]:my-5"
>
<Content />
</article>
</MarkdownContent>
</div>
</section>
{
relatedPosts.length > 0 && (
<section class="mx-auto mt-16 max-w-6xl px-5 pb-20">
<h2 class="mb-8 text-3xl font-bold">
Related Research Articles
</h2>

<div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
{
relatedPosts.map((post) => (
<BlogCard
url={`/research/${post.id}`}
title={post.data.title}
description={post.data.description}
image={post.data.image}
author={post.data.author}
date={post.data.date.toLocaleDateString()}
tags={post.data.tags}
/>
))
}
</div>
</section>
)
}

</div>
</BaseLayout>
124 changes: 124 additions & 0 deletions src/pages/research/index.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
---
import BlogCard from "@/components/BlogCard.astro";
import BaseLayout from "@/layouts/BaseLayout";

import { getCollection } from "astro:content";

const posts = await getCollection("research");

// const categories = [
// ...new Set(posts.map((post) => post.data.category).filter(Boolean))
// ];

const selectedCategory = Astro.url.searchParams.get("category");

const filteredPosts = selectedCategory
? posts.filter((post) => post.data.category === selectedCategory)
: posts;

const featuredPost = !selectedCategory
? posts.find((post) => post.data.featured)
: null;

const gridPosts = selectedCategory
? filteredPosts
: filteredPosts.filter((post) => !post.data.featured);
---

<BaseLayout meta={{ title: "Research & White Papers" }}>
<main class="bg-background text-foreground">
<section class="mx-auto max-w-7xl px-6 py-20 lg:px-10">
<!-- HERO -->
<div class="text-center">
<p class="mb-4 text-sm font-semibold uppercase tracking-[0.25em] text-blue-600">
Research Library
</p>

<h1 class="text-foreground text-4xl font-bold tracking-tight sm:text-5xl md:text-6xl">
Research & White Papers
</h1>

<p class="text-foreground/70 mx-auto mt-6 max-w-2xl text-lg leading-8">
Explore industry research, market analysis, case studies, AI trends,
sustainability insights and product vision papers.
</p>
</div>

<!-- CATEGORY FILTERS -->
<!-- <div class="mt-8 flex flex-wrap justify-center gap-3">
<a
href="/research"
class={`rounded-full border px-4 py-2 text-sm font-medium ${
!selectedCategory
? "bg-brandBlue text-white"
: "border-cyan-500/20 bg-brandBlue/10 text-blue-700"
}`}
>
All
</a>

{
categories.map((category) => (
<a
href={`/research?category=${encodeURIComponent(category)}`}
class={`rounded-full border px-4 py-2 text-sm font-medium ${
selectedCategory === category
? "bg-brandBlue text-white"
: "border-cyan-500/20 bg-brandBlue/10 text-blue-700"
}`}
>
{category}
</a>
))
}
</div> -->

<!-- FEATURED RESEARCH -->
{
featuredPost && (
<section class="mt-16">
<h2 class="mb-6 text-3xl font-bold">Featured Research</h2>

<BlogCard
url={`/research/${featuredPost.id}`}
title={featuredPost.data.title}
description={featuredPost.data.description}
image={featuredPost.data.image}
author={featuredPost.data.author}
date={featuredPost.data.date.toLocaleDateString()}
tags={featuredPost.data.tags}
priority={true}
/>
</section>
)
}

<!-- RESEARCH GRID -->
<section class="mt-16">
<div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
{
gridPosts.map((post) => (
<BlogCard
url={`/research/${post.id}`}
title={post.data.title}
description={post.data.description}
image={post.data.image}
author={post.data.author}
date={post.data.date.toLocaleDateString()}
tags={post.data.tags}
/>
))
}
</div>

{
gridPosts.length === 0 && (
<p class="mt-10 text-center text-gray-500">
No research articles found in this category.
</p>
)
}
</section>
</section>
</main>
</BaseLayout>
Loading