Commit graph

5 commits

Author SHA1 Message Date
4aaf0957dd fix: nine test-note follow-ups
1. Markdown preview in the admin edit panel now re-renders from the
   textarea's current value on every toggle (dynamic-imports marked on
   the client). Previously the panel showed the server-rendered seed
   value forever, so new dispatches always previewed empty.

2. Pulse sub-form drops the opens_at field (opens on dispatch publish
   automatically) and changes closes_at to a date input — the chosen
   day is treated as end-of-day in the DB.

3. /dispatches/[slug] reading width widened 50% (720 → 1080px).

4. Roadmap display_order cascades on insert / update / delete:
   inserting at N bumps N..end up by 1, deleting N pulls N+1..end
   down by 1, moving from A to B shifts the intermediate range by 1
   in the appropriate direction. Order stays dense — no gaps, no
   collisions. All three transitions run in a transaction.

5. /roadmap always anchors at scrollLeft=0 on mount so the first
   milestone aligns with the content-column left edge. Previously
   the page jumped to the last-shipping milestone, which felt random
   once items past the viewport landed.

6. Events admin list shows the actual date (fmtDateTime) instead of
   "in 3 days" — easier to scan when planning across months.

7. duration_label is auto-computed from starts_at + ends_at on save
   (minutes < 90, hours < 4, "Half day", "Full day", "N days").
   The manual field is gone from the admin form; the column on the
   member-facing event pages keeps reading the stored value as before.

8. Pulse hero still skips office hours per the existing logic — no
   change. Confirmed via the test note's clarification.

9. Pulse "also coming up" strip relabeled to Previous + Upcoming.
   Previous = most recent past non-office-hours event. Upcoming =
   next non-office-hours event after the hero. Each card now carries
   a small terracotta eyebrow with the label.

Typecheck clean, build clean, 147/147 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:46:06 +02:00
5ddaad3da3 feat(vote): show percentages after voting + allow change-of-mind
The pulse vote widget on /pulse and on /dispatches/[slug] now behaves
the way the v5 spec asks:

- Eyebrow shortens: 'This week's pulse' → 'This week's'.
- Status line copy changes shape depending on whether the viewer has
  voted yet. Pre-vote: 'Vote to see how the council weighs in · Closes
  TUESDAY' — sets expectations that the percentages reveal after a
  vote. Post-vote: '2 of 7 voted · Closes TUESDAY · Click to change' —
  tells the viewer they can change their mind.
- Each option now renders a right-aligned tabular-nums percentage badge,
  but only after the viewer has voted. Pre-vote there's no percentage
  on screen at all — voting is a commitment, not a peek.
- Options stay clickable after voting (no `disabled`). Re-clicking a
  different option changes the vote.

DB: new helper castOrChangeVote(pulseId, userId, optionIndex) does an
UPSERT — INSERT on first vote, UPDATE option_index + voted_at on
subsequent. Returns true if this was the brand-new vote, so the caller
can write the 'voted' activity row exactly once and not double-count
changes-of-mind in the feed. castVote(...) (INSERT OR IGNORE) stays in
db.ts for callers that explicitly want first-vote-wins semantics.

Status-class rename: .locked → .closed on both pulse-option and
inline-poll-option. The class now reflects what it actually represents
(the pulse is closed) rather than the false invariant 'the user has
voted and can't change'.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 11:20:16 +02:00
9800d0a448 feat(pulse): two-box Fenja+poll, prominent hero, single-bg council, more air
Layout (per the v4 follow-up spec):

1b. Latest from Fenja is now a two-box layout when there's an attached
    poll: article on the left (wider), poll widget on the right. Without
    a poll, the article box takes the full row. Both boxes are surfaced
    on --surface-card with the same generous padding so they read as
    sibling pieces.

1c. Featured excerpt is extended to ~720 chars (was ~520) via a wider
    threshold on dispatchLongPreview. Below the article+poll row, the
    next two most-recent published dispatches render as minimalist rows
    — just title + kind + relative time, separated by ghost borders.

2.  Hero event: date column is now 150px wide (was 110px); grid uses
    align-items: center so the date+detail columns are vertically aligned
    rather than top-stuck. Day number scaled up to 3.5rem (was 2.75).
    Outer card padding bumped from --space-7 to --space-10. Hero title
    bumped to 2rem.

3.  More air: page-level section gap --space-10 → --space-12. Each
    on-page card has been re-padded; outer page horizontal padding goes
    down to --space-16 from --space-20 to match the narrower canvas.

6.  Council members no longer have individual card chrome. One outer
    --surface-card wraps the whole grid; each member cell is just an
    avatar + name + title + company stack with no background or border.
    Cells use a larger 6/8 grid gap so they don't crowd each other.

Inline poll widget on /dispatches/[slug]: when a dispatch has an
attached pulse, the article body is followed by a compact poll card
matching the /pulse-side widget. Vote POST handled inline; the page
re-renders with the locked + result-bar state.

scripts/seed-demo.js: the existing 'Which milestone should we anchor Q3
around?' pulse now attaches to the decision dispatch ('We are
deprioritising public-cloud parity for Q3') via pulse_id. Other
dispatches stay poll-free.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 10:19:00 +02:00
66c3f6492f style: drop eyebrows + body italics across editorial pages
Applies the v3 rules globally: italics reserved for the Bifrost wordmark
and section-links; uppercase tracked eyebrows removed from page heads
and content sections.

Pages updated:
- /members: drops the 'MEMBERS' eyebrow + italic on h1 + italic on member
  name + italic on pull-quote.
- /events: drops the head eyebrow + 'Next up · Members only' /
  'Invitation by hand' hero eyebrows + 'Also coming up' / 'Past
  gatherings' sub-section eyebrows + italics on hero day, hero title,
  also-row titles, past-card titles. The past-gatherings header-row
  'View all →' link migrates to the bottom of the section as a
  section-link.
- /events/past: drops the eyebrow + italic h1; back-link uses
  .section-link.
- /dispatches/: drops the 'DISPATCHES' eyebrow + italic h1 + dispatch-row
  title italic + date column italic.
- /dispatches/[slug]: drops italic on the article title + h2/h3 inside
  rendered markdown + blockquote italic + adjacent prev/next title
  italic. Back-link migrates to .section-link.
- /roadmap: drops the 'ROADMAP' eyebrow and the .lead class on the
  subtitle.

Orphaned eyebrow class rules left in place; harmless and the next
visual pass can sweep them with the rest of the unused CSS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 09:52:29 +02:00
1bf1993040 feat(page): /dispatches index + /dispatches/[slug] detail
/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>
2026-05-11 16:07:13 +02:00