fix: keep the thin strokes of the wordmark on the panel

The A of ITA had lost its diagonal: at 260px the hairlines of a Didone
fall below a pixel, downscaling averages them into grey, and the
panel's mid-grey cut then discarded them. What reached the shop window
was a detached serif and something closer to a lambda.

Letting more grey count as ink keeps them whole. 165 was not enough —
the diagonal came back dotted — and 195 holds it solid without
thickening the stems into mud. The number belongs to this particular
drawing, so `npm run brand:variants` renders the grid of widths and
thresholds it was chosen from, magnified, for the day the logo changes.

Width stays at 260: the header shares its line with the date, and the
main block is narrower now the week runs down the right-hand side.

The panel will redraw once when it next wakes, since the image and
therefore its content hash have changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
This commit is contained in:
2026-09-21 23:24:53 +02:00
co-authored by Claude Opus 5
parent 13abcf3242
commit 61074d02f9
4 changed files with 111 additions and 2 deletions
+18 -1
View File
@@ -29,6 +29,21 @@ const LOGO_URL =
/** Wide enough to read from the pavement, short enough for a 76px header. */
const EINK_WIDTH = 260;
/**
* Where grey becomes ink.
*
* The wordmark is a Didone: its hairlines are a fraction of its stems, and at
* 260px they land below one pixel. Downscaling averages them into grey, and
* the panel's default mid-grey cut then threw them away — the diagonal of the
* A vanished entirely, leaving a detached serif and something closer to a
* lambda. Letting more grey count as ink keeps the thin strokes whole.
*
* 165 was still not enough: the diagonal came back dotted. 195 holds it solid
* without thickening the stems into mud. Re-check with `npm run brand:variants`
* if the logo is ever replaced — this number belongs to this drawing.
*/
const EINK_THRESHOLD = 195;
async function main(): Promise<void> {
const directory = path.join(process.cwd(), 'public', 'brand');
await mkdir(directory, { recursive: true });
@@ -52,7 +67,9 @@ async function main(): Promise<void> {
// Reuse the panel's own encoder, so the logo is thresholded by exactly the
// rule the rest of the screen is. A line drawing gets no dithering: it would
// only fuzz the serifs.
const bitmap = toBitmap(cropped.data, cropped.info.width, cropped.info.height);
const bitmap = toBitmap(cropped.data, cropped.info.width, cropped.info.height, {
threshold: EINK_THRESHOLD,
});
await writeFile(path.join(directory, 'logo-eink.png'), encodePng(bitmap));
console.log(`logo.png ${original.length} bytes (original)`);