Commit graph

158 commits

Author SHA1 Message Date
b0e6d7e18b feat(page): /events + /events/past + AvatarPile component
/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>
2026-05-11 16:05:47 +02:00
58faeffbc2 feat(page): /members — editorial masthead
Header: MEMBERS eyebrow, serif italic 'The council.', subtitle pitched at
'an invited circle of operators' — no number, no seat count anywhere.

One row per cab user, ordered by member_number asc. Three-column grid
(60px / 1fr / 130px, 18px gap) with 0.5px bottom borders. Avatar uses the
deterministic id-pigment palette; the same id always yields the same
colour across the portal.

Body column: serif italic name; if the row belongs to the signed-in user,
a small uppercase tracked 'You' tag in terracotta. One metadata line —
title · organisation · Member since {month year} — title is dropped if
null. The pull-quote block is only rendered when pull_quote is non-null;
its 2px left border is the user's pigment at 40% opacity (50% on the
signed-in user's own row).

Right column: up to 3 focus-tag pills, vertical right-aligned, tinted in
the same pigment family (15% bg / 100% colour). Empty focus_tags renders
nothing — no placeholder pills.

Signed-in user's row gets a 4% pigment tint and rounded corners; no border
between rows in that case.

No filters, no search, no sort, no header count, no empty-seat row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 16:03:35 +02:00
243a456880 feat(pulse): nav restructure, white surfaces, membership card, dispatches
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>
2026-05-11 16:02:46 +02:00
3e9fadcf79 feat(component): RecentlyFromTheCouncil — pulls the most recent contribution
White-card surface. Header row with secondary 'Recently from the council'
eyebrow and a terracotta 'All contributions →' link. Body is one card:
22px id-pigment avatar, full name, relative time, contribution-type pill
(idea→copper, inspiration→ochre, question→indigo), then the body rendered
as a serif italic pull-quote with a 40%-opacity terracotta left border,
clamped to 3 lines via -webkit-line-clamp with markdown stripped before
trimming. Footer shows the reaction count.

Hidden entirely when the contributions table has no visible rows — no
empty-state placeholder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:58:29 +02:00
3b602a787b feat(component): DispatchesSection + reusable Avatar + tests
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>
2026-05-11 15:57:52 +02:00
302caf8896 feat(component): MembershipCard — dark indigo identity card
Replaces the CouncilMark slot on /pulse. Dark --ink surface, --ink-text
foreground, --ink-muted for labels. Top row: 22px cream circle with the
N monogram, COUNCIL · NNN (zero-padded to 3) on the right; defensive
fallback COUNCIL · MEMBER when member_number is null. First and last name
on separate lines in serif italic, split on the last space so compound
surnames hang together. MEMBER SINCE / month-year block sits above the
focus-tag pill row. Pills render only when focus_tags is non-empty —
no placeholder.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:56:17 +02:00
368ce3ac8c feat(db): dispatches + member-number allocation + focus-tags parser
db.ts:
  - User type gains pull_quote, member_number, focus_tags; all SELECT lists
    updated. getAllCabMembers (member_number asc) and countCabMembers (used
    by /pulse denominator) added.
  - createUser allocates a member_number in-transaction when role=cab.
  - updateUserRole returns { allocated: number | null } so admin can surface
    the assignment; allocation is one-way: pilot→cab→pilot→cab keeps the
    original number.
  - allocateMemberNumber: MAX(member_number)+1, idempotent, never reuses.
  - updateUserAdminFields: title / pull_quote / focus_tags (parsed array).
  - createEvent / updateEvent extended for audience, duration_label,
    action_label, notes_url.
  - Dispatch CRUD: create / update / publish (stamps published_at) /
    archive / delete. getDispatchById, getLatestPublishedDispatches,
    getAllDispatchesForAdmin, getAdjacentDispatches (prev/next in published
    order).
  - getEventAttendees(slug, status) backs the upcoming-event avatar pile.

