#2 Mobile top bar → hamburger: extracted the editorial bar (brand + nav + new
collapsible hamburger drop-panel) into a shared lib/components/HubBar.svelte,
used by both /home3 and HubShell (the /word /quote /onthisday detail pages), so
there's one nav to maintain/audit. Full horizontal nav ≥721px; hamburger + drop
panel ≤720px. Escape + link-click close it; panel is hidden on desktop as a
safety. Removed the duplicated bar markup/CSS from home3 + HubShell.
#1 Mobile layout / Art card: on phones the Art card now stacks image-first with
the painting in a proper 3:2 frame (aspect-ratio) instead of a stubby fixed
130px band that cropped the work to a sliver. Also drop the News gist's bottom
fade once the bento is single-column (natural height = no truncation, so the
fade was just dimming the final line), and let the joys header wrap on phones.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Admin joy item route moved to /api/admin/joys/{kind}/items/{item_id} so the
/add and /repick verbs resolve to their own routes instead of 422-ing as a
non-int item id (the launch blocker). Frontend mutate URL updated to match.
- Re-pick now excludes the currently-shown item: the endpoint reads today's
daily pool_id and passes it as `avoid`, so "Re-pick today" yields a different
item. Added `avoid` to pick_daily/_candidates across wotd/quote/onthisday.
- WOTD sense selection: the LLM now proposes word + intended part of speech, and
_lookup prefers that sense (fixes "serene" returning the archaic noun).
- On This Day tone prompt tightened to favor genuinely uplifting events and
exclude merely procedural/political-administrative ones.
- Caddy @hidden now also noindexes /word /quote /onthisday /admin (+ .html).
- Regression tests: add/repick resolve (401 not 422), add/feature/block/delete,
re-pick excludes current; WOTD pos-preference + proposal parsing units.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /home3: small-joys rail now reads live /api/word|quote|onthisday/today (placeholders only
as fallback); each cell links to its detail page.
- HubShell component (shared bar/footer/fonts/tokens) for the hub + detail pages.
- /word: big word, IPA, Listen (cached clip + browser-TTS fallback), definition, sentences.
- /quote: the quote, attribution, and the AI "what it means".
- /onthisday: the date, year + fact, image, summary, source.
- Admin "Small Joys" tab: per-pool list with feature/block/delete/add + re-pick, for all
three kinds. New admin API: GET/POST /api/admin/joys/{kind}[/{id}|/add|/repick].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two jewel cards at a time, reader-advanced (no auto-motion); 3 cells total, wraps. Keeps
each card at generous size instead of cramming three.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decorative tiles (no live answer = no spoilers); placeholder copy. Built from CD's
Play Card Graphic handoff.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- News summary + headline clamped to 3 lines so the long brief gist no longer stretches
the bento (option 1; placeholder-bubble option still available if preferred).
- News CTA "Read the brief" → "Read the good news" (clearer for first-time visitors).
- Art swatch crops a few px off every edge (::after inset) to remove the black scan edge
at the top of paintings.
- Logo 42→48px, nav 15→16.5px, card titles bumped + weight 500→600 so they pop on scan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rebuilds the design handoff's preferred Frame A: Newsreader serif headlines + Hanken
Grotesk body (both self-hosted, OFL, no Google hotlink), warm cream canvas, per-card
accent tints (News teal, Art plum, Play amber, Moment green), bento grid.
- Uses our real /logo.svg instead of the mock's Bricolage wordmark + sunrise.
- Wired live: Good News pulls the top headline/summary/photo (respects the saved
Closer-to-Home filter); Daily Art pulls today's Met piece (title/artist/year + thumbnail).
- Hidden prototype (noindex), spacing tuned per the /home2 pass (hero pulled up, more air
before the bento). Sits beside /home2 for comparison.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- All four cards now tinted with a taller (18px), darker (~24%) top color strip.
- Photos (news top, art right) sit inset + rounded so the card colour frames them.
- Static cards: header centered; CTA pinned to the bottom so Play/Daily Moment links line
up. News tint = soft amber, Art = soft lilac, Play = sky, Moment = sage.
- Hub spacing: intro pulled up a touch, more gap between the intro and the cards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Art photo moved to the RIGHT (text/icon left) so it no longer clusters with the news
photo in the center.
- Unified header: every card now has icon + title side-by-side; card titles switched from
serif (too 'document') to Manrope bold. Art icon/title now match Play/Daily Moment sizes.
- Static cards (Play/Daily Moment) gain a darker top color strip (Monopoly-card feel) over
the tinted body, derived from each tint via color-mix.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Self-hosted Manrope (OFL) as the hub sans; nav lighter (weight 500, soft slate, not all
"on"). Logo up to 58px.
- News card: photo on top + headline below, and it now respects the reader's saved
Closer-to-Home filter (goodnews:home/homeScope) so the headline matches their Brief.
- Art card: rectangular cover-cropped thumbnail on the LEFT (crops ragged scan edges),
text on the right — variety against the photo-top news card.
- Play/Daily Moment: tinted backgrounds, bigger centered icon+title, blurb left-aligned.
- /fonts/* + /textures/* served immutable (Caddy live + snapshot).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New sections registry (lib/rooms.js): each room is one data entry (title/blurb/href/cta/
size/preview/icon) — add or resize by editing the list.
- Reusable RoomCard (lib/components/RoomCard.svelte) with size variants and hybrid previews:
Art shows today's live thumbnail, News shows today's top headline, others are blurb+CTA.
- /home2 hidden prototype (noindex, unlinked) with a bigger top bar (logo 50px, larger nav
labels) and a reflowing grid hub of the four rooms (News/Art/Play/Daily Moment). Iterate
the look here, then promote to / and remove the clone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /api/art/image/{id} now answers HEAD as well as GET (was 404 on HEAD) — mirrors the
/a/{id} fix. Added tests/test_art_api.py (GET+HEAD+size=full fallback + today payload).
- /textures/* served immutable (long cache) instead of no-cache; excluded from the
revalidate matcher. Live Caddyfile + repo snapshot both updated.
- Lightbox: Escape closes it, and focus moves to it on open (keyboard-friendly).
- Trimmed the gallery's top padding so "Daily Art" sits closer to the bar.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Woods are now built from four real mitered rails clipped to 45° joints, each showing the
grain along its length — horizontal on top/bottom (rotated texture wood-grain-h.jpg),
vertical on the sides — so the grain TURNS at every corner like real cut moulding. Each
rail carries a directional bevel (lit top-left) for a rounded profile; per-species recolor
moved onto the rails. Rendered via a Svelte snippet in both page + full-screen frames.
- Gold/Silver gain fine brushed-metal striations over the existing sheen.
- New asset: wood-grain-h.jpg (the CC0 texture rotated 90°), credited in textures/CREDITS.txt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- No frame in full-screen now fills the screen (img max 96vw / 88vh instead of the
framed cap).
- Full-screen backdrop is a soft top-lit gallery wall (light, not dark) so the Black
frame — and every frame — reads like a piece hung on a real wall; caption text darkened
to match.
- Mat now sits recessed below the moulding: a rabbet groove line at the lip, frame shadow
on the mat's top/left, catch-light on bottom/right — a small chiseled step in place of
the flat frame→mat transition.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Black frame (no grain) added after Silver — a satin charcoal moulding in the metals family.
- Full-screen rail bumped (clamp 18–36px) so the moulding reads as thick as the page view
against the much larger image; mat held.
- Thickness slider extended to 1.9× and the mat now caps at 1.5× (min()), so the top of
the slider thickens only the wood while the white border stays put. Applies to the
full-screen frame too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Replace procedural grain with a real fine-grain wood texture (CC0, Poly Haven —
static/textures/wood-grain.jpg). The three woods share it, recolored per species via
CSS filter on ::before: Walnut (as-is dark), Oak (lightened/warmed), Mahogany (new —
deepened/reddened). Miters now apply to all three woods; mat sits between texture and
miters via z-index.
- Full-screen sizing fixed: image capped (max-height 66vh) so the bottom rail always
fits — no more cutoff. Rail/mat dialed back to ~1:1 wood:white (was mat-heavy), only
slightly larger than the page so the proportion matches the small view.
Texture credit (CC0, no attribution required) recorded in static/textures/CREDITS.txt.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Oak grain is now a real feTurbulence noise field (organic wood, no drawn-on lines),
with a soft light/shade for moulding roundness; miters kept.
- Thickness slider (0.7–1.5×) scales rail + mat via --frame-scale, remembered locally;
applies to both the page and the full-screen view.
- No-jump layout: every frame — including "No frame" — reserves the same footprint
(rail + mat as CSS vars), so switching never reflows the page. "No frame" is the bare
art floating with a soft shadow.
- Full-screen wears a thicker rail + mat so the white border keeps its proportion at
large size; "No frame" still maximizes there.
- Frame chips redesigned as beveled beads with a clean offset selection ring.
- Subtle fading hairline divider between the title and the artwork.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New "Oak" frame: real wood-grain along the rails plus 45° mitered corner joints
(CSS conic-free miter via per-corner diagonal seam gradients sized to the rail width),
so it reads as four lengths of moulding meeting at the corners.
- The lightbox now wears the selected frame too — the same moulding + cream mat around
the full-resolution image, which makes the piece pop on a dark full screen. Image is
capped to leave room for the frame; "No frame" still maximizes size.
- Frame picker wraps gracefully now that there are five options.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Virtual frames (Walnut/Gold/Silver/None), selectable + remembered in localStorage,
built as a beveled moulding around a cream museum mat.
- Header uses the real /logo.svg wordmark; the "No ads" pill is replaced by an
account icon (the pill doesn't need to follow every page).
- Lightbox now opens a full-resolution copy that fills the screen: art._download_image
caches a hi-res {id}-full copy alongside the web-large display copy, served via
/api/art/image/{id}?size=full (image_url_large in /api/art/today).
- Centered the placard bullet separators (explicit .sep spans, equal margins).
- Image no longer shifts on hover; a quiet "Click to expand" affordance sits on the art.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A calm "walk up to one piece on a quiet wall" page for the Daily Art engine. Bright,
modern, cream canvas (not a dim brown gallery) so the artwork's own colors are the pop
— the new look prototyped, scoped to /art so it doesn't touch the rest of the site yet.
- Fetches /api/art/today; large soft-shadowed frame (click -> lightbox), a museum
"placard" (serif title, artist · date, medium, "from The Met · Public Domain (CC0)",
View-at-museum link). Calm loading/empty states. Unlinked from the homepage (safe to
iterate). Ships the (Codex-cleared) art backend too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>