ActsControl room

Send a tester a link — it opens on their phone like an app. Everything they do shows up here and grows the content.

Build doc · STATUS.md

Build log — STATUS

The running session log: everything shipped, newest first.

ACTS — build status / resume doc

Last updated 2026-08-17, pre-Steve-call (Bryan's NIGHT LIST of 9 — all shipped). Read this first to resume.

2026-08-17 — demo data + refresh lever + visit door

  • /refresh — one URL purges every cached page site-wide (revalidatePath from the root layout). Hit it after any data change.
  • Steve-demo data curated: 14 nonsense missions purged (jb;hbhjb etc., children first: proofs/comments/story cards); 10 HEARTFELT requests seeded (Rosa's surgery · Danny 28 days sober · Sister Agnes at St. Mary's · Grace's biopsy · the Alvarez family · Elena & Tom · Marcus 17 · Grandpa Joe at St. Columba · Nurse Kemi · the valley floods) with live praying counts (9–63) + a word of encouragement each. 31 missions total, all real.
  • "Acts at a place" on the hamburger → /visit door (redirects to the freshest place-anchored request's location loop).

2026-08-17 — STEVE'S LANGUAGE RULE: you ask for an ACT

  • Steve (reviewing, liked it): "Ask for an ACT — correct; ask for a prayer — incorrect." Swept en masse across every version: feed door ("Do you need an act?"), menu, cards view, /get, /p ("Someone asked for an act"), AMEN ask, MARKET door ("Ask for an act — a prayer, a candle, a visit"), Ask wizard title + posted screen ("Your ask is posted."), roles, site meta description. OFFER side (Bryan's follow-up): decision points say "Offer an Act" (feed card CTA, /m CTA, immersive rail, pray-flow entry "You are offering an Act"); once prayer IS the chosen act it says prayer ("Amen — Offer this Prayer", the /p one-tap, "Say the prayer here"). Zero residual "ask for prayer" matches; production build verified locally.

2026-08-17 — EXPERIENCE MODES (pre-meeting): you stay where you entered

  • Bryan on phone: entered HYBRID, a click later he was in the classic-brown triptych. Fix: acts_mode cookie (ModeMarker set by MarketHome/AmenShell: hybrid · market · amen) — ① shared flow pages adopt the matching skin (hybrid/market → modern, amen → acts) via the skin store; ② "/app" bounces back to the experience's OWN home (meta-refresh redirect; ?skin= links escape + reset to classic); ③ the market stalls (/market/ask · offer · you) wear .hybrid-scope when the mode is hybrid. Landing embed pinned to /app?skin=acts so the hub phone never gets hijacked by the cookie.
  • AskWizard's "Connect to your parish and earn a saint card" is now a LINK to /start/parish (Bryan).

2026-08-17 — THE NIGHT LIST (9 items for the Steve call, all live)

  1. /design opens with VERSION CARDS — 5 across: icon tile · name · 4 color swatches · identifying phrase · the metaphor at the foot (7 versions).
  2. $PRAY — THE MEME COIN OF MERCY, /app/coin (Bryan: a blockchain meme coin): gold coin + ticker + $PRAY/USD price (▲% since genesis), up-only sparkline, your bag / holders / market cap, tokenomics ("every act is a block", "no rug possible — the ledger is mercy"), testnet contract mock 0xPRAY…mercy. Price is deterministic from world acts; 100 pts mint 1 $PRAY. The real chain arrives with the wrapper. Linked from ladder + menu.
  3. CLIENT MODE — /c/steve IS the hub, filtered (v2 after Bryan: "same as my page, just fewer tabs"): the link sets an acts_client cookie and opens the real hub — identical pages, but the TabBar shows ONLY the tabs checked in Admin ("Steve's page" section, plain-form Save → activity_event latest-wins). Admin/Users/Paths etc. hidden for him (Admin is never even offerable). Header reads "Prepared for Steve" + exit; /c/exit leaves the mode. In client mode HOME leads the nav, before the Versions group. Verified in-browser both ways. New clients = new URL, zero code.
  4. CARDS ARE EARNED AT PLACES: completeAtPlace grants the place's saint (deterministic per mission) and the done screen reveals it ("This place gave you the {name} card →"). Widget milestone → Carlo; parish → Joseph.
  5. INSTALL THE APP → THE JESUS CARD: new "jesus" card leads SAINT_DECK; get_app claim grants it; MilestoneCta reveals "The Jesus Christ card is yours — pray to it →". Art: public/saints/jesus.jpg when dropped.
  6. CELEBRATION v3 — JESUS RECEIVING LIGHT: the award celebration is now an image of Jesus gaining light in waves until totally lit (~3.6s, halo bloom, then fades). Same Confetti contract → all award surfaces upgraded at once. NOTE: uses the shrine stand-in photo until public/saints/jesus.jpg exists (one drop fixes the celebration AND the card).
  7. ARTIST EDITIONS: 8 top cards carry artist credits (fictional ateliers — swap for real commissions) — ✦ chips on the deck, credit line on /card.
  8. THE EARN LADDER opens /app/you: pray +10 · app +50 · widget +50 · account +100 · share +100 · post a request +25 (NEW — createMission now pays ASK_POINTS) · do an act +25–250; done-states struck through.
  9. THE WORLD — /app/world + hub tab + menu: world events as shared intentions (world audience layer), one tap to pray with the world, and the add-a-world-event form (+25, lands in every feed). Form click-path could not be browser-tested (the preview pane's renderer died — code path is the same createMission used everywhere); SSR + feed rendering verified.
  • QA: all new + core routes 200 (/c/newclient falls back to defaults); typecheck clean throughout; each item its own commit.

2026-08-16 — demo-day batch (Bryan shows Steve tomorrow)

  • Confetti v2 = a centered EXPLOSION, longer (Bryan): pieces detonate from mid-screen on their own angle, arc out, then gravity + fade, ~3–4s (was top-rain ~2s). confetti-boom keyframes with per-piece --tx/--ty/--fall; two-phase easing (blast → gravity).
  • Confetti now fires on EVERY points-gaining activity. Newly wired: PrayButton (+10 tap) · StrippedPray (/p and /free one-tap) · CompleteProof · MilestoneCta claims · VisitFlow CHECK-IN (celebrate-per-step state so mere navigation never replays) · PrayFlow invite milestone (+100 on share) · helper-offer joins (new JoinOfferButton client component replaces the server <form action> in OfferCard + MarketHome — "Sent ✓" + burst). Funnel steps that navigate away on award (signup/parish/location) don't burst — nothing to see mid-redirect; their next page shows the ledger.
  • FREE PRAY — the app-free social page + hub tab (first in Versions): /free is the evergreen link (redirects to the freshest open request's /p one-tap page — no install, no login, works in in-app browsers). Tab explains the funnel + live embed + share row.
  • Map strip pills fixed for embeds (Bryan's screenshot): chips were mid-wrapping ("1,101⏎praying"). Now whitespace-nowrap + tighter metrics + "6 at places" copy — whole pills only, never broken text.
  • QA sweep for demo day: all 46 static routes + /p /m /pray /visit /card/[slug] with live ids → 200; bad ids → 404; typecheck + console clean; /free tap-to-pray verified end-to-end with the new explosion.

2026-08-15 — donate prompt made prominent

  • DonateLink (bottom of /m request pages) upgraded from the quiet gray strip to a tinted brand-bordered card: filled heart badge · "Give to keep Acts free" bold + "every gift keeps prayers free for everyone" · "Give →". Still quieter than the pray/app CTAs; recolors per skin. Verified on /m (TikTok skin, mobile).

2026-08-15 — "Get link" share row under every hub screen

  • components/ShareVersion.tsx sits under EVERY phone embed (baked into PhoneEmbed): Get link (clipboard + execCommand fallback) · Text (sms:) · Email (mailto:) · native Share… where supported. The body reads "Try this — {version name}: {url}" — Steve sends it, the recipient opens it on their phone and it just works (guest-first web app).
  • Gotchas fixed while building: navigator/window checks in render caused an SSR 500 + hydration mismatch → origin + canShare resolve in useEffect (SSR falls back to the prod origin for the link bodies). New mail icon in the set (the typed /design meanings record caught it).

2026-08-15 — hub embeds fixed: one PhoneEmbed, true-width viewport

  • Bryan: "versions all have scaling issues with buttons and what not." Root cause: the 375px-wide iframes lose ~15px to the desktop scrollbar gutter INSIDE the frame, so the app laid out at ~360px — chips wrapped ("1,100 praying", "the vigil"), the map info strip collided, buttons crowded.
  • components/PhoneEmbed.tsx is now THE phone frame (411×852 inner viewport = iPhone width + gutter slack, scaled 0.73 to the 300px card): replaced the five drifting copies (landing, triptych, amen, market, hybrid reviews). Verified: chips single-line, map strip fits, all three Triptych panels clean.

2026-08-15 — TRIPTYCH tab + Expo named in next steps

  • /triptych-review — "Triptych" leads the Versions nav group: the MAIN app's three skins side by side like altarpiece panels (Classic · Apple · TikTok), each a live phone embed of /app?skin=… with a one-line character note. The name = the clever word for "one scene, three panels".
  • Landing next-steps step ① now names Expo as the wrapper; step ② Astro clarified as the builder the original prototype was made in.

2026-08-15 — hub: Steve's landing page + the Design system page

  • "/" is now WHERE STEVE LANDS (Users moved to /users): left column = the product in the simplest terms (Ask/Act/See it/Keep going); right column = the latest build LIVE in a phone frame (iframe of /app) with "Open the latest build →" + the share URL under it. Below: Latest build (renders STATUS.md's newest section via lib/md.ts + "Full build log →"), Tech specs (stack, data, hosting, skins, guests-first, what's mocked), and Next steps (① add the native wrapper ② get into Astro ③ TestFlight).
  • /design — the Design system page, first section ICONS: all 37 glyphs from components/Icon.tsx, each on its own card (SVG · name · intended meaning), wrapping rows; MEANING is a typed Record<IconName,string> so a new icon without a meaning fails the build. TabBar: + Home, Users → /users, + Design system.

2026-08-15 — hub cleanup: Versions group + the Docs reader

  • TabBar regrouped (Bryan): "Versions" section (Amen · Market · Hybrid · Widgets) FIRST, a pipe divider, then Users/Paths/Lists/Images/Saints/ Gamification/Docs/Admin.
  • /docs — one readable content model for every document: left rail groups "Bryan's docs" (Phase 1.2 lists · prototype estimate · audit framework) and "the build's docs" (STATUS · DESIGN · SCHEMA · IMAGE-GUIDE); ?doc=key picks, whitelisted server-side. Markdown renders via lib/md.ts — a tiny renderer (headings/lists/bold/italics/code/links/blockquote/hr; buffers wrapped bullets so bold spanning lines still formats; .doc-prose styles in globals). All 7 docs verified rendering with zero raw markdown leaking.

2026-08-15 — ICON design pass (Bryan: same type of icons, clear to all ages)

  • components/Icon.tsx is THE icon system: ~35 hand-tuned stroke glyphs (24px grid, stroke 2, round caps, currentColor → recolors per skin). Platform emoji are GONE from UI chrome — every button, nav, chip, tile and menu row draws from this one set. Add icons there, never inline.
  • pray = Bryan's praying hands (the Noun Project logo mark, hands only, filled) — after the stroke attempt "did not read as hands". Used everywhere: Offer Prayer CTAs, praying counts, navs, map markers.
  • Data now carries icon names, not emoji: OFFER_ACTS/PLACE_ACTS/MILESTONES (scoring), MOMENTS, PRAYER_MODES/ROSARY_MYSTERIES (prayers), BADGE_ART, ACT_IMAGES, SKIN_VOCAB.rankIcon. BigCta icon="candle" resolves names (falls back to raw text). Leaflet markers = inline SVG strings (MARK_SVG in ActsMap; sized via .acts-map-emoji svg in globals).
  • Swept: feed (cards/header/map/menu/home-nav), Ask wizard, pray flow + act hub, candle/rosary/visit loops, moments, donate, parish, you/map/cards pages, AMEN (nav/rail/vigil/ask), MARKET (doors/feed/nav/stalls), badges (medal seal + editor). Kept: content emoji (iOS mock home screen), trailing ✨ in reward copy.
  • MARKET wording: "market/marketplace" was the CONCEPT name, not app copy (Bryan; he likes "Home") — tagline now "give help · receive help", nav tab "Home", "Post my offer", "Back home", "How it works". Routes + Steve tab keep the name. Hamburger added to the market chrome (MarketHome header + MarketTopBar → MenuSheet with an ink prop for non-feed scopes); HYBRID inherits it.
  • Verified in-browser (mobile, all three skins + amen + market + hybrid): feed, menu sheet, prayer choice, act hub, check-in loop. Console clean.

2026-08-14 — confetti when you win points

  • components/Confetti.tsx — zero-dep CSS confetti burst (32 pieces, token colors so it recolors per skin — violet on TikTok, gold/vermillion on Acts). Mounting it = the trigger; it rains once (~1.3–2.2s per piece) and fades. Keyframes + prefers-reduced-motion (hidden) live in globals.css (.confetti-piece, confetti-fall).
  • Fires on every points win: PrayFlow act hub (prayer offered) + Offering sent · VisitFlow act logged + COMPLETE at place · MomentJoin joined · AmenVigil joined · AmenApp card Amen. Verified in-browser on /moment/evening (mobile, TikTok skin) — burst plays over the joined screen, console clean.

2026-08-13 — the ACT LOOPS session (Bryan's spec, all verified in-browser)

  • PRAY flow rebuilt as a loop (components/PrayFlow.tsx): offer → encouragement → choose your prayer (💗 Heartfelt · ✝️ Lord's Prayer · 🕊️ Saint's Prayer — lib/prayers.ts) → full-screen prayer mode per choice → ACT HUB: add candle/rosary/fasting/novena, each pays via addDevotionAct (repeatable, running total, ✓ done-states) → Complete → share screen; first share claims the new invite milestone (+100) (MILESTONES.invite).
  • Process flows: CandleFlow.tsx (prepare → tap-to-light CSS candle .candle-* in globals.css, flicker + glow keyframes, optional dedication → comment) · RosaryFlow.tsx (mystery set by weekday from lib/prayers.ts → 5 decade screens w/ .bead progress). Both reused inside the location loop.
  • LOCATION LOOP (components/VisitFlow.tsx rebuilt): plan screen ("You've chosen an act at {place}" + 🔔 mock arrival reminder; ?here=1 skips) → map + "Arrived at {place}" strip → Check in (+25, checkInAtPlace) → Actions at {place} (PLACE_ACTS in scoring.ts: candle 50 · prayer 25 · rosary 150 · Mass 200 · adoration 250 · tend 100) — each act has its OWN screen (candle/rosary = full flows; pray/Mass/adoration/tend = per-act guidance) → "Make it real" log (photo = 2× base + 1,000, logPlaceAct) → share moment → next action loop → COMPLETE at {place} (completeAtPlace +20 + story card). /m entry: "I'm here" (→?here=1) + "Going later? reminder" link.
  • Gathering wall on /m (Bryan: each request is where acts+comments group up): "N acts completed • M comments" strip + all proof photos/notes + comments in chrono order (getMissionWall in lib/data.ts; Comments composeOnly keeps the input, wall shows the list).
  • Saint-card SHOP /app/deck: "You have N cards", buy any card with 500 pts or $0.99 (mock)buySaintCard (points path checks + deducts via awardPoints negative; owned = card:<slug> counters; cash = mock, no charge). Entry: /card/[slug] "You have N — get another →" + /app/you deck link. BuyCardButtons.tsx (two-tap confirm on $0.99).
  • Scoring canon docdocs/PHASE-1.2-LISTS.md (Bryan's full Phase 1.2 lists paste) + memory scoring-canon.md. lib/recognition.ts gained generic awardPoints(actor, pts, counters) (badges included; offer acts now use it).
  • Feed nav tightened: filter row = All/Parish/World/Live short labels, justify-between, snug .feed-scope .feed-filter — fits 375px, no side-scroll.
  • CITY_COORDS + pilgrimage cities (Vatican City, Lourdes, Fátima, Mexico City, Knock, Assisi) so seeded place missions map correctly.
  • Verified end-to-end on the dev server (mobile 375px + desktop): pray loop paid +210 (10+50+150) + 100 invite; location loop +170 (25+125+20); card bought with points deducted 500. ShareRow extracted (components/ShareRow.tsx).
  • Menu everywhere: MenuSheet added to /app/you · parish · deck · donate · role doors (active states; menu "Saint cards" now → /app/deck).
  • /saints curation at review size: each card renders LARGE (aspect 3/4, 3-up grid) with name overlay; the image itself is the upload target ("↑ Click to upload correct art") + remove-upload; Bryan/Steve drop the real card art there (or public/saints/<slug>.jpg still wins by convention).
  • Feed + visit restructure (Bryan's batch): ① visit actions list shows 3 + "See all N acts" → bottom-sheet MODAL (requests stay in view); requested cards now PROMINENT (.card, serif name, ⛪ parish tag, 🙏 praying count + "asks: {act}", gradient "Serve them →"), ordered place → parish → most popular. ② Feed: "Do you need a prayer?" small box at top → seeker; parish connect = prominent CARD below the map (connected: name + Edit + "Open your parish room" BigCta; else "Connect with your local congregation" + Find-your-parish BigCta); "Going on a pilgrimage?" card → offer wizard. FeedHeader's old parish line removed. ③ AskWizard: "Where should it happen? 🌍 Anywhere / 📍 At a location" chooser (place field only when at-location; anywhere clears place_label).
  • Location loop = a true multi-person session: the header (📍 place + accumulated points chip) is STICKY over the scrolling acts; selecting from the "Requested while you're here" feed no longer navigates — it switches the ACTIVE person in place (scrolls up under the pinned location, actions title reads "Serving {who}", per-person act done-keys, log targets their mission). One check-in, many people served, one COMPLETE.
  • Medals in ALL versions + requested-acts feed at locations: the devotional medal set now renders on /amen/you (Honors, 3-up) and /market/you (Honors, both-sides page) with real earned state + uploaded-art overrides — every app version shares one badge system. The location loop's actions screen now carries "Requested while you're here 📥": up to 6 asked acts (person · asked act · text · +25 chip) so a helper picks up new needs without leaving the church; taps jump into that person's loop at the place.
  • Badges v2 — the SAINTS aesthetic (Bryan: v1 read as 90s clipart): medals are now devotional photographs in double GOLD bezels on parchment (--holy-gold/--holy-parchment), sepia + candlelight vignette, glyph seal at the foot, display-italic serif names, ✦ gold captions — the holy-card language worn as a medal. Default photo per badge from photoCategory in lib/badges.ts; the medal itself is the click-to-upload target on /gamification (hover "↑ upload art", reset to default).
  • Badge art is UPLOADABLE: each medal on /gamification has "↑ art" (upload → uploadImagesaveBadgeArt; latest-wins badge_art rows in the activity log — the saint-card pattern) + "reset" back to the glyph. Uploaded art renders clipped inside the medal disc (rosette frame stays, so the set keeps reading as a family); /app/you honors overrides via getBadgeArtOverrides. Verified upload→render→reset live.
  • Badge MEDALLIONS (components/BadgeMedal.tsx + lib/badges.ts + --medal-* palette): the 11 badges are now a cohesive SVG medallion set — scalloped rosette seal, jewel tone + glyph each, metal ring, glass shine, ribbon tails; earned = color, locked = ghosted 🔒. Front and center at the TOP of /gamification ("The badge set") and on /app/you (3-up medal grid, real earned state). Old text-row badge lists removed.
  • Logo trial: PrayerMark (Bryan's praying-hands SVG, Noun Project — license before production) swapped into the Wordmark lockup, currentColor + breathing glow; one-line revert to FlameMark.
  • 2026-08-14 sweep: ① Feed polish — FeedCard = ONE card (who + intention + CTA inside; feed-list hairlines → space-y-5 wherever FeedCard renders), crosshair locate icon (not a paper airplane), .btn-big refined (centered icon+text group, layered shadows + inset highlight, tighter metrics so "Offer Prayer" never wraps beside the 💬 tile). ② HYBRID (/hybrid + hub tab): THE MARKET's exact components re-skinned modern via .hybrid-scope (--mkt-* remap: cool paper, blue need / green help, sans voice) — proof the concept re-skins without a rewrite. ③ Art direction panels on Amen/Market/Hybrid review tabs: upload per category (hands/candle/church/shrine/pilgrimage/portrait) → app_image; uploaded pools OVERRIDE stock Unsplash on concept surfaces via getPhotoPools+pickFromPool (verified end-to-end: uploaded church art appeared on /hybrid need cards; hide = setImageHidden). Saints tab already had its own upload; /images = the general gallery.
  • THE MARKET is a WHOLE app: /market (sticky collapsing doors, one zipped need/offer feed, four-stall nav Market·Ask·Offer·You) · /market/ask (seeker stall → real createMission → share) · /market/offer (the signature stall: place pills from the formal catalog + act chips compose the sentence "I'm headed to [PLACE] — who wants [ACT]?" → real createOffer; verified live end-to-end) · /market/you (both sides of you: given=green Grace/level/cards, asked=vermillion your needs gathering, via getMyAsks). Shell = components/MarketShell.tsx. Hub tab embeds all four live.
  • THE MARKET — the two-sided 30–45 concept (/market + hub tab "Market"); replaced DAYLIGHT same-day (Bryan: "I hate it… angry fruit salad… missing the 2-sided marketplace"). Discipline: paper + ink + photography + exactly TWO colors (needs = deep vermillion --mkt-need, helpers = deep green --mkt-help). Front page IS the market: "I need help / I can help" doors, pulse chips, Helpers on the road (real helper_offer rows, one-tap joinOfferAction join — verified count 1→2) above Open needs (photo cards → pray/visit loops), how-the-market-works both sides. Directions now: Classic · AMEN (young/vigil) · THE MARKET (30–45/two-sided).
  • AMEN is now a WHOLE app + hub tab "Amen" (/amen-review, live iframe embeds): /amen stack · /amen/ask (one-screen ask → real createMission → "watch the Amens gather" + share) · /amen/vigil (moments in the dialect, real joinMoment) · /amen/you (flame/Grace/level/card shelf, real data). Shared chrome components/AmenShell.tsx (AmenHeader vigil bar + AmenNav three verbs). Everything hits the real backend.
  • AMEN — the next-gen concept (/amen, linked from /paths): Bryan's "coolest new designer" brief — younger grammar + parent-proof reverence, instantly legible to every Christian. Full-bleed vertical prayer stack, ONE word as the CTA (Amen = the universal liturgical act, +10 via the real prayForMission), side rail 🕯️📿⤴ into the real act hub/invite, vigil bar (live praying count + moment chip), three-verb nav (Pray·Ask·You), candlelit --amen-* tokens (never pure black), design-notes screen at the stack's end (brand: keep lowercase acts + "say amen."; rename candidate if ever wanted: EMBER). A new face on the same body — all real data/actions.
  • LIVE MOMENTS — the synchronous act (Bryan: group live events, full in all feeds + a join path): lib/moments.ts = code-defined daily world moments (Morning Offering 8:00 · Noon Prayer 12:00 · Evening Peace 20:00, 20-min live windows, never empty). joinMoment action = tap points ×streak; joins are activity_event "moment_join" rows, count-per-day = the live counter (getMomentJoinCount). MomentCard (ticking countdown → LIVE pulse) pinned on /app, /app/cards, /app/parish; /moment/[key] = the gathering screen (countdown/LIVE, avatar stack, join +10, "you prayed together" + share). Pastor-created + parish-scoped moments and push reminders = next phase; the widget's top ladder rung now has something true to show.
  • Hub "Widgets" tab (/widgets): 9 iOS home-widget INCENTIVE mockups for Steve — streak flame · pray-at-noon · Today in the Church (the daily feast/season content lives HERE now, not the feed) · Grace & level · next saint card · needs near you · novena companion · answer-one-ask · invite. WidgetKit sizes (158² / 338×158), candlelit via .feed-scope tokens; each with "what it pays / when it fires" notes. Real widget = native Swift later. /widgets/add = the iOS add-a-widget PROCESS in 5 phone-frame steps (touch & hold → + → gallery → Add Widget → on the home screen) + the metaphor note: widget = vigil candle, the +50 retention moment.
  • /app/donate rebuilt in the feed dialect (components/DonateFlow.tsx): serif hero, amount pills → Give BigCta, sponsor-a-pilgrim pledge cards (10¢/act · $1/pilgrimage-day), thank-you state. Still a mockup — no charge.

2026-08-12 sweep — old screens → new design (DONE)

  • Ask wizard rebuilt (components/AskWizard.tsx, feed-scoped at /app/seeker): big Prayer intention card + Candle/Church/Cross/Rosary grid (Cross/Rosary map to acts over the prayer image — DB enum unchanged), NAME field w/ floating label (.field-float in globals.css; resting label plays the placeholder), relationship pills, progress bar, BigCta nav, PrayFlow-style celebration.
  • /m/[missionId] = the feed card opened: avatar-ring who-row, serif intention card, OfferPrayerCta (new client comp — BigCta → /pray/[id] with skin vocab) + BigCta "Get the app". One-tap PrayButton replaced by the PRAY flow.
  • /app/you + /app/parish + /manage feed-scoped; parish lists now render FeedCard (not MissionCard); saint-card gold hex → var(--color-brand).
  • /paths: feed hero + Cards/Immersive as 2-up thumbs. Demo content: scripts/seed-demo-missions.mjs (idempotent; Sarah's Mother 24🙏 · parish storm candle 12 · world peace 57). capture-previews.py sets acts_city/acts_parish cookies → captures aren't zero-state. All 9 re-captured.
  • View toggle cleanup: /app/cards feed-scoped (FeedCard + missionCopy, underlined sort tabs, parish chip from cookie); ImmersivePanel now uses OfferPrayerCta into the PRAY flow + t-hero (killed a text-[2rem]). lib/mission-copy.ts = display copy for every feed-card surface; zero counts read "Be the first to offer this ✨". .feed-list = hairline between feed items (feed/cards/parish). Social share row (Messages/WhatsApp/FB/X/Copy) on the PRAY "sent" screen.
  • Hamburger menu (components/MenuSheet.tsx): the Figma ≡ on feed/cards/map/immersive; right-side sheet with all destinations; HomeViewSwitch deleted. Overlay zIndex 1200 (beats Leaflet panes).
  • Flame logo everywhere: LogoMark → FlameMark (currentColor); ⚠️ app/icon.svg SHADOWS public/icon.svg at /icon.svg (Next metadata convention) — both now carry the flame-on-brown app icon.
  • /paths funnel strip: "The path in — tap any step" — 6 numbered, clickable step thumbs per skin (capture-previews.py shoots them; mission id resolved from the live feed).
  • ⚠️ Vercel gotchas: root tsconfig must exclude native/ (react-native types absent on Vercel → 3 failed builds); when a push doesn't trigger a deploy, npx vercel --prod --force from repo root.
  • TestFlight Option A wrapper BUILT (native/): Expo SDK 57 WebView shell around the live app — native share bridge, external-link handoff, flame icon + candlelit splash (scripts/gen-assets.py), lib/theme.ts token source, com.actsventures.acts, eas.json. expo-doctor 20/20. 🔴 NEXT: Bryan runs native/RUNBOOK-testflight.md (Apple Developer enroll → eas login → build → submit → add Steve as internal tester). Web deploys need NO new build; Option B native screens replace the shell in the same TestFlight app later.
  • The loop closes both ways (gap sweep 1-4): ① Asker loop — /app/you "Your asks" (getMyAsks: counts + words back) + markAnswered action → status=completed + story card. ② Novena in OFFER_ACTS (+100, ACT-NVN ×9 → Novena Companion) + "Pray at noon" join-at-time card in parish room. ③ scripts/seed-local-parishes.mjs — 4 parishes + 5 local place rows, missions back-linked by label. ④ ~~ChurchDayCard/liturgical~~ REMOVED 2026-08-13 (Bryan: not asked for, didn't land) — restore from git if a feast-day surface ever comes back.
  • Ask = the Figma design (acts playground → "Prayer Request Designs" frames, node 18:1280 + Make v64): header w/ brand mark + ACTS·SEEKER + progress dots, left serif title, glowing PRAYER card ("Select an intention to ask for"), 2×2 devotion grid (emoji-beside-label), ✍️ inline-icon name field, short pills (Mom/Dad/BFF/Community), ⛪ parish toggle (sets audience_layer=parish; Self-only → me), sticky Next → bar.
  • Photo avatars: components/Avatar.tsx + portrait category in lib/photos.ts — every profile circle (feed header, feed cards, /m, parish stacks) is a deterministic portrait photo in the ring color; same seed (name) → same face everywhere.
  • Place-based act path: /visit/[missionId] (components/VisitFlow.tsx) — "We see you're at {place} — check off the visit?" → add photo (+1,000) + word back → completeMission scores it + story card. Entry: "📍 I'm here" on /m's Where card + /app/map rows. GPS itself is native-app phase; web starts the flow from the place's screens.
  • Final port: OfferWizard rebuilt like AskWizard (feed-scope, progress bar, BigCta, serif celebration; WizardShell now unused); /app/map, supporter + advocate doors feed-scoped (advocate cards use missionCopy; StoryCard FB hex → btn-pray). The whole /app/* surface is now the new design — no Phase-1 screens left.

2026-08-12 late session — design system · PRAY flow · real map

  • Design system LOCKED: scripts/guard.mjs precommit ratchet (no NEW text-[Npx], tracking-[…], raw hex; --audit = 107 hex baseline to burn down; escape: git commit --no-verify). Contract merged into CLAUDE.md. ONE button system: .btn 48pt / .btn-big 72pt (title+reward subline) / .btn-icon 48×48, icons 22–24px, pills 44pt. Feed geometry now matches the Figma guides (points t-title, map 220pt, 40pt item gaps, Classic = underlined text tabs via .feed-filter).
  • REAL MAP: components/ActsMap.tsx — Leaflet + free CARTO tiles, NO token ($0; swap TILE_URL + token for Mapbox later). Themed per skin (Classic = dark_all + sepia acts-map-candlelit, Apple = light_all, Night = dark_all). Emoji divIcon markers; CITY_COORDS for the 4 location-step cities; fadeAnimation: false REQUIRED (tiles stall at opacity 0 under React double-mount). Feed card 220pt static; /app/map interactive 420pt.
  • PRAY flow (Bryan's Figma guides): FeedCard CTA → /pray/[id]components/PrayFlow.tsx state machine: offer summary + add an act (candle +50 / fasting +100 / rosary +150, OFFER_ACTS → badge counters via awardOfferAct) → encouragement note (suggestion pills → comment row) → choose saint (deck cards, lib/saint.ts now has prayer text) → prayer mode (full-screen saint card) → completeOffer action (tap points + act bonus + comment + saint in activity log) → offering sent → share → return to feed. /p/[id] invite landing stays one-tap (funnel top).
  • BigCta = THE button (cf1f497): LARGE icon left, two stacked rows right (title / reward), 64pt. Never hand-roll a big CTA — use the component.

🔴 NEXT SESSION — new-design sweep (Bryan: "all the Ask etc screens are old")

New design (feed-scope + BigCta) covers ONLY: /app feed · /pray/[id] · /p/[id] · /start/* · /get. Everything else is still the old Phase-1 look. Sweep order:

  1. Ask wizard (/app/seeker) → Bryan's Apple guide: big intention card (Prayer primary) + Candle/Church/Cross/Rosary grid, NAME field w/ floating label, relationship pills, progress bar. Feed-scope it.
  2. Act detail /m/[id] → feed-card look + BigCta.
  3. /app/you + parish room/manage → feed-scope + BigCta.
  4. /paths thumbnails (Bryan asked, unfinished): seed 2-3 rich demo missions ("Sarah's Mother — healing…", parish storms candle) via service-role script, set acts_city/acts_parish cookies in scripts/capture-previews.py so captures aren't zero-state, layout = feed hero + cards/immersive as 2-up thumbs, then re-capture.

Where it lives

  • Live: https://acts-ten.vercel.app · Deploy = git push origin main (Vercel auto-deploys ~1–2 min).
  • Repo: github.com/AutoMaticDelight/acts · Supabase project in .env.local (gitignored; also set in Vercel).
  • Local dev: npm run dev (or preview server acts-dev on port 3200). Never needs Docker.
  • Verify a deploy: poll a route/marker, e.g. curl -s -o /dev/null -w "%{http_code}" https://acts-ten.vercel.app/<route>.

Stack

Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · Supabase (Postgres + Storage + RLS).

The product path (Bryan's framing — important)

Web prototype = top of funnel: a shared web link opens on the phone, you pray in one tap, *then* it invites you to download the native app (built separately) and add the widget. The native app is its own phase (Expo / React Native + EAS → TestFlight → App Store). The web work is the design + backend spec the native build reuses. See Expo notes in chat / plan below.

The path (2026-08-12) — every step gamified

Invite → member funnel, in order (Bryan's spec, matched to the Figma feed): share link → /p/[id] stripped one-tap pray → /get?flow=1 app (+50) → widget (+50) → /start/signup (+100) → /start/parish (+150) → /start/location (+50) → /app feed.

  • Milestones: lib/scoring.ts MILESTONES + awardMilestone (idempotent — claimed state = recognition_score.category_counts["milestone:<key>"], no schema change). Actions: claimMilestone/setLocation/connectParish (city/parish = cookies acts_city/acts_parish). Profile /app/you shows the Journey ledger + parish card.
  • /app = the Figma feed (view one): header (avatar · points · rank chip · parish teaser · ✨Ask) + dotted map card (city chip, locate → /start/location) + filter pills (All/My Parish/World Events/Live) + FeedCards with one big btn-big btn-pray CTA showing the reward on its face. Older views kept: /app/cards (grid) + /app/immersive; switcher = feed/cards/immersive (map absorbed into feed).
  • .feed-scope (globals.css) remaps tokens per skin like .admin-scope: acts=Classic candlelit gold · modern=Apple light blue · night=TikTok black/violet. Per-skin vocab SKIN_VOCAB (Grace/Devout 🕊️ · pts/MVP 🏆 · XP/Warrior 🛡️) via useSkinVocab() from SkinScope context. Figma source: "acts playground" file.
  • Invites now share /p/[id] (ShareInvite on /m); /m "Keep it with you" → /get?flow=1.
  • Hub /paths — the three design systems side by side, all three views as large images (real captures in public/previews/, re-capture with python3 scripts/capture-previews.py against the dev server; ?shot=1 hides the skin switcher) + a Send-link per path (opens the funnel in that skin).
  • Parish manager /app/parish/manage — pastor side of the parish room: live stats, broadcast composer (postBroadcast → broadcast table, need pills not dropdowns), QR + Send-link parishioner invite. Linked from /app/parish ("Manage").

Built & live (routes)

  • App (skinnable, /app/*): /app Home (Cards) · /app/immersive (18–25 full-screen) · /app/map (stub+list) · /app/[role] (seeker=AskWizard, doer feed, supporter=story cards, advocate=QR broadcast) · /app/offer (OfferWizard → significant places) · /app/parish (room + Join) · /app/donate (mockup) · /app/you (guest→member ladder).
  • Shared: /m/[id] Act Detail (photo, geographic+Directions, Share/Invite, completion loop trust-led, comments, subtle donate, ✦ prayer-card link) · /s/[slug] Story Card · /card/[id] Saint card · /get App Store listing mockup + widget mockup.
  • Control room (/): Users · Lists · Images (gathering guide) · Saints (approve/edit/upload) · Admin (moderation).

Gamification (2026-08-11)

  • Engine: lib/scoring.ts (bonuses, levels, badge rules, SAINT_CARD_EVERY=5) + lib/recognition.ts (service-role recorder: streaks, counters, auto-badges, 10-min anti-farm). Every pray/join/complete/photo-upload is scored via recordAct.
  • Saint cards = the REWARD, not per-prayer artifacts. A collectible deck (lib/saint.ts SAINT_DECK), next card earned every 5 completions, collection on /app/you, card page /card/[slug]. Steve curates the deck on /saints.
  • Control room /gamification: levels, streaks/bonuses, saint-card rule + deck, badges, editable per-item points list (updates act.default_points live), top helpers.
  • Admin = Apple style (.admin-scope in globals.css): SF system font, #f5f5f7, white cards, Apple blue/green/red. The app keeps the Acts brand.
  • LogoMark (components/LogoMark.tsx) renders /public/icon.svg — swap that one file to change the logo everywhere. Feed is THE center — keep Home chrome slim.

Key decisions (don't relitigate)

  • Ask = ONE act (OR selector); actors add any. The seeker's chosen act is a featured suggestion, not a constraint — the PRAY act hub highlights it ("what {who} asked for ✨", sorts first via offerActForActCode) and any helper can stack any devotion. At a location the loop is open too: any act at the place, then the person-loop — "while you're at {place}" offers other people to serve from the same spot (same place → same city → any, each with the +25 check-in chip), on the logged and complete screens.
  • Brand: Cormorant Garamond (italic for feeling) + Instrument Sans + #ec612b vermillion. Logo = vermillion candle disc (app/icon.svg, favicon/PWA). Ring colors: me/vermillion · another/terracotta · parish/green · world/blue.
  • 8pt grid + named type scale (.t-hero/title/headline/body/sub/caption). Pills, not dropdowns (PillSelect/.pill).
  • Completion: ~~four candidates side-by-side on /m~~ RETIRED 2026-08-13 — the act loops (pray hub · location loop) ARE completion now; trust stays the philosophy (no tracking), CompletionMethods component kept but unmounted.
  • Photography: lib/photos.ts = curated Unsplash interim (License-free, no key), deterministic per item. Swap for owned photos later; shot list in docs/IMAGE-GUIDE.md.
  • Saint cards: patron saint matched to the intention (lib/saint.ts); Steve curates on /saints; curation persists in the activity_event log (no schema change) — public /card/[id] renders curated data.
  • Guest→member: prototype only — a name, no credentials (never collect passwords / create real accounts).
  • Design contract: DESIGN.md (rules + full screen spec). Never regress to the generic AI look.

Data

19 tables, all seeded. Seed utilities (run cd ~/acts && node --env-file=.env.local scripts/<x>.mjs): seed-all.mjs (audit + backfill all tables) · seed-offers.mjs · seed-place-needs.mjs. Migrations in supabase/. Note: proof counting is paginated past the 1000-row cap (lib/data.ts proofCounts).

Next / not done

  • Wire the /get "download the app" invites into the "Keep it with you" sections (Act Detail, /m, wizard posted states).
  • Advocate broadcast wizard (pick a need → QR + link).
  • Expo native app — scaffold create-expo-app + Expo Router + NativeWind + supabase-js; port screens reusing the Supabase backend + taxonomy + tokens. Widget = native Swift/WidgetKit (sized L). EAS build/submit needs Bryan's Apple Developer account (his step; I supply the eas commands).
  • Out of scope (mockups): real payments, real SMS, real widget install, real App Store submission.