format.ts:
  - AVATAR_PIGMENTS (terracotta/copper/walnut/indigo/heather) + pigmentForId
    (id % palette, deterministic).
  - parseFocusTags: trim, strip ASCII control chars (\x00-\x1F\x7F),
    collapse internal whitespace, dedupe, cap 3 × 24.
  - readFocusTags (safe JSON.parse for display).
  - dispatchSlug / parseDispatchSlug: {id}-{kebab(title)}; renames don't
    break links because the id leads.
  - dispatchKindLabel, stripMarkdownLight, dispatchExcerptParas (two-paragraph
    excerpt with sentence-boundary cut).

Tests: member-number allocation (idempotent, never reuses, allocates on
role transition) and focus_tags parser (control chars, whitespace collapse,
dedupe, cap). 24/24 passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:55:35 +02:00
865347f682 feat(db): migration 0004 — phase 2 schema + 4 new tokens
users: adds pull_quote, member_number (unique partial index, NULLs allowed),
focus_tags. title already exists from 0003 — not re-added. Backfills
member_number for existing cab users in COALESCE(cab_joined_date,
created_at) asc, tiebreak id asc. Lars gets #1.

events: rebuild required to widen the kind CHECK constraint (SQLite can't
ALTER it in place). Adds working_session as a new kind. Same rebuild adds
four new columns: audience, duration_label, action_label, notes_url. Data
preserved.

dispatches: new entity, status enum draft/published/archived, kind enum
decision/update/behind_the_scenes/note. published_at nullable until
publishPulse-equivalent stamps it. Indexes on (status, published_at) and
author_id.

Tokens (src/styles/tokens.css): adds --surface-card, --surface-card-border,
--ink, --ink-text, --ink-muted. Spec called these out as the only
additions; existing --radius-lg covers the spec's --border-radius-lg
reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:46:53 +02:00
7f6668f909 docs: Phase 0.5 audit — Phase 2 preflight
Confirmed before writing code:
  - latest migration: 0003_council_portal.sql
  - /pulse render order: greeting · ActivityTicker · pulse card · preview
    row (roadmap + CouncilMark) · room-row (online chip strip) · event row
  - CouncilMark exists and works (one lit dot for Lars in seed); not broken
  - ActivityTicker exists at src/components/ActivityTicker.astro and renders
    at pulse.astro:169
  - 'online now' strip lives at pulse.astro:266-290
  - vote-count denominator is already count(role='cab'); '1 of 1' on the
    smoke run was seed shape, not a bug — Phase 2 seed populates 4 CAB users
  - events.kind enum: ('dinner','office_hours','summit','virtual'). Adding
    'working_session' needs a CHECK rebuild
  - 'Office hours with the founder' → exactly one match in
    scripts/seed-demo.js:106

Divergences resolved with the user before step 2:
  1. Migration 0004 must skip users.title (already added in 0003) and only
     add pull_quote, member_number, focus_tags.
  2. Token name: spec writes --border-radius-lg; use existing --radius-lg.
  3. --secondary stays warm-walnut accent. Muted body text uses
     --on-surface-variant.
  4. Member-number backfill: order by cab_joined_date asc (NULLS via
     COALESCE to created_at), tiebreak user.id asc. Deterministic across
     re-runs and machines.
  5. focus_tags parser: trim, dedupe, strip control chars, collapse internal
     whitespace to single space, cap 3 entries × 24 chars.
  6. /events/past filters on starts_at < now() — no boolean past flag column.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:46:02 +02:00
fe27811d16 chore(demo): seed-demo.js + utc fix for last_seen_at
scripts/seed-demo.js — one open Pulse with realistic context, marks
"Traceability layer" as shipping with shipped_at -2 days and attributes
it to the cab user, two events (dinner in 5 weeks, office hours in 2
weeks), six hand-crafted activity rows mixing all 5 activity kinds so
the ticker has something to scroll on first load. Idempotent: skips if
any pulses exist. Backdates Lars's cab_joined_date so the greeting
renders "2 years, 4 months". Wired to db:setup and db:seed:demo.

