Events card (--ink):
- 'Next up · Members only' and 'Invitation by hand' eyebrows removed.
- All ink-card text uses cream tones (rgba(232,224,208,...) at 92/75/70/65%)
instead of the warm tan --ink-muted; the previous low-contrast labels
read 'dark' on the indigo and now read uniformly light.
- Italic font removed everywhere on the card (hero day number, hero title,
coming-up titles, etc.) — italic is reserved for the Bifrost wordmark
and section-links only.
- Past gatherings dropped from /pulse entirely; the listing lives on
/events and /events/past.
- 'Also coming up' is now a grid of small bundled sub-cards inside the
blue surface (auto-fit minmax 220px). Each card shows date + title +
meta only — no RSVP action, no per-row submit form.
- 'See all events →' section-link replaces the old past-gatherings
'View all →' as the sole bottom-of-block link to /events.
Latest from Fenja (unboxed):
- Card surface dropped. Article sits on the cream page background.
- Excerpt extended via new dispatchLongPreview(d, 520) helper —
sentence-boundary cut at ~520 chars (was ~200). Title in serif regular,
not italic.
- 'Read the full dispatch →' section-link at the bottom.
Roadmap (horizontal):
- Three roadmap items become a 3-column grid of small white cards instead
of a vertical list. Each card has status dot + title + status blurb
with consistent min-height.
- 'See the full roadmap →' section-link at the bottom.
Council members (larger cards):
- Was a flowing pill row, now an auto-fit grid (minmax 260px) of larger
white cards. Each card has a 56px avatar + name + title + company,
with generous padding for whitespace. Company name is the new field.
- 'See who our council is made up of →' section-link at the bottom.
General (eyebrows + italics): all uppercase tracked eyebrow labels gone
from /pulse — date label, 'Latest from Fenja', 'From the roadmap', 'The
council', etc. Italic body text removed throughout — greeting, titles,
member names, dispatch title, roadmap titles. The Bifrost wordmark in the
header and the .section-link utility class are the only remaining italics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/dispatches: editorial header (DISPATCHES · 'Notes from the studio.' ·
'Decisions, half-built ideas, and things we've changed our mind about.')
+ a vertical list of every published dispatch. Each row is a single link
target with a three-column grid: 180px byline (avatar + author name + title
or role label) / 1fr body (serif italic title + kind pill, then a single-
paragraph excerpt) / 130px date column. 0.5px bottom borders, hover tint.
/dispatches/[slug]: 720px single-column read view. Header is kind pill +
publish date, serif italic title at 2rem, author byline with 32px avatar.
Body uses the existing renderMd() (marked) with serif italic h2s, copper
blockquotes, mono code blocks. Footer is a 0.5px divider then two adj-card
links (prev / next in published order) on opposite ends — the missing side
renders an empty grid slot so layout is preserved.
Canonical-slug redirect: if /dispatches/12-old-title is hit but the title
has since changed, the page issues a 302 to /dispatches/12-new-title. id
is the authority, kebab title is for readability.
format.ts: adds roleLabel (pilot/cab/fenja → 'Pilot' / 'Council' /
'Fenja team') for the byline.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
/events:
- Header: EVENTS · 'Where the council gathers.' · one-line subtitle
- Hero invitation card on --ink for the soonest non-office_hours event:
NEXT UP · MEMBERS ONLY / INVITATION BY HAND eyebrow strip, two-column
date/detail body separated by a 0.5px vertical line, foot strip with
'{capacity} seats · {confirmed} confirmed' + AvatarPile of confirmed
attendees and the RSVP CTA. The RSVP button toggles between cream-on-ink
'Save your seat →' and outlined 'You're confirmed ✓ Change'. Empty-state
card retains the visual weight when no upcoming non-office_hours event.
- ALSO COMING UP — every other upcoming event including office_hours.
Three-column rows; the right column uses event.action_label or falls back
to defaultActionLabel(kind). Studio hours surfaces with 'Book a slot →'.
- PAST GATHERINGS — two-column grid. Each card has a 56px thumb: photo_url
if set, else a copper-tinted notes square when notes_url is present, else
a deterministic two-pigment gradient block. View all → links to /events/past.
/events/past — same card component, full list of starts_at < now() events.
No boolean past flag column; filter is purely date-based.
AvatarPile (src/components/AvatarPile.astro) — reusable. Overlapping circle
slots with a 1.5px border in a caller-provided colour (defaults to surface,
the hero card overrides to --ink so circles read on dark). Stacks z-index
so leftmost is on top; +N overflow chip at the end.
format.ts: adds eventKindLabel (office_hours → 'Studio hours') and
defaultActionLabel per kind.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Nav (AppLayout.astro): brand stays left; everything else is a single
right-aligned flex group — Pulse · Roadmap · Members · Events · [Admin
if fenja] · 0.5px vertical divider · name · Sign out. The .nav-user
wrapper is gone; the name and logout button now belong to the same flex
flow as the link list.
/pulse:
- ActivityTicker render removed. The component file gets a one-line
deprecation comment; the activity table and write hooks stay in place
for later use.
- 'X others online now' chip strip removed — including all its supporting
helpers and styles in the page.
- CouncilMark replaced with <MembershipCard> in the right column of the
preview row. The roadmap preview is now a white --surface-card with
0.5px border; pulse-card switches to the same white surface and
--radius-lg. The .chosen pulse option uses --pigment-terracotta border
and a 6% terracotta tint via color-mix.
- <DispatchesSection limit={4} /> and <RecentlyFromTheCouncil /> stacked
below the preview row, in the position the online-now strip vacated.
- Vote-count denominator pulls from countCabMembers() and renders via
voteCountSentence(votes, total) — a new helper covering 0/1/5+ cases.
- Event row: dark dinner card now uses --ink/--ink-text; light card uses
--surface-card with 0.5px border.
Tests: 3 new cases for voteCountSentence (0/1/5). 36/36 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DispatchesSection (white-card, used on /pulse):
- Header row: LATEST FROM THE STUDIO + All dispatches → link
- Featured: 26px avatar byline (name · title · relative time + kind pill),
serif italic title, two-paragraph excerpt (lead in body tone, trail in
--secondary with ellipsis) cut on the nearest sentence boundary, then
the terracotta uppercase 'Read the full dispatch →' link
- Earlier: 1px divider + EARLIER label + up to 3 rows with 22px avatar,
serif italic title (single-line ellipsis), relative time
- Hidden entirely when zero published dispatches; divider + earlier list
omitted when exactly one published dispatch exists
Avatar component (src/components/Avatar.astro) — pure presentational,
takes id + name + size, paints a deterministic-pigment circle with serif
italic initials. Reused by DispatchesSection now and by /members, /events,
and RecentlyFromTheCouncil in the next commits.
format.ts: adds dispatchKindPigment (decision→terracotta, update→indigo,
behind_the_scenes→ochre, note→heather) for pill backgrounds.
Tests: 9 cases covering create-as-draft vs published, publishDispatch
idempotency (re-publish preserves published_at), archive preserves
published_at, the published feed excludes drafts/archived, adjacent
prev/next, and slug round-trip.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ActivityTicker — pure presentational. Renders a horizontal strip of items
that scrolls right→left over 38s, with CSS mask fades on both edges and
pause-on-hover. Items are duplicated so translateX(-50%) wraps seamlessly.
Hidden entirely when empty (no "no activity" placeholder).
format.ts — small set of formatters used by the ticker and the upcoming
/pulse greeting:
- relativeTime (2m / 3h / 5d / just now)
- redactName ("Maya Rasmussen" → "Maya R.")
- tenureSince (e.g. "2 years, 4 months")
- pulseDateLabel ("MONDAY · 11 MAY", Europe/Copenhagen)
- timeOfDay (morning/afternoon/evening, Europe/Copenhagen)
- tickerItem (ActivityRow + subject label → display struct, with role-dot
colours mapped to existing pigments: copper for pilot, terracotta for
cab, indigo for fenja)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>