diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 68dd8b8..2714f94 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,4 +21,5 @@ jobs:
- run: npm ci
- run: npm run check
- run: npm run build
+ - run: npm test
- run: npm run design:audit
diff --git a/.gitignore b/.gitignore
index 9d19539..cae2205 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,7 @@ pnpm-debug.log*
.playwright-libs/
home-desktop*.png
entry-mobile*.png
+optical-*.png
.wrangler/
# impeccable-ignore-start
diff --git a/DESIGN.md b/DESIGN.md
index 000ae9f..36a867d 100644
--- a/DESIGN.md
+++ b/DESIGN.md
@@ -1,24 +1,184 @@
-# Design
+---
+version: alpha
+name: Optical Research Console
+description: A precise research instrument that remains calm and highly readable for long technical reports.
+colors:
+ primary: "#071513"
+ secondary: "#536764"
+ tertiary: "#007D78"
+ neutral: "#F3F7F6"
+ surface: "#FFFFFF"
+ rule: "#C9D6D3"
+ signal: "#16C8BD"
+ evidenceText: "#C3FFF8"
+ positive: "#117A4E"
+ warning: "#C88912"
+ danger: "#C14343"
+ mediaBlack: "#030A09"
+typography:
+ h1:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 6.6rem
+ fontWeight: 620
+ lineHeight: 0.94
+ letterSpacing: "-0.052em"
+ h2:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 3rem
+ fontWeight: 600
+ lineHeight: 1
+ letterSpacing: "-0.035em"
+ body-md:
+ fontFamily: Newsreader Variable
+ fontSize: 1.125rem
+ fontWeight: 400
+ lineHeight: 1.8
+ interface:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 1rem
+ fontWeight: 400
+ lineHeight: 1.6
+ body-sm:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 0.94rem
+ fontWeight: 400
+ lineHeight: 1.65
+ interface-sm:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 0.8rem
+ fontWeight: 400
+ lineHeight: 1.6
+ label:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 0.78rem
+ fontWeight: 650
+ lineHeight: 1.2
+ caption:
+ fontFamily: IBM Plex Sans Variable
+ fontSize: 0.7rem
+ fontWeight: 400
+ lineHeight: 1.5
+ micro:
+ fontFamily: IBM Plex Mono
+ fontSize: 0.56rem
+ fontWeight: 500
+ lineHeight: 1.2
+ metadata:
+ fontFamily: IBM Plex Mono
+ fontSize: 0.625rem
+ fontWeight: 500
+ lineHeight: 1.2
+ letterSpacing: 0.08em
+rounded:
+ sm: 0px
+ md: 0px
+ lg: 0px
+ focus: 0.125em
+ mark: 0.2em
+ inline: 0.35em
+ code: 0.5em
+ media: 0.6em
+spacing:
+ xs: 4px
+ sm: 8px
+ md: 16px
+ lg: 32px
+ xl: 64px
+components:
+ filter-active:
+ backgroundColor: "{colors.primary}"
+ textColor: "{colors.neutral}"
+ rounded: "{rounded.sm}"
+ padding: 8px
+ evidence-label:
+ backgroundColor: "{colors.primary}"
+ textColor: "{colors.evidenceText}"
+ typography: "{typography.metadata}"
+ rounded: "{rounded.sm}"
+ padding: 6px
+ focus-control:
+ backgroundColor: "{colors.neutral}"
+ textColor: "{colors.primary}"
+ rounded: "{rounded.sm}"
+ padding: 8px
+ separator:
+ backgroundColor: "{colors.rule}"
+ textColor: "{colors.primary}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+ signal-label:
+ backgroundColor: "{colors.primary}"
+ textColor: "{colors.signal}"
+ typography: "{typography.micro}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+ status-positive:
+ backgroundColor: "{colors.positive}"
+ textColor: "{colors.surface}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+ status-warning:
+ backgroundColor: "{colors.warning}"
+ textColor: "{colors.primary}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+ status-danger:
+ backgroundColor: "{colors.danger}"
+ textColor: "{colors.surface}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+ media-stage:
+ backgroundColor: "{colors.mediaBlack}"
+ textColor: "{colors.evidenceText}"
+ rounded: "{rounded.sm}"
+ padding: 4px
+---
-## Direction
-An engineering journal with the pacing of a high-end independent publication: strong editorial type, evidence-sized media, technical labels, and hairline rules. Layout—not decoration—creates hierarchy.
+## Overview
+
+Optical Research Console treats the progress archive as a scientific instrument, not a magazine and not a science-fiction dashboard. The primary surface is **Monitor**: readers scan current state, open investigations, and inspect evidence. Dense labels, calibrated rules, coordinate grids, and tabular numerals establish technical credibility; generous reading measures and a dedicated long-form serif preserve comfort.
+
+## Colors
+
+- **Primary** is a deep green-black used for core text and the lead investigation slab.
+- **Neutral** is a cool laboratory canvas rather than warm paper.
+- **Tertiary** is the accessible interaction accent. Use it for links, focus, indices, and measured status.
+- **Signal** is a brighter spectral cyan reserved for dark surfaces and evidence labels.
+- Grid lines use a low-opacity mixture of Primary. They organize space but must never compete with text.
+- Dark mode keeps the green-tinted optical character rather than becoming neutral charcoal.
## Typography
-- Display: Newsreader Variable, 420–500 weight, tight optical display tracking.
-- Interface: Manrope Variable, compact and direct.
-- Metadata/code: JetBrains Mono Variable.
-- Rendered Markdown: shadcn/typeset with Newsreader body, Manrope headings, and an 18px / 1.78 reading rhythm.
-## Color
-Warm paper (`#f2f0e9`), green-black ink (`#191d1b`), vermilion action/evidence accent (`#e34a29`), restrained green for verified/published state. Dark mode keeps the same tinted character.
+- **IBM Plex Sans Variable** carries titles and interface copy. It feels engineered without sacrificing legibility.
+- **Newsreader Variable** is reserved for long report bodies. Research findings should remain comfortable over thousands of words.
+- **IBM Plex Mono** carries metadata, experiment IDs, dates, status, and tabular values. Never use mono for paragraphs.
+- Headings are assertive but not monumental. The hierarchy comes from weight, alignment, and information density—not decorative display typography.
+
+## Layout
+
+- Homepage: calibrated masthead, one dark lead investigation paired with primary evidence, then compact ruled archive rows.
+- Entry: technical title field with subtle vertical coordinates, full-width evidence, then a 760px reading column.
+- Imagery is evidence and should be shown large enough to inspect.
+- Use square corners, hairline rules, and explicit alignment. Avoid floating cards and arbitrary elevation.
+- Mobile preserves the reading sequence: status, title, evidence, report. Tables scroll horizontally rather than compressing numeric columns.
+
+## Elevation & Depth
+
+No box shadows and no glass layers. Depth is communicated by ink-on-canvas contrast, full-bleed evidence, rules, and surface changes. The site should resemble a calibrated optical workstation rather than a stack of cards.
+
+## Shapes
+
+Application chrome uses square corners. Small proportional radii in the inherited Typeset layer remain available for focus outlines, code, keys, and inline semantics. The only circles are semantic status indicators. The crosshair wordmark and corner registration marks are functional visual signatures for the research system.
+
+## Components
+
+- Filters are outlined metadata controls; active filters invert to Primary.
+- Evidence labels sit directly on dark image/video surfaces using Signal text.
+- Status colors must accompany text labels and never communicate meaning alone.
+- Focus states use Tertiary with sufficient contrast and must remain visible in both themes.
-## Composition
-- Homepage: large journal masthead, one lead investigation, then ruled archive rows.
-- Entry: editorial title and deck, full-width evidence, then a narrow 760px reading measure.
-- Never place experiment imagery in tiny decorative thumbnails when it is the evidence.
+## Do's and Don'ts
-## Motion
-Only subtle image scale and state transitions. Respect reduced motion.
+**Do** use spectral cyan sparingly, show real experiment outputs, favor tabular numerals, preserve article measure, and let rules establish structure.
-## Accessibility
-Visible focus, semantic hierarchy, 44px-equivalent mobile controls, readable contrast, native video controls, captions/alt text supported in the media schema.
+**Don't** use neon-on-black for full articles, blue-violet gradients, glassmorphism, pill-shaped UI, fake telemetry, tiny decorative evidence, or mono body copy.
diff --git a/package-lock.json b/package-lock.json
index 9e971a2..ef4e457 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,9 +9,9 @@
"version": "0.0.1",
"dependencies": {
"@astrojs/mdx": "^7.0.3",
- "@fontsource-variable/jetbrains-mono": "^5.2.8",
- "@fontsource-variable/manrope": "^5.2.8",
+ "@fontsource-variable/ibm-plex-sans": "^5.2.8",
"@fontsource-variable/newsreader": "^5.2.10",
+ "@fontsource/ibm-plex-mono": "^5.2.7",
"astro": "^7.0.9",
"sharp": "^0.35.3"
},
@@ -1288,28 +1288,28 @@
"node": ">=18"
}
},
- "node_modules/@fontsource-variable/jetbrains-mono": {
+ "node_modules/@fontsource-variable/ibm-plex-sans": {
"version": "5.2.8",
- "resolved": "https://registry.npmjs.org/@fontsource-variable/jetbrains-mono/-/jetbrains-mono-5.2.8.tgz",
- "integrity": "sha512-WBA9elru6Jdp5df2mES55wuOO0WIrn3kpXnI4+W2ek5u3ZgLS9XS4gmIlcQhiZOWEKl95meYdvK7xI+ETLCq/Q==",
+ "resolved": "https://registry.npmjs.org/@fontsource-variable/ibm-plex-sans/-/ibm-plex-sans-5.2.8.tgz",
+ "integrity": "sha512-n5PF2iFa0CZT0QYTPzxvZ39opC9LnU0zdoRccoADbs+Dtsd+lbXOZF7RNuIPHcQX1dKjF63sxnRImQIB5eD0Ag==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
- "node_modules/@fontsource-variable/manrope": {
- "version": "5.2.8",
- "resolved": "https://registry.npmjs.org/@fontsource-variable/manrope/-/manrope-5.2.8.tgz",
- "integrity": "sha512-nc9lOuCRz73UHnovDE2bwXUdghE2SEOc7Aii0qGe3CLyE03W1a7VnY5Z6euRiapiKbCkGS+eXbY3s/kvWeGeSw==",
+ "node_modules/@fontsource-variable/newsreader": {
+ "version": "5.2.10",
+ "resolved": "https://registry.npmjs.org/@fontsource-variable/newsreader/-/newsreader-5.2.10.tgz",
+ "integrity": "sha512-MdI2iRwrqWpMOU/2aV2BgfZ4dJNlj/XlYaY8Zb7t87mWqHskYW0XiUANkt1cyOCiEfW2VQ0bQ5vZgGPp6B2B4w==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
}
},
- "node_modules/@fontsource-variable/newsreader": {
- "version": "5.2.10",
- "resolved": "https://registry.npmjs.org/@fontsource-variable/newsreader/-/newsreader-5.2.10.tgz",
- "integrity": "sha512-MdI2iRwrqWpMOU/2aV2BgfZ4dJNlj/XlYaY8Zb7t87mWqHskYW0XiUANkt1cyOCiEfW2VQ0bQ5vZgGPp6B2B4w==",
+ "node_modules/@fontsource/ibm-plex-mono": {
+ "version": "5.2.7",
+ "resolved": "https://registry.npmjs.org/@fontsource/ibm-plex-mono/-/ibm-plex-mono-5.2.7.tgz",
+ "integrity": "sha512-MKAb8qV+CaiMQn2B0dIi1OV3565NYzp3WN5b4oT6LTkk+F0jR6j0ZN+5BKJiIhffDC3rtBULsYZE65+0018z9w==",
"license": "OFL-1.1",
"funding": {
"url": "https://github.com/sponsors/ayuhito"
diff --git a/package.json b/package.json
index f1122cb..e659683 100644
--- a/package.json
+++ b/package.json
@@ -11,15 +11,16 @@
"preview": "astro preview",
"astro": "astro",
"sync": "node scripts/sync-content.mjs",
+ "test": "node --test tests/*.test.mjs",
"check": "astro check",
"design:audit": "impeccable detect src",
"deploy": "npm run build && wrangler deploy"
},
"dependencies": {
"@astrojs/mdx": "^7.0.3",
- "@fontsource-variable/jetbrains-mono": "^5.2.8",
- "@fontsource-variable/manrope": "^5.2.8",
+ "@fontsource-variable/ibm-plex-sans": "^5.2.8",
"@fontsource-variable/newsreader": "^5.2.10",
+ "@fontsource/ibm-plex-mono": "^5.2.7",
"astro": "^7.0.9",
"sharp": "^0.35.3"
},
diff --git a/public/journal-filter.js b/public/journal-filter.js
index 05a3974..674b6d9 100644
--- a/public/journal-filter.js
+++ b/public/journal-filter.js
@@ -1,15 +1,121 @@
const buttons = [...document.querySelectorAll('[data-filter]')];
-const rows = [...document.querySelectorAll('[data-kind]')];
-
-buttons.forEach((button) => button.addEventListener('click', () => {
- buttons.forEach((candidate) => candidate.classList.toggle('active', candidate === button));
- const filter = button.dataset.filter;
- let visible = 0;
- rows.forEach((row) => {
- const show = filter === 'all' || row.dataset.kind === filter;
- row.hidden = !show;
- if (show) visible += 1;
- });
- const empty = document.querySelector('.empty-filter');
- if (empty) empty.hidden = visible !== 0;
-}));
\ No newline at end of file
+const list = document.querySelector('.journal-list');
+const template = document.querySelector('#journal-row-template');
+const sentinel = document.querySelector('[data-journal-sentinel]');
+const loadButton = document.querySelector('.journal-load-button');
+const loadStatus = document.querySelector('.journal-load-status');
+const empty = document.querySelector('.empty-filter');
+
+if (list && template && sentinel && loadButton && loadStatus) {
+ let nextCursor = list.dataset.nextCursor || null;
+ let currentFilter = 'all';
+ let loading = false;
+ const total = Number(list.dataset.total || 0);
+
+ const rows = () => [...list.querySelectorAll('.journal-row')];
+
+ const setText = (row, field, value) => {
+ const target = row.querySelector(`[data-field="${field}"]`);
+ if (target) target.textContent = value;
+ };
+
+ const createRow = (entry) => {
+ const row = template.content.firstElementChild.cloneNode(true);
+ row.dataset.kind = entry.kind;
+ const link = row.querySelector('[data-field="url"]');
+ if (link) link.href = entry.url;
+ setText(row, 'index', entry.index);
+ setText(row, 'kind', entry.kind);
+ setText(row, 'status', entry.status);
+ setText(row, 'title', entry.title);
+ setText(row, 'summary', entry.summary);
+ const time = row.querySelector('[data-field="date"]');
+ if (time) {
+ time.textContent = entry.dateShort;
+ if (entry.date) time.dateTime = entry.date;
+ }
+ const imageSlot = row.querySelector('[data-field="image-slot"]');
+ if (imageSlot && entry.image) {
+ const image = document.createElement('img');
+ image.src = entry.image;
+ image.alt = '';
+ image.loading = 'lazy';
+ imageSlot.replaceWith(image);
+ }
+ return row;
+ };
+
+ const applyFilter = () => {
+ let visible = 0;
+ rows().forEach((row) => {
+ const show = currentFilter === 'all' || row.dataset.kind === currentFilter;
+ row.hidden = !show;
+ if (show) visible += 1;
+ });
+ if (empty) empty.hidden = visible !== 0 || Boolean(nextCursor);
+ };
+
+ const updateStatus = (message) => {
+ const loaded = rows().length;
+ loadStatus.textContent = message ?? (nextCursor ? `${loaded} of ${total} entries loaded` : `All ${loaded} entries loaded`);
+ loadButton.hidden = !nextCursor;
+ };
+
+ const loadNext = async () => {
+ if (loading || !nextCursor) return false;
+ const cursor = nextCursor;
+ loading = true;
+ list.setAttribute('aria-busy', 'true');
+ loadButton.disabled = true;
+ updateStatus('Loading more research…');
+
+ try {
+ const response = await fetch(`/journal/${encodeURIComponent(cursor)}.json`);
+ if (!response.ok) throw new Error(`Journal manifest returned ${response.status}`);
+ const page = await response.json();
+ const fragment = document.createDocumentFragment();
+ page.entries.forEach((entry) => fragment.append(createRow(entry)));
+ list.append(fragment);
+ nextCursor = page.nextCursor;
+ list.dataset.nextCursor = nextCursor ?? '';
+ applyFilter();
+ updateStatus();
+ return true;
+ } catch (error) {
+ console.error('Unable to load the next journal page.', error);
+ updateStatus('Loading paused. Use “Load more” to retry.');
+ return false;
+ } finally {
+ loading = false;
+ list.setAttribute('aria-busy', 'false');
+ loadButton.disabled = false;
+ }
+ };
+
+ const loadAll = async () => {
+ while (nextCursor) {
+ const loaded = await loadNext();
+ if (!loaded) break;
+ }
+ };
+
+ buttons.forEach((button) => button.addEventListener('click', async () => {
+ buttons.forEach((candidate) => candidate.classList.toggle('active', candidate === button));
+ currentFilter = button.dataset.filter || 'all';
+ if (currentFilter !== 'all') await loadAll();
+ applyFilter();
+ }));
+
+ loadButton.addEventListener('click', loadNext);
+
+ if ('IntersectionObserver' in window && nextCursor) {
+ const observer = new IntersectionObserver((entries) => {
+ if (entries.some((entry) => entry.isIntersecting)) loadNext();
+ if (!nextCursor) observer.disconnect();
+ }, { rootMargin: '700px 0px' });
+ observer.observe(sentinel);
+ }
+
+ applyFilter();
+ updateStatus();
+}
diff --git a/src/components/JournalRow.astro b/src/components/JournalRow.astro
new file mode 100644
index 0000000..ec6ee89
--- /dev/null
+++ b/src/components/JournalRow.astro
@@ -0,0 +1,24 @@
+---
+import type { JournalCard } from '../lib/journal';
+
+interface Props {
+ entry: JournalCard;
+}
+
+const { entry } = Astro.props;
+---
+
+
+ {entry.index}
+
+
{entry.kind}{entry.status}
+
{entry.title}
+
{entry.summary}
+
+
+ {entry.image &&

}
+
+
↗
+
+
+
diff --git a/src/layouts/SiteLayout.astro b/src/layouts/SiteLayout.astro
index b2e3ded..2825fe8 100644
--- a/src/layouts/SiteLayout.astro
+++ b/src/layouts/SiteLayout.astro
@@ -1,7 +1,8 @@
---
-import '@fontsource-variable/manrope/index.css';
+import '@fontsource-variable/ibm-plex-sans/index.css';
import '@fontsource-variable/newsreader/index.css';
-import '@fontsource-variable/jetbrains-mono/index.css';
+import '@fontsource/ibm-plex-mono/400.css';
+import '@fontsource/ibm-plex-mono/500.css';
import '../styles/global.css';
interface Props {
@@ -21,7 +22,7 @@ const pageTitle = title === 'Research Progress' ? title : `${title} — Research
-
+
{pageTitle}
diff --git a/src/pages/journal/[cursor].json.ts b/src/pages/journal/[cursor].json.ts
new file mode 100644
index 0000000..9ba7c48
--- /dev/null
+++ b/src/pages/journal/[cursor].json.ts
@@ -0,0 +1,30 @@
+import type { APIRoute, GetStaticPaths } from 'astro';
+import { getJournal, JOURNAL_PAGE_SIZE, toJournalCard, type JournalCard } from '../../lib/journal';
+import { paginateJournal } from '../../lib/journal-pagination.mjs';
+
+interface JournalManifest {
+ cursor: string;
+ nextCursor: string | null;
+ entries: JournalCard[];
+}
+
+export const getStaticPaths = (async () => {
+ const { remaining } = await getJournal();
+ const cards = remaining.map(toJournalCard);
+ const pages = paginateJournal(cards, JOURNAL_PAGE_SIZE) as JournalManifest[];
+
+ return pages.slice(1).map((page) => ({
+ params: { cursor: page.cursor },
+ props: { page },
+ }));
+}) satisfies GetStaticPaths;
+
+export const GET: APIRoute = ({ props }) => {
+ const { page } = props as { page: JournalManifest };
+ return new Response(JSON.stringify(page), {
+ headers: {
+ 'Content-Type': 'application/json; charset=utf-8',
+ 'Cache-Control': 'public, max-age=300, s-maxage=31536000, immutable',
+ },
+ });
+};
diff --git a/src/styles/global.css b/src/styles/global.css
index 290101d..95c6867 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -1,38 +1,56 @@
@import './typeset.css';
:root {
- --paper: #f2f0e9;
- --paper-raised: #faf9f5;
- --ink: #191d1b;
- --muted: #636a65;
- --faint: #d8d5cc;
- --accent: #e34a29;
- --accent-soft: #f7d8cd;
- --positive: #287658;
+ --paper: #f3f7f6;
+ --paper-raised: #ffffff;
+ --ink: #071513;
+ --muted: #536764;
+ --faint: #c9d6d3;
+ --grid: rgba(23, 67, 61, .075);
+ --accent: #007d78;
+ --accent-bright: #16c8bd;
+ --accent-soft: #c9f2ee;
+ --positive: #117a4e;
+ --warning: #c88912;
+ --danger: #c14343;
+ --media-black: #030a09;
+ --evidence-text: #c3fff8;
+ --panel: #071513;
+ --panel-text: #eef9f6;
+ --panel-muted: #9fb9b4;
--color-foreground: var(--ink);
--color-muted-foreground: var(--muted);
--color-border: var(--faint);
- --color-muted: #e7e4dc;
+ --color-muted: #e5ecea;
--color-ring: var(--accent);
- --font-heading: 'Newsreader Variable', Georgia, serif;
- --font-mono: 'JetBrains Mono Variable', monospace;
- --radius: 2px;
- font-family: 'Manrope Variable', sans-serif;
+ --font-heading: 'IBM Plex Sans Variable', 'Helvetica Neue', sans-serif;
+ --font-body: 'IBM Plex Sans Variable', 'Helvetica Neue', sans-serif;
+ --font-reading: 'Newsreader Variable', Georgia, serif;
+ --font-mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;
+ --radius: 0;
+ font-family: var(--font-body);
color: var(--ink);
background: var(--paper);
font-synthesis: none;
}
+
:root[data-theme='dark'] {
- --paper: #171b19;
- --paper-raised: #202522;
- --ink: #ecebe4;
- --muted: #a7afa9;
- --faint: #39413c;
- --accent: #ff7658;
- --accent-soft: #512e26;
- --positive: #62c99a;
- --color-muted: #292f2b;
+ --paper: #07110f;
+ --paper-raised: #0d1b18;
+ --ink: #e9f4f1;
+ --muted: #96aaa5;
+ --faint: #29413c;
+ --grid: rgba(129, 202, 191, .075);
+ --accent: #4ee8dc;
+ --accent-bright: #4ee8dc;
+ --accent-soft: #123e39;
+ --positive: #63d99b;
+ --panel: #0c211d;
+ --panel-text: #effbf8;
+ --panel-muted: #9fb9b4;
+ --color-muted: #12231f;
}
+
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body { margin: 0; min-width: 320px; background: var(--paper); color: var(--ink); }
@@ -41,110 +59,135 @@ button { font: inherit; }
::selection { background: var(--accent-soft); }
.skip-link { position: fixed; left: 1rem; top: -5rem; z-index: 100; background: var(--ink); color: var(--paper); padding: .7rem 1rem; }
.skip-link:focus { top: 1rem; }
-.site-header { height: 76px; display: flex; align-items: center; justify-content: space-between; padding: 0 clamp(1.2rem, 4vw, 4.5rem); border-bottom: 1px solid var(--faint); position: relative; z-index: 10; }
-.wordmark { text-decoration: none; display: flex; gap: .72rem; align-items: center; font-size: .82rem; font-weight: 730; letter-spacing: -.02em; }
-.mark { width: 12px; height: 12px; background: var(--accent); display: inline-block; clip-path: polygon(0 0, 100% 0, 100% 55%, 55% 55%, 55% 100%, 0 100%); }
-.site-header nav { display: flex; gap: 1.5rem; align-items: center; font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; }
+
+.site-header { height: 68px; display: flex; align-items: center; justify-content: space-between; padding: 0 clamp(1.2rem, 4vw, 4.5rem); border-bottom: 1px solid var(--faint); position: relative; z-index: 10; background: var(--paper); }
+.wordmark { text-decoration: none; display: grid; grid-template-columns: 20px auto; column-gap: .8rem; align-items: center; font-size: .78rem; font-weight: 650; letter-spacing: -.01em; }
+.wordmark > span:last-child { grid-column: 2; }
+.wordmark-code { grid-column: 2; color: var(--accent); font: 500 .56rem/1 var(--font-mono); letter-spacing: .09em; text-transform: uppercase; }
+.mark { grid-row: 1 / span 2; width: 20px; height: 20px; border: 1px solid var(--accent); display: inline-block; position: relative; }
+.mark::before, .mark::after { content: ''; position: absolute; background: var(--accent); }
+.mark::before { width: 8px; height: 1px; left: 5px; top: 9px; }
+.mark::after { width: 1px; height: 8px; left: 9px; top: 5px; }
+.site-header nav { display: flex; gap: 1.5rem; align-items: center; font: 500 .66rem var(--font-mono); text-transform: uppercase; letter-spacing: .07em; }
.site-header nav a { text-decoration: none; }
.site-header nav a:hover { color: var(--accent); }
.theme-toggle { border: 1px solid var(--faint); background: transparent; color: inherit; width: 36px; height: 36px; cursor: pointer; }
-.theme-toggle:hover { border-color: var(--ink); }
-.journal-intro { padding: clamp(4rem, 9vw, 9rem) clamp(1.2rem, 7vw, 8rem) clamp(3rem, 7vw, 6rem); border-bottom: 1px solid var(--faint); }
-.eyebrow, .section-label, .story-meta, .entry-kicker { display: flex; align-items: center; gap: .7rem; color: var(--muted); font: 600 .68rem/1.2 var(--font-mono); text-transform: uppercase; letter-spacing: .08em; }
-.live-pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--positive); box-shadow: 0 0 0 4px color-mix(in oklab, var(--positive) 15%, transparent); }
-.journal-intro h1 { max-width: 1100px; margin: 1.6rem 0 0; font: 420 clamp(3.7rem, 9vw, 9rem)/.83 var(--font-heading); letter-spacing: -.065em; }
-.journal-intro h1 em { color: var(--accent); font-weight: 380; }
-.intro-meta { margin-top: clamp(3rem, 6vw, 6rem); display: grid; grid-template-columns: minmax(260px, 600px) 1fr; gap: 3rem; align-items: end; }
-.intro-meta > p { margin: 0; color: var(--muted); font: 470 clamp(1rem, 1.5vw, 1.25rem)/1.6 'Manrope Variable'; }
+.theme-toggle:hover { color: var(--accent); border-color: var(--accent); }
+
+.journal-intro { position: relative; padding: clamp(4rem, 8vw, 8rem) clamp(1.2rem, 7vw, 8rem) clamp(3rem, 6vw, 5.5rem); border-bottom: 1px solid var(--faint); background-image: linear-gradient(to right, var(--grid) 1px, transparent 1px), linear-gradient(to bottom, var(--grid) 1px, transparent 1px); background-size: 64px 64px; background-position: -1px -1px; }
+.journal-intro::after { content: ''; position: absolute; right: clamp(1.2rem, 7vw, 8rem); top: clamp(2rem, 4vw, 4rem); width: 46px; height: 46px; border-top: 1px solid var(--accent); border-right: 1px solid var(--accent); }
+.eyebrow, .section-label, .story-meta, .entry-kicker { display: flex; align-items: center; gap: .7rem; color: var(--muted); font: 500 .64rem/1.2 var(--font-mono); text-transform: uppercase; letter-spacing: .09em; }
+.live-pulse { width: 7px; height: 7px; border-radius: 50%; background: var(--positive); box-shadow: 0 0 0 4px color-mix(in oklab, var(--positive) 14%, transparent); }
+.journal-intro h1 { max-width: 1080px; margin: 1.8rem 0 0; font: 650 clamp(3.25rem, 7.4vw, 7.6rem)/.91 var(--font-heading); letter-spacing: -.057em; text-wrap: balance; }
+.journal-intro h1 em { color: var(--accent); font-weight: 420; font-style: normal; }
+.intro-meta { margin-top: clamp(3rem, 6vw, 6rem); display: grid; grid-template-columns: minmax(260px, 630px) 1fr; gap: 3rem; align-items: end; }
+.intro-meta > p { margin: 0; color: var(--muted); font: 410 clamp(1rem, 1.4vw, 1.2rem)/1.64 var(--font-body); }
.intro-meta dl { margin: 0; justify-self: end; display: flex; gap: clamp(2rem, 5vw, 5rem); }
.intro-meta dl div { display: grid; gap: .5rem; }
-.intro-meta dt { font: 600 .65rem var(--font-mono); color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
-.intro-meta dd { margin: 0; font: 600 .85rem 'Manrope Variable'; }
-.lead-story { display: grid; grid-template-columns: minmax(0, 1fr) minmax(360px, .95fr); height: min(68vw, 780px); min-height: 620px; border-bottom: 1px solid var(--faint); }
-.lead-copy { text-decoration: none; padding: clamp(3rem, 6vw, 7rem) clamp(1.2rem, 7vw, 8rem); display: flex; flex-direction: column; align-items: flex-start; }
+.intro-meta dt { font: 500 .6rem var(--font-mono); color: var(--muted); text-transform: uppercase; letter-spacing: .08em; }
+.intro-meta dd { margin: 0; font: 600 .8rem var(--font-body); font-variant-numeric: tabular-nums; }
+
+.lead-story { display: grid; grid-template-columns: minmax(0, 1fr) minmax(360px, .95fr); height: min(66vw, 760px); min-height: 600px; border-bottom: 1px solid var(--faint); }
+.lead-copy { text-decoration: none; padding: clamp(3rem, 6vw, 7rem) clamp(1.2rem, 7vw, 8rem); display: flex; flex-direction: column; align-items: flex-start; background: var(--panel); color: var(--panel-text); }
+.lead-copy .story-meta, .lead-copy > p { color: var(--panel-muted); }
.story-meta { gap: .55rem; }
-.story-meta span:not(:last-child)::after { content: '·'; margin-left: .55rem; color: var(--faint); }
-.lead-copy h2 { margin: auto 0 1.5rem; max-width: 760px; font: 430 clamp(2.6rem, 5.5vw, 6rem)/.93 var(--font-heading); letter-spacing: -.045em; text-wrap: balance; }
-.lead-copy > p { margin: 0; max-width: 630px; color: var(--muted); line-height: 1.65; font-size: .98rem; }
-.read-link { margin-top: 2rem; font-size: .75rem; text-transform: uppercase; letter-spacing: .08em; font-weight: 750; border-bottom: 1px solid currentColor; padding-bottom: .3rem; }
-.lead-copy:hover .read-link { color: var(--accent); }
-.lead-visual { min-height: 440px; overflow: hidden; background: #242b27; display: block; }
+.story-meta span:not(:last-child)::after { content: '/'; margin-left: .55rem; color: var(--accent-bright); }
+.lead-copy h2 { margin: auto 0 1.5rem; max-width: 760px; font: 590 clamp(2.4rem, 5vw, 5.4rem)/.96 var(--font-heading); letter-spacing: -.042em; text-wrap: balance; }
+.lead-copy > p { margin: 0; max-width: 630px; line-height: 1.65; font-size: .94rem; }
+.read-link { margin-top: 2rem; color: var(--accent-bright); font: 500 .64rem var(--font-mono); text-transform: uppercase; letter-spacing: .08em; border-bottom: 1px solid currentColor; padding-bottom: .35rem; }
+.lead-copy:hover .read-link { color: var(--panel-text); }
+.lead-visual { min-height: 420px; overflow: hidden; background: #071513; display: block; position: relative; }
+.lead-visual::after { content: 'EVIDENCE / PRIMARY'; position: absolute; right: 1rem; bottom: 1rem; padding: .4rem .55rem; color: var(--evidence-text); background: color-mix(in oklab, var(--media-black) 84%, transparent); font: 500 .56rem var(--font-mono); letter-spacing: .08em; }
.lead-visual img { width: 100%; height: 100%; object-fit: cover; transition: transform .7s cubic-bezier(.22,1,.36,1); }
-.lead-visual:hover img { transform: scale(1.018); }
-.type-visual { display: grid; place-items: center; color: #f2f0e9; font: 500 clamp(4rem, 10vw, 10rem) var(--font-heading); letter-spacing: -.08em; }
+.lead-visual:hover img { transform: scale(1.015); }
+.type-visual { display: grid; place-items: center; color: var(--evidence-text); font: 620 clamp(3.5rem, 8vw, 8rem) var(--font-heading); letter-spacing: -.06em; }
+
.archive { padding: clamp(4rem, 7vw, 7rem) clamp(1.2rem, 4vw, 4.5rem); }
-.archive-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 1.5rem; border-bottom: 1px solid var(--ink); }
-.archive-head h2 { margin: 0; font: 460 clamp(2rem, 3.5vw, 3.6rem)/1 var(--font-heading); letter-spacing: -.04em; }
-.archive-head p { margin: 0; color: var(--muted); font: .67rem var(--font-mono); text-transform: uppercase; }
-.filter-bar { display: flex; gap: .45rem; padding: 1.2rem 0 2rem; overflow: auto; }
-.filter-bar button { background: transparent; color: var(--muted); border: 1px solid var(--faint); padding: .55rem .8rem; cursor: pointer; font: 650 .65rem var(--font-mono); text-transform: uppercase; letter-spacing: .04em; }
+.archive-head { display: flex; justify-content: space-between; align-items: baseline; padding-bottom: 1.25rem; border-bottom: 1px solid var(--ink); }
+.archive-head h2 { margin: 0; font: 600 clamp(1.8rem, 3vw, 3rem)/1 var(--font-heading); letter-spacing: -.035em; }
+.archive-head p { margin: 0; color: var(--muted); font: .62rem var(--font-mono); text-transform: uppercase; }
+.filter-bar { display: flex; gap: .4rem; padding: 1.1rem 0 2rem; overflow: auto; }
+.filter-bar button { background: transparent; color: var(--muted); border: 1px solid var(--faint); padding: .55rem .8rem; cursor: pointer; font: 500 .6rem var(--font-mono); text-transform: uppercase; letter-spacing: .04em; }
.filter-bar button:hover, .filter-bar button.active { color: var(--paper); background: var(--ink); border-color: var(--ink); }
-.journal-row { border-top: 1px solid var(--faint); }
+.journal-row { border-top: 1px solid var(--faint); content-visibility: auto; contain-intrinsic-size: auto 170px; }
.journal-row:last-child { border-bottom: 1px solid var(--faint); }
-.journal-row > a { min-height: 190px; display: grid; grid-template-columns: 60px minmax(0, 1fr) minmax(130px, 220px); gap: clamp(1rem, 3vw, 3rem); align-items: center; padding: 1.5rem .2rem; text-decoration: none; }
-.journal-row:hover { background: color-mix(in oklab, var(--paper-raised) 70%, transparent); }
-.row-index { color: var(--muted); font: .65rem var(--font-mono); align-self: start; padding-top: .3rem; }
-.row-copy h3 { margin: .65rem 0 .6rem; max-width: 820px; font: 470 clamp(1.65rem, 3vw, 3.2rem)/1.02 var(--font-heading); letter-spacing: -.035em; }
-.row-copy p { margin: 0; color: var(--muted); max-width: 760px; font-size: .82rem; line-height: 1.6; }
-.row-end { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: .8rem; align-self: stretch; color: var(--muted); font: .62rem var(--font-mono); text-transform: uppercase; }
-.row-end img { width: 100%; height: 100px; object-fit: cover; grid-column: 1 / -1; filter: saturate(.82); }
-.row-end > span { justify-self: end; font-size: 1.1rem; color: var(--ink); }
+.journal-row > a { min-height: 170px; display: grid; grid-template-columns: 54px minmax(0, 1fr) minmax(130px, 220px); gap: clamp(1rem, 3vw, 3rem); align-items: center; padding: 1.35rem .2rem; text-decoration: none; }
+.journal-row:hover { background: var(--paper-raised); }
+.row-index { color: var(--accent); font: .58rem var(--font-mono); align-self: start; padding-top: .3rem; font-variant-numeric: tabular-nums; }
+.row-copy h3 { margin: .62rem 0 .58rem; max-width: 860px; font: 570 clamp(1.5rem, 2.5vw, 2.65rem)/1.05 var(--font-heading); letter-spacing: -.032em; }
+.row-copy p { margin: 0; color: var(--muted); max-width: 760px; font-size: .8rem; line-height: 1.62; }
+.row-end { display: grid; grid-template-columns: minmax(0,1fr) auto; align-items: end; gap: .8rem; align-self: stretch; color: var(--muted); font: .57rem var(--font-mono); text-transform: uppercase; }
+.row-end img { width: 100%; height: 92px; object-fit: cover; grid-column: 1 / -1; filter: saturate(.8) contrast(1.04); }
+.row-end > span { justify-self: end; font-size: 1rem; color: var(--accent); }
.empty-filter { color: var(--muted); padding: 4rem 0; }
-.site-footer { border-top: 1px solid var(--faint); padding: 2rem clamp(1.2rem, 4vw, 4.5rem); display: flex; justify-content: space-between; color: var(--muted); font: .65rem/1.5 var(--font-mono); }
+.journal-sentinel { min-height: 96px; display: flex; align-items: center; justify-content: space-between; gap: 1rem; border-bottom: 1px solid var(--faint); color: var(--muted); font: 500 .62rem var(--font-mono); text-transform: uppercase; letter-spacing: .05em; }
+.journal-load-button { min-height: 44px; border: 1px solid var(--faint); background: transparent; color: var(--ink); padding: .7rem 1rem; cursor: pointer; font: 500 .62rem var(--font-mono); text-transform: uppercase; letter-spacing: .05em; }
+.journal-load-button:hover, .journal-load-button:focus-visible { color: var(--paper); background: var(--ink); border-color: var(--ink); }
+.journal-load-button:disabled { cursor: wait; opacity: .55; }
+.journal-noscript { color: var(--muted); font-size: .8rem; }
+.archive-head a { text-underline-offset: .2em; }
+
+.site-footer { border-top: 1px solid var(--faint); padding: 2rem clamp(1.2rem, 4vw, 4.5rem); display: flex; justify-content: space-between; color: var(--muted); font: .6rem/1.5 var(--font-mono); }
.site-footer p { margin: 0; }
+
.entry-shell { padding-bottom: clamp(5rem, 10vw, 10rem); }
-.entry-header { padding: clamp(4rem, 10vw, 9rem) clamp(1.2rem, 13vw, 15rem) clamp(3rem, 6vw, 6rem); }
+.entry-header { position: relative; padding: clamp(4rem, 9vw, 8rem) clamp(1.2rem, 13vw, 15rem) clamp(3rem, 6vw, 6rem); background-image: linear-gradient(to right, var(--grid) 1px, transparent 1px); background-size: 80px 100%; }
.entry-header.compact { padding-bottom: clamp(4rem, 8vw, 8rem); }
-.back-link { display: inline-block; margin-bottom: 3rem; color: var(--muted); text-decoration: none; font: 650 .66rem var(--font-mono); text-transform: uppercase; }
+.back-link { display: inline-block; margin-bottom: 3rem; color: var(--muted); text-decoration: none; font: 500 .62rem var(--font-mono); text-transform: uppercase; }
.back-link:hover { color: var(--accent); }
.status-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); }
.status-dot.published { background: var(--positive); }
-.status-dot.running { background: #d89b28; }
-.status-dot.blocked { background: var(--accent); }
-.entry-header h1 { margin: 1.4rem 0 0; max-width: 1150px; font: 430 clamp(3rem, 7.5vw, 8rem)/.9 var(--font-heading); letter-spacing: -.055em; text-wrap: balance; }
-.entry-deck { margin: clamp(2rem, 4vw, 4rem) 0 0; max-width: 760px; color: var(--muted); font-size: clamp(1rem, 1.5vw, 1.25rem); line-height: 1.65; }
-.tag-row { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: .5rem; }
-.tag-row span { border: 1px solid var(--faint); padding: .4rem .65rem; font: .62rem var(--font-mono); }
-.article-rule { margin: 0 clamp(1.2rem, 4vw, 4.5rem); height: 30px; border-top: 1px solid var(--ink); display: flex; justify-content: flex-end; }
-.article-rule span { width: 90px; height: 8px; background: var(--accent); }
+.status-dot.running { background: var(--warning); }
+.status-dot.blocked { background: var(--danger); }
+.entry-header h1 { margin: 1.5rem 0 0; max-width: 1150px; font: 620 clamp(2.75rem, 6.4vw, 6.6rem)/.94 var(--font-heading); letter-spacing: -.052em; text-wrap: balance; }
+.entry-deck { margin: clamp(2rem, 4vw, 4rem) 0 0; max-width: 760px; color: var(--muted); font-size: clamp(1rem, 1.4vw, 1.18rem); line-height: 1.68; }
+.tag-row { margin-top: 2rem; display: flex; flex-wrap: wrap; gap: .45rem; }
+.tag-row span { border: 1px solid var(--faint); padding: .38rem .62rem; font: .58rem var(--font-mono); text-transform: uppercase; }
+.article-rule { margin: 0 clamp(1.2rem, 4vw, 4.5rem); height: 24px; border-top: 1px solid var(--ink); display: flex; justify-content: flex-end; }
+.article-rule span { width: 120px; height: 5px; background: var(--accent); }
.article-layout { display: grid; grid-template-columns: minmax(130px, 1fr) minmax(0, 760px) minmax(130px, 1fr); padding: clamp(2rem, 5vw, 5rem) clamp(1.2rem, 4vw, 4.5rem); }
-.article-rail { grid-column: 1; display: flex; flex-direction: column; gap: .45rem; color: var(--muted); font: .62rem var(--font-mono); text-transform: uppercase; }
-.article-rail strong { color: var(--ink); font-size: .7rem; }
+.article-rail { grid-column: 1; display: flex; flex-direction: column; gap: .45rem; color: var(--muted); font: .58rem var(--font-mono); text-transform: uppercase; }
+.article-rail strong { color: var(--accent); font-size: .64rem; }
.article-body { grid-column: 2; min-width: 0; }
-.typeset-report { --typeset-font-body: 'Newsreader Variable', Georgia, serif; --typeset-font-heading: 'Manrope Variable', sans-serif; --typeset-font-mono: 'JetBrains Mono Variable', monospace; --typeset-size: 18px; --typeset-leading: 1.78; --typeset-flow: 1.45em; }
+.typeset-report { --typeset-font-body: var(--font-reading); --typeset-font-heading: var(--font-heading); --typeset-font-mono: var(--font-mono); --typeset-size: 18px; --typeset-leading: 1.8; --typeset-flow: 1.48em; }
.typeset-report > h1:first-child { display: none; }
-.typeset-report :where(table) { font-family: 'Manrope Variable'; font-size: .76em; }
+.typeset-report :where(table) { font-family: var(--font-body); font-size: .74em; font-variant-numeric: tabular-nums; }
.typeset-report .typeset-scroll :where(table) { min-width: 680px; }
.typeset-report :where(img, video) { width: 100%; background: var(--paper-raised); }
.typeset-report :where(h2) { letter-spacing: -.025em; }
+.typeset-report :where(a) { text-decoration-color: var(--accent); text-underline-offset: .18em; }
+
.media-gallery { padding: 0 clamp(1.2rem, 4vw, 4.5rem) clamp(3rem, 6vw, 6rem); }
.section-label { justify-content: space-between; padding: 1rem 0; border-top: 1px solid var(--ink); }
.media-grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 1rem; }
.media-item { grid-column: span 6; margin: 0; }
.media-item:first-child:last-child, .media-item.comparison { grid-column: 1 / -1; }
-.media-item > img, .media-item > video { width: 100%; max-height: 75vh; object-fit: contain; display: block; background: #0e1110; }
-.media-item figcaption { color: var(--muted); margin-top: .7rem; font-size: .72rem; line-height: 1.5; }
-.stream-frame { aspect-ratio: 16/9; background: #0e1110; }
+.media-item > img, .media-item > video { width: 100%; max-height: 75vh; object-fit: contain; display: block; background: var(--media-black); }
+.media-item figcaption { color: var(--muted); margin-top: .7rem; font-size: .7rem; line-height: 1.5; }
+.stream-frame { aspect-ratio: 16/9; background: var(--media-black); }
.stream-frame iframe { width: 100%; height: 100%; border: 0; }
.comparison-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1px; background: var(--faint); }
-.comparison-pair > div { position: relative; background: #0e1110; }
+.comparison-pair > div { position: relative; background: var(--media-black); }
.comparison-pair img { display: block; width: 100%; }
-.comparison-pair span { position: absolute; z-index: 1; top: .8rem; left: .8rem; background: rgba(16,18,17,.82); color: white; padding: .35rem .5rem; font: .58rem var(--font-mono); text-transform: uppercase; }
+.comparison-pair span { position: absolute; z-index: 1; top: .8rem; left: .8rem; background: color-mix(in oklab, var(--media-black) 84%, transparent); color: var(--paper-raised); padding: .35rem .5rem; font: .56rem var(--font-mono); text-transform: uppercase; }
+
.system-grid { border-top: 1px solid var(--faint); border-bottom: 1px solid var(--faint); display: grid; grid-template-columns: repeat(3,1fr); padding: 0 clamp(1.2rem, 4vw, 4.5rem); }
.system-grid section { padding: 3rem clamp(1rem,3vw,3rem); border-right: 1px solid var(--faint); }
.system-grid section:last-child { border-right: 0; }
-.system-num { color: var(--accent); font: .65rem var(--font-mono); }
-.system-grid h2 { font: 500 2rem var(--font-heading); margin: 2rem 0 1rem; }
-.system-grid p { color: var(--muted); font-size: .82rem; line-height: 1.65; }
+.system-num { color: var(--accent); font: .6rem var(--font-mono); }
+.system-grid h2 { font: 600 1.8rem var(--font-heading); margin: 2rem 0 1rem; letter-spacing: -.025em; }
+.system-grid p { color: var(--muted); font-size: .8rem; line-height: 1.68; }
.system-prose { margin: clamp(4rem,8vw,8rem) auto 0; padding: 0 1.2rem; }
+
@media (max-width: 800px) {
.site-header { height: 64px; }
.site-header nav a { display: none; }
- .journal-intro h1 { letter-spacing: -.05em; }
+ .journal-intro { background-size: 48px 48px; }
+ .journal-intro h1 { letter-spacing: -.048em; }
.intro-meta { grid-template-columns: 1fr; }
.intro-meta dl { justify-self: start; flex-wrap: wrap; }
.lead-story { grid-template-columns: 1fr; height: auto; min-height: 0; }
- .lead-copy { min-height: 500px; }
+ .lead-copy { min-height: 480px; }
.lead-visual { min-height: 360px; }
.journal-row > a { grid-template-columns: 32px minmax(0,1fr); }
.row-end { display: none; }
@@ -158,4 +201,5 @@ button { font: inherit; }
.site-footer { flex-direction: column; gap: .8rem; }
.typeset-report { --typeset-size: 16px; --typeset-leading: 1.72; --typeset-flow: 1.35em; }
}
+
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; } }
diff --git a/src/styles/typeset.css b/src/styles/typeset.css
index 5079441..6c3334f 100644
--- a/src/styles/typeset.css
+++ b/src/styles/typeset.css
@@ -1,3 +1,4 @@
+/* impeccable-disable design-system-radius: shadcn Typeset owns proportional inline/content radii; the site-level token resolves application components to square corners. */
/*
* shadcn/typeset
* https://ui.shadcn.com/docs/typeset.
diff --git a/tests/journal-build-contract.test.mjs b/tests/journal-build-contract.test.mjs
new file mode 100644
index 0000000..539ae7f
--- /dev/null
+++ b/tests/journal-build-contract.test.mjs
@@ -0,0 +1,19 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { readFile } from 'node:fs/promises';
+
+test('static build exposes an initial journal batch and a cursor manifest', async () => {
+ const homepage = await readFile(new URL('../dist/index.html', import.meta.url), 'utf8');
+ const renderedHomepage = homepage.split('')[0];
+ const initialRows = renderedHomepage.match(/ 0 && response.entries.length <= 12);
+ assert.ok(response.entries.every((entry) => entry.url.startsWith('/progress/')));
+});
diff --git a/tests/journal-pagination.test.mjs b/tests/journal-pagination.test.mjs
new file mode 100644
index 0000000..8860bc2
--- /dev/null
+++ b/tests/journal-pagination.test.mjs
@@ -0,0 +1,15 @@
+import test from 'node:test';
+import assert from 'node:assert/strict';
+import { paginateJournal } from '../src/lib/journal-pagination.mjs';
+
+test('paginateJournal emits stable cursor pages without losing entries', () => {
+ const entries = Array.from({ length: 25 }, (_, index) => ({ id: `entry-${index + 1}` }));
+
+ const pages = paginateJournal(entries, 10);
+
+ assert.equal(pages.length, 3);
+ assert.deepEqual(pages.map((page) => page.cursor), ['1', '2', '3']);
+ assert.deepEqual(pages.map((page) => page.nextCursor), ['2', '3', null]);
+ assert.deepEqual(pages.map((page) => page.entries.length), [10, 10, 5]);
+ assert.deepEqual(pages.flatMap((page) => page.entries), entries);
+});