Also fixes a parse bug on /pulse: SQL stores last_seen_at as
'YYYY-MM-DD HH:MM:SS' UTC, but new Date(string) parses that as local
time — on a non-UTC server the freshness check was wrong by the server's
offset. Coerce to UTC ISO before parsing. Manual smoke as Lars now shows
two member chips in "online now"; admin tabs all render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 15:04:11 +02:00
f6e7337c5e feat(admin): pulses, roadmap, events, activity tabs
Four new tabs added to /admin, matching the existing pattern exactly: ?tab=
querystring, plain HTML POST with hidden action field, redirect-with-?msg=
success path, existing .tabs / .section / .data-table / .form-grid / .input
/ .select / .btn-primary / .danger-btn classes — no new form library, no JS
beyond `confirm()`.

Pulses tab — create / edit / publish / close / delete + a results view
(?view=ID) with per-option vote counts and bar charts. Publish writes the
'pulse_opened' activity row and calls notifyPulseOpened() so members can
be notified the same way once the integration lands.

Roadmap tab — full CRUD + multi-select attribution (checkbox grid of
council + pilot users) + up/down arrow reorder within each status column
(JS-free, swaps display_order with neighbour). Status transition to
'shipping' stamps shipped_at exactly once and writes 'roadmap_shipped'
activity.

