initial: four-collection content pipeline for unique.rzen.dev

A small Astro + MDX blog for delightful, daily-use finds. The architecture
separates capture from publication and supports reuse:

- find/    captured items, hidden from indexes
- finds/   editorial superposts referencing find slugs in a grid
- reviews/ full-length writeups; can graduate from a find via fromFind
- posts/   regular essays

Three project-local skills under .claude/skills/ drive the pipeline:
daily-finds (capture-only sweep across sources.json), build-finds (draft
a thematic superpost), build-review (graduate a find).

sources.json carries 47 curated feeds with per-source fetch strategy
(webfetch / curl / skip) so Cloudflare-blocked sites and Reddit JSON
endpoints are handled deterministically. /sources renders a 3-column
card stream with a "Recent finds" cross-reference per source.

Seeds: 17 reviews and one superpost referencing 5 finds.
This commit is contained in:
2026-05-03 11:48:03 -04:00
commit 9858abedc2
95 changed files with 10230 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
---
import { getCollection } from 'astro:content';
import BaseLayout from '../layouts/BaseLayout.astro';
import { heroes } from '../lib/hero';
const reviews = (await getCollection('reviews')).sort(
(a, b) => b.data.date.valueOf() - a.data.date.valueOf()
);
const tiles = reviews.map((review) => ({
href: `/reviews/${review.id}/`,
name: review.data.name,
subtitle: review.data.subtitle,
hero: heroes[review.id],
}));
---
<BaseLayout title="Grid" wide>
<div class="intro">
<h1>Grid view</h1>
<p><a href="/">← list view</a></p>
</div>
<ul class="grid">
{
tiles.map((tile) => (
<li>
<a href={tile.href} class:list={['tile', { 'text-only': !tile.hero }]}>
{tile.hero?.kind === 'image' && (
<img
class="media"
src={tile.hero.src.src}
width={tile.hero.src.width}
height={tile.hero.src.height}
loading="lazy"
decoding="async"
alt=""
/>
)}
{tile.hero?.kind === 'video' && (
<video
class="media"
src={tile.hero.src}
autoplay
loop
muted
playsinline
preload="metadata"
/>
)}
<div class="text">
<span class="name">{tile.name}</span>
<span class="subtitle">{tile.subtitle}</span>
</div>
</a>
</li>
))
}
</ul>
<style>
.intro {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
margin-bottom: 1.5rem;
}
.intro h1 { margin: 0; font-size: 1.5rem; }
.intro p { margin: 0; font-size: 0.9rem; color: var(--muted); }
.grid {
column-count: 3;
column-gap: 1rem;
list-style: none;
margin: 0;
padding: 0;
}
@media (max-width: 880px) {
.grid { column-count: 2; }
}
@media (max-width: 520px) {
.grid { column-count: 1; }
}
.grid > li {
break-inside: avoid;
display: block;
margin: 0 0 1rem;
}
.tile {
display: block;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--rule);
background: var(--bg);
color: inherit;
transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.tile:hover {
text-decoration: none;
transform: translateY(-2px);
border-color: color-mix(in srgb, var(--accent) 40%, var(--rule));
box-shadow: 0 8px 24px -10px rgba(0, 0, 0, 0.18);
}
.tile:hover .name { color: var(--accent); }
.media {
display: block;
width: 100%;
height: auto;
background: var(--rule);
}
.text {
padding: 0.85rem 1rem 1rem;
display: flex;
flex-direction: column;
gap: 0.2rem;
}
.name {
font-family: ui-sans-serif, system-ui, -apple-system, 'Helvetica Neue', sans-serif;
font-weight: 700;
font-size: 1rem;
letter-spacing: -0.01em;
color: var(--fg);
transition: color 0.18s ease;
}
.subtitle {
color: var(--muted);
font-size: 0.92rem;
line-height: 1.4;
}
.tile.text-only {
background: linear-gradient(155deg, var(--rule), transparent);
}
.tile.text-only .text {
padding: 1.5rem 1.25rem;
}
.tile.text-only .name {
font-size: 1.25rem;
margin-bottom: 0.3rem;
}
.tile.text-only .subtitle {
font-size: 1rem;
}
</style>
</BaseLayout>