diff --git a/frontend/src/components/react/PostList.tsx b/frontend/src/components/react/PostList.tsx
index cf134d2..26026a2 100644
--- a/frontend/src/components/react/PostList.tsx
+++ b/frontend/src/components/react/PostList.tsx
@@ -42,19 +42,6 @@ function formatMonth(date: string) {
return new Date(date).toLocaleDateString('en-US', { month: 'short' }).toUpperCase();
}
-function toRoman(n: number): string {
- const map: [number, string][] = [
- [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
- [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
- [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
- ];
- let out = '';
- for (const [val, sym] of map) {
- while (n >= val) { out += sym; n -= val; }
- }
- return out;
-}
-
// Deterministic salon-hang layout. Each tile gets a col-span (out of 12) and an aspect ratio.
// The cycle is chosen so the room reads asymmetric but balanced.
const LAYOUT_CYCLE: Array<{ col: number; aspect: string; tilt: number }> = [
@@ -123,7 +110,6 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
const displayTitle = post.title || formatSlug(post.slug);
const isDeleting = deleting === post.slug;
const layout = LAYOUT_CYCLE[idx % LAYOUT_CYCLE.length];
- const exhibitNumber = toRoman(idx + 1);
const hasCover = !!post.cover_image?.url;
return (
@@ -141,8 +127,6 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
style={{ transform: `rotate(${layout.tilt}deg)` }}
aria-label={`View ${displayTitle}`}
>
- № {exhibitNumber}
-
-
-
{displayTitle}
- {post.summary && (
-
- {post.summary}
-
- )}
-
+
{displayTitle}
+ {post.summary && (
+
{post.summary}
+ )}
{formatMonth(post.date)}
- ·
+ ·
{formatYear(post.date)}
+ ·
+ {post.reading_time} min
diff --git a/frontend/src/pages/posts/[slug].astro b/frontend/src/pages/posts/[slug].astro
index 200e354..ede2331 100644
--- a/frontend/src/pages/posts/[slug].astro
+++ b/frontend/src/pages/posts/[slug].astro
@@ -28,19 +28,6 @@ function formatDate(d: string) {
return new Date(d).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
}
-function toRoman(n: number): string {
- const map: [number, string][] = [
- [1000, 'M'], [900, 'CM'], [500, 'D'], [400, 'CD'],
- [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
- [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'], [1, 'I'],
- ];
- let out = '';
- for (const [val, sym] of map) {
- while (n >= val) { out += sym; n -= val; }
- }
- return out;
-}
-
function formatSlug(s: string) {
if (!s) return '';
return s.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(' ');
@@ -49,7 +36,7 @@ function formatSlug(s: string) {
let post: PostDetail | null = null;
let html = '';
let error = '';
-let neighbors: { prev?: PostInfo; next?: PostInfo; index: number; total: number } = { index: -1, total: 0 };
+let neighbors: { prev?: PostInfo; next?: PostInfo } = {};
try {
const [postRes, listRes] = await Promise.all([
@@ -67,8 +54,6 @@ try {
const i = list.findIndex(p => p.slug === slug);
if (i >= 0) {
neighbors = {
- index: i,
- total: list.length,
prev: i > 0 ? list[i - 1] : undefined,
next: i < list.length - 1 ? list[i + 1] : undefined,
};
@@ -82,7 +67,6 @@ try {
const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
-const exhibitNumber = neighbors.index >= 0 ? toRoman(neighbors.index + 1) : '';
---
= 0 ? toRoman(neighbors.index + 1) : '';
{/* Plaque header */}
- {exhibitNumber && (
-
- № {exhibitNumber} / {neighbors.total}
-
- )}
-
{displayTitle}
diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css
index 7eabefe..fee5552 100644
--- a/frontend/src/styles/global.css
+++ b/frontend/src/styles/global.css
@@ -553,46 +553,56 @@ code, pre, kbd, samp {
filter: saturate(1.05) contrast(1.04);
}
.plate .plate-caption {
- padding: 12px 4px 14px 4px;
+ padding: 14px 6px 16px 6px;
+ margin-top: 2px;
+ border-top: 1px solid color-mix(in srgb, var(--surface2) 50%, transparent);
display: flex;
- align-items: flex-end;
- justify-content: space-between;
- gap: 1rem;
+ flex-direction: column;
+ gap: 0.4rem;
}
.plate .plate-caption-title {
font-family: var(--font-display);
font-style: italic;
font-weight: 500;
- font-size: 1.05rem;
- line-height: 1.2;
+ font-size: 1.18rem;
+ line-height: 1.18;
color: var(--text);
letter-spacing: -0.005em;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ transition: color 0.25s ease;
+}
+.plate:hover .plate-caption-title {
+ color: var(--mauve);
+}
+.plate .plate-caption-summary {
+ font-family: var(--font-sans);
+ font-style: italic;
+ font-size: 0.82rem;
+ line-height: 1.4;
+ color: var(--subtext0);
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
}
.plate .plate-caption-meta {
font-family: var(--font-sans);
- font-size: 0.72rem;
+ font-size: 0.68rem;
text-transform: uppercase;
- letter-spacing: 0.18em;
+ letter-spacing: 0.22em;
color: var(--subtext0);
white-space: nowrap;
- align-self: flex-start;
- padding-top: 0.35rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding-top: 0.25rem;
}
-
-/* The little exhibit number stuck to the corner of a plate */
-.plate-tag {
- position: absolute;
- top: -10px;
- left: 14px;
- background: var(--mauve);
- color: var(--rosewater);
- font-family: var(--font-display);
- font-size: 0.7rem;
- font-weight: 600;
- letter-spacing: 0.18em;
- padding: 4px 8px;
- text-transform: uppercase;
- box-shadow: 0 2px 6px -2px rgba(20, 16, 12, 0.45);
+.plate .plate-caption-sep {
+ color: var(--mauve);
+ opacity: 0.55;
}
.plate-tag-mini {
position: absolute;