Events tab — full CRUD + an RSVP summary view (?view=ID) showing going /
interested / declined counts. Slug is required on create and readonly on
edit (it's the URL handle).

Activity tab — read-only debug table of the last 200 activity rows. Per
your call: shipping with the rest, not optional. Saves hours of "why
isn't the ticker showing X" later.

Tab views extracted to src/components/admin/{Pulses,Roadmap,Events,Activity}Tab.astro
to keep admin/index.astro navigable; the POST handlers and data loading
stay in index.astro as the single dispatch point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:57:44 +02:00
4611b687c9 feat(lib): notify() stub for pulse-opened events
Today this just logs to console. Wired up in step 10's admin pulse-publish
handler so the integration point exists from day one — only the body
changes when we pick a transactional email or Slack webhook later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:53:40 +02:00
351d90a3e8 feat(pulse): full member-portal landing at /pulse
Sections (top to bottom, all wrapped in a cascade-entry animation):
- greeting block: MONDAY · 11 MAY label, serif-italic "Good morning, X."
  greeting (server time, Europe/Copenhagen), tenure line that uses
  cab_joined_date for cab members and falls back to created_at
- ActivityTicker fed by getRecentActivity (hidden when empty)
- this-week Pulse card: live breathing dot + time-left countdown, italic
  serif question, 2×2 grid of option buttons that submit a hidden-action
  vote form. Once voted, options lock and show a distribution bar fill;
  the user's choice keeps a terracotta border. Empty state when no pulse
  is open.
- 2-col roadmap preview + council mark: three most-recently-updated items
  with status dot (copper/ochre/muted), and the user's CouncilMark md with
  the "{n} of 12 quarters" stat
- members-in-the-room: 4 chips for other CAB members seen within 5 min,
  +N overflow chip; pulsing copper dot for the "online now" indicator
- two-column event row: dark indigo "members-only" card (next dinner or
  summit) + light "office hours" card (next office_hours event). Hidden
  per-card when no matching upcoming event exists.

Vote POST uses the existing admin form pattern (hidden action field,
redirect on success). Activity row for 'voted' is written inline here;
step 11 covers the other activity sources.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:52:51 +02:00
39c9c805cd feat(component): ActivityTicker + format helpers
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>
2026-05-11 14:50:10 +02:00
6f23c47e7a feat(component): CouncilMark — generative member sigil
SVG component, sizes sm (48) / md (100) / lg (200) on a 120 viewBox.

12 quarter positions on a circle, i=0 = current quarter at 12 o'clock,
going clockwise into the past. A position is lit iff the member has a
roadmap_attribution to an item with shipped_at in that quarter. Pulse
votes do NOT light a quarter — the sigil represents consequential
contribution, not participation.

Visual layers:
- faint dotted outer ring rotating 90s linear
- small grey dots on empty quarters
- terracotta-filled polygon (15% fill) connecting lit dots, draws in
  over 1.6s on mount via stroke-dasharray
- terracotta dots on lit quarters with staggered 2.4s breathing
- serif italic initials in the centre, walnut secondary

Uses --pigment-terracotta instead of the spec's coral — no new tokens.
Honours prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:48:47 +02:00
3cb76b33c8 feat(home): role-based / redirect via homeRouteForRole
cab and fenja are sent to /pulse on landing; pilot continues to see the
existing editorial home content in place. Uses the same helper the test
suite already covers, so behaviour is locked in.

Adds a minimal /pulse stub so the redirect target resolves; step 7 replaces
it with the full member view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:47:28 +02:00
267ba34747 feat(nav): restructure header + add footer for council portal
Nav now reads Pulse · Roadmap · Members · Events · [Admin]. Old links
(Home / Vision / Product / Updates / Contribute) are dropped from the
header; Vision moves to the footer alongside a Council manifesto stub.

Header keeps the name + Sign out on the right exactly as before — Sign out
is NOT duplicated in the footer.

Footer: small Fenja icon mark + © year + two text links. Uses the existing
ghost-border, no shadows, matches editorial flatness.

/council-manifesto added as a one-screen stub so the link doesn't 404.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:46:55 +02:00
20209db2d8 test: vitest suite — pulse status, vote uniqueness, home route
Three test files covering the Phase 1 invariants:
- derivePulseStatus: draft/closed are sticky; open auto-closes by date.
- vote UNIQUE(pulse_id, user_id): castVote (OR IGNORE) keeps the first vote
  silently, a raw second INSERT raises a constraint error, and uniqueness is
  per-pulse (same user on a different pulse is fine).
- homeRouteForRole: cab/fenja → /pulse, pilot → null (render existing home).

tests/setup.ts opens BIFROST_DB_PATH=':memory:' and applies all migrations
before tests run, so the in-memory DB has the live schema. Each vitest fork
gets its own globalThis → its own fresh in-memory DB.

The homeRouteForRole helper extraction makes the / role-redirect testable
without booting Astro. Step 6 will use it from /index.astro.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:45:55 +02:00
fba369a36d chore(seed): one-time roadmap seed from content/roadmap.md
Parses the H2 sections (In progress / Next / Later) into roadmap_items rows.
Maps In-progress → beta (actively built, tested with pilots) and Next/Later
→ exploring with a target hint. Idempotent: skips entirely if the table is
already populated, so admin edits are never overwritten.

content/roadmap.md stays in the repo as the seed source. Once admin starts
editing via /admin, the DB is the source of truth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:44:09 +02:00
1735487ab9 feat(db): pulse/vote/roadmap/event/activity helpers + derivePulseStatus
Adds typed query functions for the council-portal entities. Pulse status is
stored AND derived: draft and closed are sticky, open auto-decays to closed
once now ≥ closes_at. Draft → open is an explicit admin Publish action, not
date-driven, so admins can stage a pulse without surprise auto-publishing.

Roadmap updateRoadmapItem stamps shipped_at the first time status transitions
to 'shipping' and never resets it; returns { shippedNow } so callers can fire
the roadmap_shipped activity row exactly once.

Event RSVPs reuse the existing attendance table with kind='event'; no
parallel Rsvp table. setEventRsvp upserts on UNIQUE(user_id, meeting_slug).

getLitQuarters drives the CouncilMark dot pattern from
roadmap_attributions × shipped_at — admin-curated, not derived from votes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:41:49 +02:00
56992ed4ca feat(db): configurable path, user field extensions, slug generation
- BIFROST_DB_PATH env var overrides the default bifrost.db path; lets
  vitest open ':memory:' per suite without touching prod data.
- Extend User/UserPublic with title, cab_joined_date, slug.
- Update SELECT lists for getUserPublicById and getAllUsersPublic.
- Add getUserBySlug for /members/:slug routes.
- Add slugifyName + generateUniqueSlug; createUser now auto-slugs from name
  and stamps cab_joined_date for cab-role users.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:40:24 +02:00
53cb9a7e49 feat(db): add migration 0003 for council portal schema
Adds title/cab_joined_date/slug to users, extends attendance with kind enum
and 'interested' status, and creates pulses, votes, roadmap_items,
roadmap_attributions, events, and activity tables.

Slug backfill covers the three seed users; new-user slug generation will
live in db.ts. roadmap_items has shipped_at to drive the council mark
(simpler than an audit table). roadmap_attributions is admin-curated only.

Also logs the pre-existing /api/contributions/[id]/edit 302-only bug in
KNOWN_ISSUES.md so it isn't lost; out of scope for this work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 14:39:07 +02:00
Jonathan
d054b56bf7 chore: remove participants and calendar pages and nav links 2026-04-19 20:51:55 +02:00
Jonathan
a0931ea527 refactor: home page CTA as proper button with confirmation state 2026-04-19 20:46:43 +02:00
Jonathan
bc8e08d022 refactor: home page structure and pacing 2026-04-19 20:46:06 +02:00
Jonathan
f6e6fb255d fix: hero lockup — Fenja wordmark scale and colour 2026-04-19 20:44:57 +02:00
Jonathan
6cbab5ba36 chore: add Innofounder logo to public, wire to home page 2026-04-19 20:36:54 +02:00
Jonathan
49f5d976fb docs: document decisions D-15 through D-18 2026-04-19 20:32:09 +02:00
Jonathan
2d3391f531 feat: product page with architecture and platform framing 2026-04-19 20:31:51 +02:00
Jonathan
f7bd9085de feat: vision page as manifesto 2026-04-19 20:30:57 +02:00
Jonathan
d9c75a1921 refactor: rebuild home as welcome and pitch page 2026-04-19 20:30:14 +02:00
Jonathan
4bed3a5fe0 feat: join_requests table and join CTA flow 2026-04-19 20:29:09 +02:00
Jonathan
fa5e6d8414 docs: add editorial patterns (pulled quote, stat figure, arch diagram) to style guide 2026-04-19 20:28:10 +02:00
Jonathan
26173b7396 docs: add ProjectLockup to style guide 2026-04-19 20:27:20 +02:00
Jonathan
5a7af0b0d8 feat: ProjectLockup component 2026-04-19 20:26:45 +02:00
Jonathan
f8c7152fa9 chore: remove preview page in preparation for product page 2026-04-19 20:26:31 +02:00
Jonathan
a6cad10a72 docs: HANDOVER.md — build summary and next steps 2026-04-18 22:54:48 +02:00
Jonathan
82861ca4d2 chore: typecheck and build clean 2026-04-18 22:54:25 +02:00
Jonathan
7f02600c05 feat: admin panel 2026-04-18 22:52:29 +02:00
Jonathan
99f3052651 feat: participants directory and account page 2026-04-18 22:51:30 +02:00
Jonathan
636ef129bb feat: product preview page 2026-04-18 22:50:42 +02:00
Jonathan
40aed88525 feat: contribute feed, reactions, and edit flow 2026-04-18 22:50:11 +02:00
Jonathan
caab3ab187 feat: calendar 2026-04-18 22:48:27 +02:00
Jonathan
edc0cfdb0f feat: roadmap 2026-04-18 22:47:38 +02:00
Jonathan
d300e4a76e feat: updates 2026-04-18 22:47:13 +02:00
Jonathan
76c7dfa985 feat: vision page 2026-04-18 22:45:56 +02:00
Jonathan
9de5602d2d feat: authentication and invite flow 2026-04-18 22:45:25 +02:00
Jonathan
0dc2dbd849 feat: database schema, migrations, and seed data 2026-04-18 22:43:16 +02:00
Jonathan
2c2446ba4b chore: add white logo variants to public/ 2026-04-18 16:59:02 +02:00
Jonathan
389c50eb67 chore: update logos to correct white variants 2026-04-18 16:57:56 +02:00