DEMO · datos ficticios

Faith Groups · App redesign — Developer handoff

Clickable prototype for Faith Layer Software · Yelevo LLC. DEMO · datos ficticios (fictional “Iglesia Esperanza” data).

  • Prototype: index.html — one self-contained file (inline CSS + JS, no build step, no dependencies except the Inter web font). Open it on a phone for the real mobile experience. On a desktop (≥ 1024px) the Leader role renders inside a phone frame with a side panel, while the Administración role renders as a full desktop dashboard (see “Desktop admin”). A “Vista móvil” button shows the phone version of the admin.
  • Language: Spanish is the default, English is a toggle on every screen (button in the demo bar). All strings are inline T('español','English') pairs — see the copy table.
  • Everything is fictional. Data lives in memory + localStorage (key faith-groups-proto-v1). No network calls, no real maps, no real people. A “DEMO · datos ficticios” bar is always visible; remove it for production.
  • Deep links for review: index.html#leader/report, #leader/review, #leader/success, #leader/home, #leader/group, #admin/overview, #admin/leaders, #admin/admins, #admin/map, #admin/notices, #admin/reports, #admin/print, #login.
  • Code layout: section comments in the CSS (tokens → base → screens/nav → components → screen-specific → animations → print) and in the JS (config/i18n → data → helpers → router → one block per screen → actions → boot). Every screen is registered as SCREENS['name'] = { tab, hash, render(), mount() }. Every interactive element declares data-act="…", handled in one ACTIONS map.

1. Design tokens

All tokens are CSS custom properties in the :root block of index.html. Use them by name; never hard-code values.

Brand colors

Same palette as the new faithgroups.app landing page. Navy = text/headers, teal = primary action and brand accent.

TokenValue
--navy#05223a
--teal#008d9b
--teal-d#007883
--muted#546675
--line#e2e9ed
--bg#fafcfd
--soft#f0f6f8
--white#fff
--teal-50#e8f5f7
--teal-100#cfe9ed

Semantic colors

ok = success/reported, warn = pending/attention (never alone: always paired with text or icon), danger = errors.

TokenValue
--ok#1c8456
--ok-bg#e6f5ed
--warn#9a5b00
--warn-bg#fff3de
--warn-line#f1d29b
--danger#b83227
--danger-bg#fdecea
--blue#2a6ab0
--overlayrgba(5,34,58,.42)
--stage-bg#e9f0f3

Typography

Inter 400/500/600/700/800. Body 15.5px/1.45, large titles 30px/800 with −0.025em tracking, display numbers 44px/800.

TokenValue
--fontInter,ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif
--fs-2xs11px
--fs-xs12px
--fs-sm13.5px
--fs-md15.5px
--fs-lg17px
--fs-xl20px
--fs-2xl24px
--fs-3xl30px
--fs-display44px
--fw-regular400
--fw-medium500
--fw-semi600
--fw-bold700
--fw-xbold800
--lh-tight1.15
--lh-base1.45

Spacing (4px base)

Use the scale only; screen side padding is --pad (20px).

TokenValue
--sp-14px
--sp-28px
--sp-312px
--sp-416px
--sp-520px
--sp-624px
--sp-732px
--sp-848px
--pad20px

Radii

Cards 20px, inputs/rows 14px, buttons and chips fully round, bottom sheet 28px top corners.

TokenValue
--r-sm10px
--r-md14px
--r-lg20px
--r-xl28px
--r-pill999px

Shadows

Soft, navy-tinted. Teal glow only on the primary button.

TokenValue
--shadow-sm0 1px 2px rgba(5,34,58,.05)
--shadow-md0 1px 2px rgba(5,34,58,.04),0 10px 28px -12px rgba(5,34,58,.16)
--shadow-lg0 2px 4px rgba(5,34,58,.05),0 24px 48px -16px rgba(5,34,58,.28)
--shadow-sheet0 -12px 40px -8px rgba(5,34,58,.28)
--shadow-teal0 10px 22px -10px rgba(0,141,155,.7)

Sizing

Minimum tap target 48px. Primary button 52px. Tab bar 64px + safe area.

TokenValue
--tap48px
--btn-h52px
--input-h52px
--tabbar-h64px
--demobar-h38px
--statusbar-h0px

Motion

Durations and easings used by every animation (see Animation specs).

TokenValue
--ease-ioscubic-bezier(.32,.72,0,1)
--ease-outcubic-bezier(.22,1,.36,1)
--ease-springcubic-bezier(.34,1.56,.64,1)
--dur-press120ms
--dur-fast180ms
--dur-base280ms
--dur-screen380ms
--dur-slow560ms
--stagger55ms

Desktop admin layout (>=1024px; --d-side-w and --d-pad grow at >=1280px)

Sidebar 232px (256px from 1280), top bar 64px, content padding 24px (32px from 1280) capped at 1320px, report drawer 480px, modal 540px. Colors, type, radii and shadows are the same tokens as mobile.

TokenValue
--d-side-w232px
--d-top-h64px
--d-pad24px
--d-max1320px
--d-drawer-w480px
--d-modal-w540px
--d-row-h58px

Safe areas

TokenValue
--safe-topenv(safe-area-inset-top,0px)
--safe-bottomenv(safe-area-inset-bottom,0px)

Typography scale

RoleSize / weightUsed for
Display44 / 800, −0.03emBig numbers (metrics, totals)
Title L30 / 800, −0.025emScreen titles
Title M24 / 800Wizard step titles, counters
Title S20 / 700Sheet titles
Body L17 / 600Section titles
Body15.5 / 400–600Default
Small13.5 / 500Helper text, labels
Caption12 / 600Notes, badges
Overline11 / 700, +0.09em, uppercaseEyebrows

2. Components

Button

.btn .btn-primary | .btn-secondary | .btn-dark | .btn-ghost | .btn-danger | .btn-wa | .btn-sm | .block

Pill button, 52px high (40px .btn-sm). Primary = teal with glow, secondary = white with border, dark = navy, WhatsApp = soft green (used only for share/remind actions).

StateBehavior
DefaultResting style per variant.
Hover (desktop)Primary darkens to --teal-d.
Pressedtransform: scale(.965) in 120ms + 8ms haptic vibration where supported.
Focus-visible3px teal outline, 2px offset.
Disabled45% opacity, no pointer events, no shadow.
Loading.is-loading: label hidden, 22px spinner; used by Save report and Send notice (800ms / 650ms simulated).

Card

.card .card--hero .card--alert .card.tap .metric

White surface, 1px --line, radius 20px, --shadow-md. Hero = navy gradient for the weekly status. Alert = amber surface for “groups without a report”. .tap cards are fully clickable.

StateBehavior
Default
Pressed (.tap)scale(.985)
AlertAmber background + border; always has an icon + text.

Input / Select / Textarea

.field > label + .input | .select | .textarea (+ .err)

52px high fields, 14px radius, label above (13.5px/600). Numeric fields use inputmode; dates/times use native pickers.

StateBehavior
Default1.5px --line border.
FocusTeal border + 4px teal halo (14% alpha).
Read-onlySoft grey background (group name in step 1).
Error.field.has-error: red border/halo and .err text under the field; field shakes via haptic 20ms.

Chip

.chip (.is-on, .dashed)

42px pill. Used for quick-adding attendees, assigning helpers, group filters and recipient selection. Toggle chips use aria-pressed.

StateBehavior
OffWhite, 1.5px border.
OnTeal-50 background, teal border/text, check icon slides in (width 0→16px) and chip pops (280ms spring).
DashedAction chip “Otra persona” (opens Add person sheet).

Segmented control

.seg (--n, --i) / .seg.sm

Sliding white thumb driven by CSS variables --n (segments) and --i (active index). Used for login method, Leaders/Administrators, and attendee category.

StateBehavior
ActiveNavy label, white thumb.
TransitionThumb translates 280ms --ease-ios.

Counter stepper

.counter

Large − / + buttons (48px) around a 24px value. Used for kids, Sunday attendance and fasting count.

StateBehavior
Default
PressedButton scales .88 and tints teal-100.
At minimum− button disabled (35% opacity).
Value changeValue ‘bumps’ (scale 1→1.28→1, 280ms spring).

Switch row

.switch-row[role=switch][aria-checked]

iOS-style 52×32 switch with label and optional hint. Whole row is the tap target.

StateBehavior
OffGrey track.
OnTeal track, knob slides 20px with spring easing.

Wizard progress

.progress > i (.done, .now)

Five segments (6px tall) above the step title; role=progressbar. Review step shows all five complete.

StateBehavior
PendingGrey.
CurrentHalf-filled, gentle pulse.
DoneFills left→right in 560ms.

Person card

.person

One attendee: initial avatar, name, remove (✕, not for the leader), category segmented control (Discípulo / Visita / Visita nueva) and a ‘Lección’ pill to mark who taught (single selection).

StateBehavior
NewFades up on insert.
Teacher onNavy pill, aria-pressed=true.

Bottom sheet

#sheet-root .sheet

Modal sheet with drag handle, title, close button. One open at a time; content can be swapped in place (Add leader → Invitation → WhatsApp preview). Max height 88%.

StateBehavior
OpeningBackdrop fades in 280ms; sheet slides up 380ms --ease-ios.
DismissTap backdrop, ✕, Esc, or drag the handle down > 110px.
Content swapBody fades up 280ms.

Tab bar

#tabbar .tab (.is-on, .cta)

Leader: Inicio · Reporte (primary action) · Avisos · Mi grupo. Admin: Resumen · Líderes · Mapa · Avisos · Reportes. Hidden on pushed screens (wizard, detail, print).

StateBehavior
ActiveTeal label, teal-50 pill behind icon, icon pops (560ms spring).
BadgeAmber dot on Avisos when a leader has an unread notice.
Hide/showSlides down 380ms when a pushed screen opens.
Re-tap active tabScrolls the screen to top.

Toast

.toast (.success .error .info)

Floats under the demo bar, auto-dismisses after 2.8s, role=status live region.

StateBehavior
SuccessNavy with mint check.
ErrorRed background.
InfoNavy with cyan bell.

Badge

.badge (.ok .warn .teal .danger)

26px status pill: Reportó (ok), Pendiente (warn), Inactivo (neutral), Líder (teal).

List row

.row (.is-missing .is-inactive)

64px+ row with avatar, two text lines (ellipsis) and trailing content. Missing-report rows get an amber surface.

StateBehavior
Pressedscale(.985)

Empty state

.empty

96px illustration, title, one line of help. Used for no leaders, no matches, no notices, no reports for the filter.

Skeleton

.skel

Shimmering 68px placeholder rows shown ~520ms on the Reports list to demonstrate the loading state.

3. Animation specs

Durations: press 120ms · fast 180ms · base 280ms · screen 380ms · slow 560ms. Easings: --ease-ios (.32,.72,0,1), --ease-out (.22,1,.36,1), --ease-spring (.34,1.56,.64,1). All motion respects prefers-reduced-motion.

AnimationTriggerDurationEasingWhat movesReduced motion
Desktop · sidebar highlightClick a sidebar item560mscubic-bezier(.32,.72,0,1)Teal-50 pill slides vertically to the active item (--i)Instant
Desktop · content enterSidebar navigation, sign-in560ms + 35ms per table row / 55ms per cardease-outCards and table rows fade up in sequence; KPI counters count up; chart bars grow and the line drawsNo movement; final values shown
Desktop · drawerOpen / close report detail560mscubic-bezier(.32,.72,0,1)Panel translateX(100%→0), backdrop opacity 0→1Instant
Desktop · modalAny sheet opened on desktop560ms spring / 280ms fadecubic-bezier(.34,1.56,.64,1)Panel translateY(16px) scale(.96) → 1, backdrop fadesInstant
Desktop · week labelWeek selector ◀ ▶280msease-outLabel fades up; KPIs, chart and table re-animateNo movement
Screen pushOpen report detail, wizard, print preview380mscubic-bezier(.32,.72,0,1)New screen translateX(100%→0); previous screen translateX(0→−24%) + brightness .9Instant
Screen backBack arrow380mssameReverse of pushInstant
Tab / root changeTab bar tap, login → home, role switch280mscubic-bezier(.22,1,.36,1)opacity 0→1 + translateY(12px→0); old screen removed on finishInstant
List staggerAny .stagger list on screen entry560ms each, +55ms per item (--i)ease-outopacity + translateY(14px→0)No delay, no movement
CountersMetrics on home, overview, review, success900ms (350ms for live total)easeOutCubicNumber counts up from 0 (or previous value)Final number shown at once
Wizard step slideNext / Previous / Edit380mscubic-bezier(.32,.72,0,1)Body slides in from the right (forward) or left (back) + fadeInstant
Progress segmentsStep change560mscubic-bezier(.32,.72,0,1)scaleX 0→1 on each segment; current segment pulsesInstant
Chip toggleTap an attendee/helper chip280mscubic-bezier(.34,1.56,.64,1)Pop 0.92→1.05→1; check icon width 0→16pxInstant
Counter bump+ / − on steppers, total changes280msspringValue scale 1→1.28→1Instant
Bottom sheetopenSheet / closeSheet380ms (backdrop 280ms)cubic-bezier(.32,.72,0,1)translateY(105%→0); drag handle follows fingerInstant
Toasttoast()380ms in / 280ms outspring / iostranslateY(−18px→0) + scale .96→1Instant
Success checkReport saved700ms ring draw + 450ms tick (delay 650ms)ease-in-outSVG stroke-dashoffset draw; green disc scales in (spring); ring rippleFinal state shown
Confetti-liteReport saved1.5s, 30 pieces, staggered 0.25–0.5sease-outPieces burst outward with rotation and fade; CSS variables --x --y --r --d per pieceHidden (no movement)
Map pins dropMap screen enters700ms, +110ms per pinspringtranslateY(−70px) + scale .7 → bounce → rest; pin shadow stays on the groundInstant
Map zoom+ / − buttons380mscubic-bezier(.32,.72,0,1)Canvas scales (1 → 1.7 → 2.4) around the selected pin; pins counter-scale to stay constant sizeInstant
ChartOverview enters / week changesBars 560ms (+70ms each), line 1.1s draw, dots fadeios / ease-outBars scaleY from baseline; polyline stroke-dashoffset; dots fade inFinal state
Face ID scanFace ID tab → Entrar con Face ID1.5s scan + 650ms confirmease-in-outScan line sweeps, corner brackets, icon pulses, then turns green with a check~100ms
PIN dots / errorKeypadDot pop 280ms; shake 450ms on wrong PINspring / linearDots fill teal; error shakes and turns red, then clearsInstant
Button pressAny button, chip, tab, key, row120msease-outscale .965 (buttons) / .88 (keypad, steppers) + 8ms vibrateKept (tiny, non-vestibular)
Pulse ring‘Nuevo reporte’ CTA when report pending2.2s loopease-outExpanding ring around the CTADisabled
Paper inPrint preview500msease-outSheet rises + scales .97→1Instant

4. Screens

Login (Acceso) #login

Purpose
Let leaders and administrators sign in on their phone and save the app to the home screen.
Contents
Brand, church name, three methods in a segmented control: Código (access code field with show/hide), PIN (6-dot keypad), Face ID (scan animation). “Guardar en mi teléfono” opens iPhone/Android install steps. Demo bar lets reviewers pick which role they enter as.
Interactions
Valid code (111115 Elena, 000000 admin, others in the demo) → signs in as that user. PIN 123456 → signs in as the selected demo role. Face ID → scans then signs in.
States
Error: wrong code → red field + “Código de acceso inválido.” + shake; wrong PIN → dots turn red, shake, toast “PIN incorrecto o bloqueado…”. Loading/success: Face ID scan → green check → home. Empty: n/a.

Leader · Inicio #leader/home

Purpose
At a glance: has my group reported this week, and a one-tap way to start the report.
Contents
Greeting (“Hola, Elena”), hero status card for the current week, optional new-notice banner, two metrics (last meeting, 4-week average), meeting place card with mini map, last 3 reports.
Interactions
“Nuevo reporte” → wizard. Hero becomes “¡Reporte enviado!” with total and “Ver reporte” once submitted. Banner → Avisos. “Editar” → Mi grupo. Report row → detail.
States
Pending (CTA with pulse ring) / Sent (total + view). Empty reports: “Todavía no hay reportes guardados.” Already reported: tapping Reporte shows info toast “Ya se envió un reporte de tu célula para esta fecha.”

Leader · Reporte (wizard, 5 pasos + revisión) #leader/report

Purpose
File the weekly meeting report in about two minutes, one focused step per screen.
Contents
1 Reunión: group (read-only), date, start/end, lesson, offering. 2 Asistencia: roster chips to tap who attended, “Otra persona” sheet, per-person category (Discípulo/Visita/Visita nueva) and Lección toggle, kids stepper, live total (also in the Next button). 3 Cierre de la reunión: vision / mission / announcements switches + notes. 4 Planeación de la próxima reunión: welcome & prayer, worship, closing, childcare, refreshments — type or tap a name. 5 Motivación y crecimiento: absent chips, who calls them, Sunday count, who can bring a guest, people to invite, prayer, fasting count + names. Revisar y guardar: summary by section with Edit links.
Interactions
Next/Previous slide steps; step 1 validates date. ✕ with unsaved changes opens “¿Salir sin guardar?” sheet. “Llenar ejemplo” (demo helper only) fills a realistic report (total 14 for Amistad → week total 69). Save → success screen.
States
Validation errors: missing date; no attendees and no kids → toast “Agrega al menos una persona o un niño.” and jumps to step 2; duplicate week → toast “Ya se envió un reporte de tu célula…”. Loading: Save button spinner 800ms. Success: see next screen. Empty roster: “Todos asistieron” in absent list.

Leader · Reporte enviado #leader/success

Purpose
Reward and confirm the submission.
Contents
Animated checkmark, light confetti, “¡Reporte enviado!”, summary (attendees, new guests, children counting up), Volver al inicio / Ver reporte.
Interactions
Home returns to Inicio with the sent state; Ver reporte opens the detail.
States
Success only. Offline/failed-send handling is not prototyped (see Open questions).

Leader · Mi grupo #leader/group

Purpose
Let the leader keep the meeting place current and review their own history.
Contents
Group header, meeting address field, mini map (tap to move the pin), coordinates, “Usar mi ubicación”, “Guardar lugar”, privacy note, list of the leader’s reports, Cerrar sesión.
Interactions
Tap map → pin drops there. Save validates the address.
States
Error: empty address → “Escribe la dirección de reunión.” Success: toast “Lugar de reunión guardado.”

Avisos (leader and admin) #leader/notices · #admin/notices

Purpose
Admin sends messages to all active leaders or a selection; leaders read them.
Contents
Admin: composer (asunto, mensaje, “Todos los líderes activos” switch or recipient chips), Enviar aviso, Compartir por WhatsApp (preview sheet), sent list with recipient count. Leader: received list; unread dot on tab.
Interactions
Send validates and shows spinner → toast “Aviso enviado a N líder(es).” WhatsApp opens a preview bubble sheet (simulated).
States
Empty: “Todavía no hay avisos.” Errors: empty subject/message, no recipients (“Selecciona destinatarios.”). Success: toast + list updates.

Admin · Resumen #admin/overview

Purpose
See how the week is going and who still needs a nudge.
Contents
Week selector (◀ ▶, last 6 weeks), four metrics (Grupos que reportaron, Grupos sin reporte, Asistencia total, Visitas nuevas), amber alert card listing groups without a report with “Recordar por WhatsApp”, attendance trend chart (labeled Datos ficticios), group list with progress meters and status badges, Agregar líder.
Interactions
Changing week re-animates counters, chart and list. Pending group row or alert button opens the reminder sheet (WhatsApp preview, simulated). Reported row opens the report.
States
All reported: green “¡Todos los grupos reportaron!” and the chart footnote is hidden. Partial: amber alert, chart bar flagged , and the footnote “ Semana incompleta: faltan grupos por reportar.” is shown (only when a group is missing). Reset “Practicar desde cero”: all five groups pending.

Admin · Líderes #admin/leaders

Purpose
Find leaders, invite new ones privately, manage access; manage administrators.
Contents
Segmented Líderes / Administradores. Search by name or group (accent-insensitive). Rows open a manage sheet: Editar nombre, Restablecer código, Desactivar, Archivar (with confirmations). “+” opens Agregar líder → creates access code → Invitación privada (code, message) → Compartir por WhatsApp (preview) / Copiar mensaje. Administrators tab invites a new admin the same way.
Interactions
Reset code shows the new invitation. Deactivate keeps reports. Archive hides the leader.
States
Errors: empty name/group, “Esta célula ya tiene un líder activo.” Empty: “Todavía no hay líderes guardados.” / “No se encontraron líderes.” Inactive rows are dimmed with a badge.

Admin · Mapa de células #admin/map

Purpose
See where groups meet (privately) and how far each is from the church.
Contents
Labeled ‘Ubicaciones privadas’ and ‘Ubicaciones ficticias · Phoenix’. Stylized illustration (not real tiles), church pin + numbered group pins, zoom +/−, “Ver iglesia”, selected-pin card with address and distance, “Medir desde” chips (church or any group) and a distance list sorted ascending in miles (“en línea recta”).
Interactions
Tap a pin or row to select (pin enlarges, card updates); zoom centers on the selection.
States
Pins drop in on entry. For production use real tiles and real coordinates (see Notes).

Admin · Reportes #admin/reports

Purpose
Review submitted reports and print or save them as PDF.
Contents
Filtros sheet (desde, hasta, grupo), group filter chips, Imprimir / PDF sheet (Resumen de asistencia, Totales por Grupo de Amistad, Imprimir selección, Imprimir todos), list with group, date, leader and attendance.
Interactions
Row → report detail (all five sections + attendance by name). Print options → print preview → browser print dialog (window.print()), where PDF can be saved.
States
Loading: skeleton rows ~520ms. Empty: “No hay reportes para esta selección.” Error: from > to → “La fecha inicial debe ser anterior o igual a la final.”

Report detail & Print preview #admin/print

Purpose
Read one report fully, or preview a printable sheet.
Contents
Detail: total, breakdown, meeting, attendance chips (★ = taught), closing, planning, growth. Print preview: paper sheet with logo, table, DEMO watermark; Cerrar / Imprimir / PDF.
Interactions
Print copies the sheet into #print-sheet; @media print hides the app and prints only that sheet.
States
Error: “No se pudo cargar el reporte.” if the id is missing.

Demo bar, role sheet, account sheet (always visible)

Purpose
Prototype-only tools so reviewers can explore every role and language. Remove in production (account sheet content such as Cerrar sesión stays).
Contents
Demo bar: ‘DEMO · datos ficticios’, role button (Administración or a leader), ES/EN toggle. Role sheet lists the six demo roles with their report status. Account sheet: restore sample data, practice from scratch, sign out.
Interactions
Switching role signs in without a code. ES/EN re-renders every open screen and sheet in place.
States
—

5. Desktop admin (≥ 1024px)

At ≥ 1024px the Administración role renders as a desktop dashboard instead of the phone frame. Leader role and any width below 1024px keep the phone experience. A “Vista móvil” button (top bar) shows the phone version of the admin; “Vista de escritorio” (side panel) returns. “Cambiar vista” opens the role switcher; picking a leader switches to the phone UI. Everything stays labeled DEMO · datos ficticios.

Layout, regions and measurements

RegionSize / structureBehavior
Shell #deskFixed, full viewport. CSS grid var(--d-side-w) minmax(0,1fr); fades in (280ms)..desk / body.is-desk hides .stage (phone frame + side panel).
Sidebar .d-side232px (256px ≥ 1280). Logo 32px · church card (“Iglesia Esperanza / Grupos de Amistad”) · nav · demo note · account button.Nav = 6 items (Resumen, Líderes, Mapa, Avisos, Reportes, Administradores), 46px high, 4px gap. A teal-50 highlight slides between items (translateY(var(--i)*50px), 560ms --ease-ios). Active item: teal text + aria-current=page.
Top bar .d-top64px, translucent white, bottom border.Breadcrumb “Iglesia Esperanza › {screen}” · week selector (Resumen only: ◀ label ▶, 44px, label cross-fades) · DEMO pill · ES/EN toggle · “Cambiar vista” · “Vista móvil” · admin avatar (opens Account sheet). Below 1180px the buttons become icon-only, the breadcrumb shows only the screen name.
Content .d-contentScrolls independently; padding 24px (32px ≥ 1280); inner .d-page max-width 1320px centered; vertical gap 20px.Page header = title 30px/800 + one-line subtitle + actions on the right.
Cards / tablesCard = white, 1px --line, radius 20px, --shadow-md. Tables live in .d-table-wrap (horizontal scroll inside the card only).Header row 11px/700 uppercase on --soft; body rows ≥ 58px, hover --soft; numeric columns right-aligned with tabular figures; totals row on Reportes; pending row tinted amber; inactive rows 60% opacity.
Report drawer .d-drawerRight slide-over, 480px (max 94vw), 560ms --ease-ios; backdrop fades.Opens from any report row/card. Hero total (counter) + all report sections + “Imprimir o guardar PDF”. Esc / backdrop / ✕ close; focus returns to the row.
Modal .dmodalCentered, 540px (max 92vw), radius 28px, spring-in (scale .96 → 1, 560ms).Reuses the phone bottom-sheet definitions (openSheet({title, body, mount})): add leader, invitation, WhatsApp share, manage leader, reminder, print options, role switch, account, invite administrator. sheetEls() picks phone sheet or desk modal.
Toasts .d-toastTop-right under the top bar, 380px max.Same toast() as mobile; role=status.

Breakpoints

WidthLayoutNotes
< 1024pxPhone UI for every role (full-screen ≤ 760px; phone frame + hidden side panel between 761 and 1023px).—
1024 – 1179pxDesktop dashboard, compact: sidebar 232px, padding 24px; KPI grid 2×2; chart and recent reports stack; map and side list stack; notices composer and preview stack; top-bar buttons icon-only; low-value table columns hidden (.hide-md: Líder/Visitas/Ofrenda on Reportes, Visitas nuevas/Niños/Fecha on Resumen, Grupo/Promedio on Líderes).Tested at 1024×768: no overflow.
≥ 1180pxTwo-column cards (chart 1.85fr + recent 1fr; map 1fr + 340px list; composer 1.1fr + preview 1fr), KPI grid 4×1, full tables, labeled top-bar buttons.Tested at 1280×800 and 1440×900.
≥ 1280pxSidebar 256px, padding 32px.—

Mode switching (phone ↔ desktop)

EventResult
Admin signs in at ≥ 1024pxPhone frame hides, dashboard shows on Resumen (entrance fade + KPI counters + chart draw).
“Vista móvil”S.mobileView = true → dashboard hides, phone frame + side panel return on the matching phone tab (Administradores → Líderes ▸ Administradores). The side panel shows “Vista de escritorio” to go back.
“Cambiar vista” → a leaderRole switches; leader UI is always the phone frame (also on desktop).
Window resized below 1024px / back aboveDashboard ↔ phone UI, same screen kept. Open modal/drawer is closed.
Language toggleES/EN repaints sidebar, top bar, content, drawer and any open modal without losing scroll or state.
Deep links#admin/overview, #admin/leaders, #admin/admins, #admin/map, #admin/notices, #admin/reports, #admin/print open the dashboard at ≥ 1024px and the phone UI below.

Desktop screens

Desktop · Resumen #admin/overview

Purpose
See the week at a glance and nudge groups that have not reported.
Contents
Amber banner (“N grupo(s) sin reporte” + “Recordar por WhatsApp”) or green “¡Todos los grupos reportaron!”; four KPI cards (Grupos que reportaron x/N with progress meter, Grupos sin reporte, Asistencia total, Visitas nuevas; deltas vs previous week only for complete weeks); large attendance trend chart (bars + line + gridlines + tooltips, last 6 weeks, Datos ficticios, legend, footnote only when a week is incomplete); “Reportes recientes” list; groups table (Grupo, Líder, Estado Reportado/Pendiente, Asistentes with meter, Visitas nuevas, Niños, Fecha, action).
Interactions
Week selector in the top bar re-animates counters/chart/table. “Imprimir resumen” opens the print preview. Table “Ver reporte” / recent item opens the drawer; “Recordar” opens the WhatsApp reminder modal.
States
All reported / Partial / Past week supported. Counters animate; reduced motion shows final values.

Desktop · Líderes #admin/leaders

Purpose
Find, invite and manage leaders.
Contents
Search field with live count; table (Líder + group, Grupo, Acceso Activo/Inactivo, Esta semana Reportado/Pendiente, Promedio · últimas 4, actions “Invitación” and “Gestionar”); “Agregar líder” button.
Interactions
Search filters as you type (accent-insensitive). “Agregar líder” → modal (validation: name, group, duplicate group) → private invitation modal → “Compartir por WhatsApp” (chat preview) / “Copiar mensaje”. “Invitación” re-opens the invitation for an existing code. “Gestionar” → rename / reset code / deactivate / archive modals.
States
Empty / no results illustrations; inactive rows dimmed; new leader appears with 0 average.

Desktop · Mapa #admin/map

Purpose
See where groups meet and how far they are from the church or each other.
Contents
Large illustrated map with dropping pins (church = navy cross, groups = numbered teal), zoom +/−, “Ver iglesia”, “Ilustración ficticia” tag; side panel with selected-place card, “Medir desde” select and the distance list (mi, en línea recta).
Interactions
Click a pin or a list row to select (pin scales, card updates, map zooms toward it when zoomed in); the select re-sorts the list from the chosen origin.
States
Pins fall in staggered (reduced motion: no fall). Locations are private to administration (badge).

Desktop · Avisos #admin/notices

Purpose
Write a notice and see exactly how leaders will receive it.
Contents
Composer (Asunto, Mensaje, “Todos los líderes activos” switch or recipient chips, “Enviar aviso”, “Compartir por WhatsApp”); live preview card (as the leader sees it) and WhatsApp bubble preview; “Avisos enviados” grid.
Interactions
Preview updates on every keystroke and recipient change. Send validates subject, message and recipients, shows a loading state, toasts and clears the form. WhatsApp opens the simulated share modal.
States
Field errors; loading button; empty sent list illustration.

Desktop · Reportes #admin/reports

Purpose
Filter, review and print or save attendance reports.
Contents
Filter bar (Desde, Hasta, Grupo de Amistad, Limpiar, live “N reporte(s) · Promedio por reunión”); data table (Fecha, Grupo, Líder, Disc., Visitas, Nuevas, Niños, Total, Ofrenda) with a totals row; “Imprimir / PDF” button.
Interactions
Filters apply immediately (date range validated). A row opens the report drawer (full detail + print). “Imprimir / PDF” → options modal → full-width print preview (paper sheet) → “Imprimir / PDF” calls window.print(); “Volver” returns.
States
Skeleton rows on entry (520ms), empty state, date-range error text.

Desktop · Administradores #admin/admins

Purpose
See who can administer the church account and invite more people.
Contents
Table (Nombre, Rol Propietario/Administrador, Estado Activo/Invitación pendiente, “Invitación” action); “Invitar administrador” button.
Interactions
Invite → name modal → private invitation + WhatsApp preview (same flow as leaders, admin copy).
States
Owner has no actions; pending invitations are flagged amber.

Desktop screenshots

6. Screenshots

Mobile 390×844 captured from the automated walkthrough. Desktop admin captures are in section 5 (screenshots/dd-*.png); screenshots/d-*.png show the phone frame on desktop. Report-flow video (mp4).

7. ES / EN copy (key strings)

Español (default)EnglishWhere
Grupos de AmistadFriendship GroupsApp title / login
Acceso de líderes y administradoresLeader and administrator accessLogin subtitle
Código de accesoAccess codeLogin
Entrar con códigoSign in with codeLogin button
Entrar con Face IDSign in with Face IDLogin
Guardar en mi teléfonoAdd to my phoneInstall guide
Código de acceso inválido.Invalid access code.Error
PIN incorrecto o bloqueado. Usa Face ID o tu código de recuperación.Incorrect or locked PIN. Use Face ID or your recovery code.Error toast
Hola, {nombre}Hi, {name}Leader greeting
Cambiar vistaSwitch viewDesktop top bar (role switch)
Vista móvilMobile viewDesktop top bar
Vista de escritorioDesktop viewSide panel button (returns from mobile view)
AdministradoresAdministratorsDesktop sidebar / screen
Invitar administradorInvite administratorDesktop button
Reportado / PendienteReported / PendingDesktop status badges
Imprimir resumenPrint summaryDesktop Resumen action
Reportes recientesRecent reportsDesktop Resumen card
Ver reporte / RecordarView report / RemindDesktop groups table actions
Invitación / GestionarInvitation / ManageDesktop leaders table actions
AccesoAccessDesktop leaders table column
Esta semanaThis weekDesktop leaders table column / week selector
Vista previa · como lo ve el líderPreview · as the leader sees itDesktop notices preview
Medir desdeMeasure fromDesktop map side panel (select)
Semana seleccionada / Semanas anteriores / Semana incompletaSelected week / Previous weeks / Incomplete weekDesktop chart legend
LimpiarClearDesktop reports filter bar
Nuevo reporteNew reportPrimary CTA
Falta el reporte de esta semanaThis week’s report is pendingHero (pending)
¡Reporte enviado!Report sent!Hero / success
ReuniónMeetingStep 1
AsistenciaAttendanceStep 2
Cierre de la reuniónClosing the meetingStep 3
Planeación de la próxima reuniónPlanning the next meetingStep 4
Motivación y crecimientoEncouragement and growthStep 5
Revisar y guardarReview and saveFinal step
Guardar reporteSave reportButton
Total de asistentesTotal attendeesLive total
Discípulo · Visita · Visita nuevaDisciple · Guest · New guestAttendee categories
Dio la lección / LecciónTaught the lesson / LessonTeacher toggle
Total de niñosNumber of childrenCounter
Declaración de visión / misiónVision / Mission statementStep 3 switches
ResumenOverviewAdmin tab
Grupos que reportaronGroups that reportedMetric
Grupos sin reporteGroups without a reportMetric
* Semana incompleta: faltan grupos por reportar.* Incomplete week: groups still to report.Chart footnote — only when a group is missing
Asistencia totalTotal attendanceMetric
Visitas nuevasNew guestsMetric
Agregar líderAdd leaderSheet / button
Buscar por nombre o grupoSearch by name or groupPlaceholder
AdministradoresAdministratorsTab
Crear código de accesoCreate access codeButton
Invitación privadaPrivate invitationSheet
Compartir por WhatsAppShare via WhatsAppButton
Copiar mensajeCopy messageButton
Esta célula ya tiene un líder activo.This group already has an active leader.Error
Mapa de célulasGroup mapScreen
Ubicaciones privadasPrivate locationsBadge
Medir desdeMeasure fromSection
en línea rectastraight lineDistance suffix (mi)
Lugar de reuniónMeeting locationCard / section
Dirección de reuniónMeeting addressField
Usar mi ubicaciónUse my locationButton
AvisosNoticesTab
Todos los líderes activosAll active leadersSwitch
Enviar avisoSend noticeButton
Todavía no hay avisos.No notices yet.Empty
ReportesReportsTab
FiltrosFiltersButton / sheet
Imprimir o guardar PDFPrint or save PDFSheet
Resumen de asistenciaAttendance summaryPrint option
Totales por Grupo de AmistadTotals by Friendship GroupPrint option
Imprimir selecciónPrint selectionPrint option
Imprimir todosPrint allPrint option
No hay reportes para esta selección.No reports match this selection.Empty
La fecha inicial debe ser anterior o igual a la final.The start date must be on or before the end date.Error
Ya se envió un reporte de tu célula para esta fecha.A report was already submitted for your group on this date.Error
Cerrar sesiónSign outAccount
DEMO · datos ficticiosDEMO · fictional dataDemo bar

All copy lives inline as T('español','English'). Dates: fmtDate / weekLabel.

8. Accessibility notes

  • Touch targets: every control is ≥ 42px; primary controls, tabs, steppers and keypad keys are 48–64px.
  • Contrast: navy #05223a on #fafcfd ≈ 15:1; teal #008d9b is used for large/bold text and fills with white text (≈ 4:1 — keep ≥ 15.5px/600 on teal fills); muted #546675 on white ≈ 6:1. Verify final brand colors with your tool before release.
  • State is never color-only: report status uses text + icon badges (Reportó / Pendiente); errors have text under the field; chips show a check icon when on.
  • Semantics: chips use aria-pressed; switches use role=switch + aria-checked; wizard progress is role=progressbar with aria-valuenow; sheets are role=dialog aria-modal; toasts are a role=status live region; tab bar buttons set aria-current=page; icon-only buttons have aria-label.
  • Keyboard: all controls are native buttons/inputs; map pins are focusable and respond to Enter/Space; Esc closes sheets; visible 3px teal focus ring.
  • Desktop admin: landmarks are aside (nav with aria-label and aria-current=page), header (top bar) and main; tables use th scope=col and a visually hidden “Acciones/Actions” header; clickable report rows are focusable (role=button, Enter/Space opens the drawer); the drawer and the modal are role=dialog aria-modal, move focus in on open, trap Tab, close with Esc and return focus to the trigger; search/filter results announce counts via aria-live; the week selector is a labeled group with disabled ends.
  • Reduced motion: prefers-reduced-motion: reduce removes transitions/animations (CSS) and JS transitions/counters jump to their end state; the app stays fully usable.
  • Language: <html lang> follows the toggle (es/en); inputs use inputmode (numeric PIN/code, decimal offering).
  • Safe areas: env(safe-area-inset-*) is applied to the demo bar, tab bar and footers for notched phones. Use viewport-fit=cover.
  • To do in production: trap focus inside sheets and return focus to the trigger on close; move focus to the screen heading after each route change and announce it; add screen-reader text for the chart (table alternative); add real error-recovery/offline states.

9. Mapping to the existing demo

Source: faith-layer-groups-demo (index.html, pilot.js, empower-*.js, admin-management.js, cloud.js).

Redesigned screenExisting demo screen / featureWhat changed
Login#login section in index.html: #access-form (access code + show/hide), #device-pin-login (personal PIN), #unlock-device (Face ID), .install-guide (“Guardar en mi teléfono”)Three methods become one segmented screen; PIN becomes a keypad; Face ID gets a scan animation.
Leader · InicioNew. Replaces landing on the report form: show('new') + #quick-new “Nuevo reporte” and the account bannerAdds weekly status, last reports, notice banner and meeting place card.
Leader · Reporte (5 pasos)#report form in pilot.js: five <details> sections 1 Reunión · 2 Asistencia · 3 Cierre de la reunión · 4 Planeación de la próxima reunión · 5 Motivación y crecimiento; person() rows; children field; #total + #breakdownAccordion → one step per screen. Líder/Asistente rows with Dom./Lunes checkboxes → roster chips + category segmented control; “Lección por” radio → Lección toggle. Same fields and the same total formula (disciples + guests + new + children).
Reporte enviadotoast('Reporte enviado. Un administrador puede consultarlo.')Text toast → full success screen.
Leader · Mi grupo#meeting-location-panel + #meeting-location-form (empower-map.js): address, lat/lng, GPS button, map pinLat/lng inputs hidden; tap-to-place map + coordinates caption. Leader report history is new.
Admin · Resumen#overview / showOverview(): #overview-metrics, #overview-weekSame four metrics. New: week selector, “sin reporte” alert with WhatsApp reminder, trend chart, group list.
Admin · Líderes#leader-form (Agregar líder + #invitation-text, share-invitation, copy-invitation), #leader-directory (Buscar líderes + manageLeader: rename / reset / disable / archive), #admin-management (Administradores)Three collapsed <details> → one tab with search, manage sheet and invitation sheets. Same copy and rules.
Admin · Mapa de células#groups-map-panel (empower-map.js): zoom +/−, “Ver iglesia”, #distance-origin “Medir desde”, #groups-map-list “mi en línea recta”OSM tiles replaced by a stylized illustration in the prototype; behavior and distance list kept.
Avisos#notice-panel (empower-notices.js): subject, message, recipients (all / selected), send, “Compartir por WhatsApp”, sent/received listsSame fields; recipients become chips; leader view is its own tab with unread dot.
Admin · Reportes + Impresión#reports: #report-from/#report-to/#report-group, #summary-print, #group-print, #detail-print, #all-print, #print-report, report-cardFilters → sheet + chips; four print actions → one sheet; list cards → rows + detail screen; print preview added.
Desktop admin · all screensSame sources as the matching admin screens above (#overview, #leader-form + #leader-directory + #admin-management, #groups-map-panel, #notice-panel, #reports) — the demo is a single-column responsive pageLeft sidebar + top bar replace the stacked <details> panels; tables replace list cards; report detail opens in a drawer; same copy, rules and actions.
Demo bar / role switcher.demo-banner: #demo-role select, “Abrir vista”, “Practicar desde cero”, “Ver cinco reportes”, “Llenar reporte de ejemplo”Role select → role sheet; reset buttons → account sheet; “Llenar ejemplo” in the wizard header.
Language toggle.header-language ES/EN buttons (localStorage empower-language)Single ES/EN control in the demo bar (in production put it in Account/Settings and on Login).
Usage guide#usage-guide “Guía de uso”Not redesigned in this prototype (candidate for first-run coach marks).

10. Data contract

NeedExisting actionNotes
sign in (code / PIN / Face ID)profile, redeem_admin_invite, device PIN/Face ID helpersReturns role (admin | leader), id, name, group.
List leaders / directorylist_leadersid, label (name), cell (group), active, archived.
Create leadercreate_leader {label, cell}Returns access code; app composes the invitation text.
Manage leadermanage_leader {id, operation: rename | reset | disable | archive, label?}reset returns a new code.
Administratorslist_admins, invite_admin, manage_adminOwner-only.
Submit reportsubmit {…fields, people[], total}Server must reject duplicates for the same leader + date and totals ≠ sum.
List reportslist (admin = all, leader = own)Filters (date/group) are client-side in the demo; move to the API for scale.
Meeting locationmeeting_location, save_meeting_location, list_meeting_locations (admin only)address, latitude, longitude.
Noticessend_notice {id, subject, message, all | recipients[]}, list_noticesid makes sends idempotent.
New for this redesignWeek aggregate (reported groups, total, new guests, last 6 weeks); “missing this week” list; WhatsApp reminder textCan be computed client-side from list, or add one aggregate endpoint.

Report record: id, leaderId, group, leader, weekId (Monday ISO date), date, total, disciples, guests, newGuests, children, offering, lesson, start, end, people[{name, cat: disciple|guest|new, teacher}], closing{vision, mission, announcements, notes}, planning{welcome, worship, farewell, childcare, snacks}, growth{absent[], calls, sunday, inviters, invitees, prayer, fastingCount, fastingNames}

11. Notes, assumptions, open questions

  • Desktop admin is a second layout, not a second app: it reads the same data, helpers, i18n and sheet definitions as the phone UI. In production use one responsive admin app (CSS grid breakpoint at 1024px) and keep the phone UI for leaders. Desktop DOM ids are prefixed d- only because the prototype keeps the hidden phone DOM alive.
  • Prototype-only (do not ship): DEMO bar and role sheet, fixed date (2026-11-10, week 9–15 nov), seeded data, “Llenar ejemplo”, stylized map, simulated WhatsApp/clipboard/PIN (123456)/Face ID, attendance chart data, localStorage persistence.
  • Map: the existing app uses OpenStreetMap tiles. Keep a real map in production (MapLibre/Leaflet/OSM) with these pins, zoom controls and the distance list; the illustration only shows the layout and animation. Keep locations private to administrators.
  • Week definition: weeks run Monday–Sunday; weekId is the Monday. Attendance total counts disciples + guests + new guests + children (same as the current app).
  • WhatsApp: real sharing should use the Web Share API / https://wa.me/?text= with the same invitation/reminder text; the prototype only previews the message.
  • Open questions for the product owner: offline/queued report submission; whether leaders can edit a submitted report; whether reminders should be automatic; push notifications for new notices.
  • Brand: logo/mark and colors come from the new faithgroups.app landing page; the church (tenant) name is fictional here — in production it should come from tenant settings.

12. Weekly-report form editor (admin)

Where: desktop #admin/form (sidebar → Formulario del reporte, three panes: palette · canvas · properties/preview/versions). Phone: Administración → Formulario del reporte (#admin/form at <1024px): list with ↑/↓ buttons, bottom-sheet editor, Vista previa tab, sticky Guardar borrador / Publicar.

WPAC source (honest note). The two Drive docs (1eKSdLeJ7aC7r6NZml9T_IR7PoA42MbDVCiO4hufzNiU, 1UWtL8XjAs4kGG4Y6CqksTgyXSivU2M_sp4SPASIpCjs) list only the sections of the WPAC report (lugar de reunión; datos de la reunión incl. asistencia, visitas, niños y ofrenda; cierre; planeación; motivación y crecimiento; visión y misión). Individual WPAC questions were not found in those docs. The editor therefore shows the sections from WPAC's docs and the questions from the prototype's 5-step wizard, labeled as the prototype base form — not as WPAC's own wording.

ConceptShapeNotes
form_version{ id, n, fields[], published_at, applies_from_week, status: draft|published }Publishing creates n+1; it applies from the next week (weekStart + 7d). Banner: “Versión 3 — se aplica desde la semana del 16–22 nov 2026”.
field{ id, type, label_es, label_en, section, status: required|optional|hidden, options? }Types: short text, long text, number, yes/no, single choice, multi choice, date, time, attendee list, group person, photo. Hidden fields stay in the form but are not shown to leaders.
report.form_version_idFK to the version the report was submitted withPast reports keep their original questions and show “Formulario v2”. Render-time fallback: reports without the id resolve to the version in force that week (no DB migration in the prototype).
  • Drag & drop: HTML5 DnD (palette → canvas, row → row) with FLIP animation; keyboard/phone: ↑/↓ buttons (fe-up, fe-down). Both write the same fields[] order.
  • Autosave: the draft autosaves (debounced) with the cloud pill (Guardando… / Borrador guardado en la nube / Sin conexión · Pendiente de sincronizar). Publicar is a separate, explicit action with a confirmation dialog.
  • Storage in the prototype: localStorage['fg-proto-forms-v1'] (RFORM). The leader's real 5-step wizard is still hard-coded; the published form drives the preview only (see caveats). Production: the wizard must render from form_version.fields.

Tests: python3 _tests/editor.py (add, drag, reorder, bilingual edit, hide, autosave, publish → v3, past report = v2, phone ↑/↓ and sheet).

13. Church identity (logo, accent color, contrast)

Where: desktop #admin/identity (sidebar → Identidad de la iglesia); phone Administración → Identidad; also an optional step in onboarding (Omitir por ahora).

FieldTypeMeaning
church.logo_urlstring (https URL or data URL in the prototype)Uploaded logo. Production: store in object storage, re-encode PNG/WebP, max 2 MB, serve via CDN.
church.accent_hex#RRGGBBAccent used for buttons, progress bars, links. Everything else stays Faith Groups navy/teal.
church.uses_default_themebooltrue → official Faith Groups navy #05223a / teal #008d9b (“Faith Groups (predeterminado)”).
  • Color extraction (client-side): the logo is drawn to a 96-px canvas; transparent/near-white pixels are dropped; k-means (k=4, 8 iterations, deterministic seeding) returns 4–6 swatches (dominant colors + tonal variants) plus the default swatch.
  • Contrast rule: the accent must reach 4.5:1 against white (WCAG AA for text). Below that, the UI shows the exact ratio and Ajustar automáticamente (darkens in small steps until ≥ 4.5:1). The official default teal measures 3.97:1 and is exempt (shown as meeting the 3:1 UI-component threshold) — flagged for Victor to decide whether to darken it for text.
  • Logo placements: login screen, app header (leader/admin), desktop sidebar, onboarding preview, printed/PDF reports (header), invitation screens, giving QR screen.
  • WPAC demo: WPAC uses the default colors plus the WPAC logo. Logo source: Google Drive file Logo Clear.png (id 1c9fZ318GqPQKB6Ft6PAmkfG0koM42O-d), downloaded and visually verified (navy “WP” + orange “AC” wordmark on a transparent background). The church switcher toggles Iglesia Esperanza (fictional, untouched) ↔ WPAC.

14. Plans, onboarding and invitations

Routes: #plans (public plans page) · #onboarding → /verify → /plan → /giving → /identity → /preview · #join, #join/<CODE>, #join/code, #join/expired, #join/request · admin #admin/setup (Primeros pasos).

PlanPriceNotes
Iglesia / Church$39/mes · $39/moPlaceholder feature bullets
Multicampus / Multi-campus$99/mes · $99/moPlaceholder feature bullets
Red de iglesias / Church networkSolicitar cotización · Request a quoteNo price; opens a quote request

Feature bullets under each plan are illustrative placeholders for Victor to confirm. No payment is taken and there are no card fields. Every plan lists: Copias de seguridad automáticas y cifradas (automatic, encrypted backups included).

church        { id, name, city, tz, lang, logo_url, accent_hex, uses_default_theme, plan_id, giving{...} }
admin_user    { id, church_id, name, email, role: owner|admin, email_verified_at }
plan          { id: church|multicampus|network, price_month, quote: bool }
invitation    { id, church_id, code /* 6 chars, A-Z0-9 no I/O/0/1 */, single_use: true,
                expires_at /* created + 48 h */, group_id, leader_name, channel, status: pending|accepted|expired|revoked, leader_id? }
invitation_request { id, church_id, invitation_id, name, contact, group_name, created_at }   // admin notice
  • Flow: registration form → verify e-mail (demo code button) → plan → offerings (optional) → identity (optional, Omitir por ahora) → preview → Entrar a mi panel; stepper on every step.
  • Setup checklist: (1) review the form → opens the editor, (2) create first group, (3) invite first leader (share menu). Invitation list shows Vence en 46 h / Aceptada / Venció with Renovar.
  • Join my church: admin shares a link (WhatsApp or SMS) or a 6-char code; leader sees church name + logo + assigned group before Aceptar invitación; the code is single-use. Expired: screen Esta invitación venció → Solicitar nueva invitación → request lands in the admin's Primeros pasos with Enviar nueva invitación.

Tests: python3 _tests/onboard.py (plans, no card fields, stepper, identity, Path A, Path B, expired, request).

15. Cloud save states, offline queue and idempotent submit

StateESENWhen
savingGuardando…Saving…debounced autosave in flight
savedBorrador guardado en la nubeDraft saved to the cloudserver ack of draft.save
offlineSin conexión · Pendiente de sincronizarOffline · Pending syncnavigator.onLine === false or request failed; data kept locally
sendingEnviando…Submitting…submit in flight
sentReporte enviadoReport submittedonly after the server acknowledges the submit (celebration plays here and nowhere else)
retryReintentarRetrymanual retry; also automatic on online
  • Autosave: every keystroke writes a local copy (fg-proto-local-v1); a debounced (900 ms) draft.save goes to the server. A lost connection never loses typed data.
  • Submit: POST /reports with header Idempotency-Key: idem-<leader>-<week>-<random>. The key is generated once per submission series and reused on every retry; the server stores key → report and returns the stored receipt (duplicate: true) on repeats → exactly one report.
  • Offline at submit: state offline, banner + Reintentar, no celebration, report is not in the admin list. On online the app retries automatically with the same key.
  • Mock server (prototype): NET.call(type, payload) with 350–900 ms latency, navigator.onLine + a demo toggle (En línea / Sin conexión button), fg-proto-server-v1 as the “server database”.
  • Backups: automatic, encrypted, included in every plan (stated on the plans page, settings and checklist). Real implementation = managed DB with PITR + encryption at rest; not simulated beyond copy.
  • Admin form editor shows the same pill (saving/saved/offline); Publicar is separate.

Test: python3 _tests/cloud.py [--shots] — WPAC leader Daniel Torres; context.set_offline(True) exactly at Submit → offline/pending with data preserved → set_offline(False) → submit → Reporte enviado; asserts exactly one report and a deduplicated second report.submit with the same key.

16. Native iPhone app + web panel (Path A / Path B)

Web panel: select a group → Compartir invitación / Share invitation → Copiar enlace, Copiar código, Correo, Mensaje de texto, WhatsApp (one option), Más apps (Web Share API when available). iPhone app (iOS-native look, Inter, navy/teal, iOS status bar): showcase at #ios/<scene> (full-bleed 390×844; framed on desktop) and the synced journey at #journey (web panel left, iPhone right, 17 steps; autoplay).

PathSteps
A · universal linkAdmin → Mensaje de texto → SMS bubble with https://<domain>/join/<CODE> → app installed → opens straight on the invitation (church, logo, group) → Aceptar
B · no appLink opens a web fallback page → text button Descargar en App Store (no Apple badge art) → App Store listing (mockup, labeled) → open app → welcome → Unirme con un código / Join with a code → 6-char segmented code → confirm church/logo/group → Aceptar
ExpiredEsta invitación venció → Solicitar nueva invitación → admin notice
Report5-step report with cloud states; offline at submit → draft kept → reconnect → exactly one report → Reporte enviado + celebration only on server confirmation; admin shows 1 report received
  • Universal links / associated domains: host https://<app-domain>/.well-known/apple-app-site-association (no redirect, application/json): {"applinks":{"details":[{"appIDs":["<TEAMID>.com.faithlayer.faithgroups"],"components":[{"/":"/join/"},{"/":"/give/","exclude":true}]}]}}. Add the entitlement com.apple.developer.associated-domains = applinks:<app-domain>. Handle in application(_:continue:) / SwiftUI .onOpenURL → route /join/<CODE>.
  • Deferred deep link: iOS cannot hand the link through an App Store install. Do not rely on the clipboard or fingerprinting; the web fallback shows the 6-char code and the app asks for it on first launch (Unirme con un código). A first-party deferred-link service (Branch/AppsFlyer) is optional later.
  • Invitation API: POST /churches/:id/invitations {group_id, leader_name, channel} → {code, link, expires_at}; GET /invitations/:code (public: church name, logo, group, status — no personal data); POST /invitations/:code/accept {name, device_id} (single use, 410 when expired, 409 when used); POST /invitations/:code/request-new {name, contact} → notifies admins.
  • Push (later): APNs for aviso and reporte pendiente reminders; token registered on accept.
  • Prototype caveat: the iPhone scenes are web-rendered; the App Store page is a labeled mockup; the SMS/e-mail/WhatsApp buttons show a simulated preview (nothing is sent).

Tests: onboard.py covers Path A/B/expired in the real UI; cloud.py covers the offline report; the #ios and #journey scenes are exercised by shots_ext.py (no console errors).

17. Ofrendas y diezmos / Offerings & tithes (optional)

Where: onboarding step Ofrendas y diezmos (optional) and later Configuración → Ofrendas y diezmos (#admin/giving; phone: Administración → Ofrendas y diezmos). Leader: Mi grupo → Compartir ofrendas y diezmos. Visitor pages: #give/<church>/<group> (Stripe-style, TEST MODE) and #give-ext/<church>/<group> (example external provider).

ModeWhat it does
Conectar StripeStates: not connected → connecting → verification pending → Habilitada para recibir pagos. Only when enabled does Faith Groups generate a link + QR per célula. Confirmed payments show in the weekly report as Efectivo vs En línea (Stripe) and in the admin week summary.
Usar mi enlace de donacionesPaste an https:// link → Probar enlace (format validation, simulated reachability, opens a new tab) → save. General or per-célula link. If the provider has categories the person picks Ofrenda or Diezmo there. With an external link, payments are checked in the church’s own system; reflecting them automatically in Faith Groups would require an integration with that provider. The weekly report keeps cash entry only.
Configurar más adelanteContinue onboarding; enable anytime; cash keeps being recorded.

Simulation boundaries: no real Stripe keys, no real charges. Everything Stripe-related is a clearly-labeled simulated TEST MODE (banner MODO DE PRUEBA / TEST MODE, test-card hint 4242 4242 4242 4242). QR codes are real and scannable (generated client-side with the inline MIT library qrcode-generator 2.0.4; no CDN) and are verified by decoding in the tests. Stripe demo URL: https://faithgroups-app-prototype.vercel.app/#give/wpac/<celula>.

church.giving   { mode: unset|stripe|external|later, stripe: not_connected|connecting|pending|enabled,
                  ext: { url, tested, saved, groupUrls: { <group_slug>: url } } }
payment         { id, church_id, group, group_slug, week_id, amount, category: offering|tithe,
                  status: succeeded, session_id /* idempotency */, created_at }

Real implementation (not built):

  • Onboarding: Stripe Connect Standard (church owns the Stripe account; fewer compliance duties for us) or Express. POST /v1/accounts → POST /v1/account_links {type: account_onboarding} → redirect → return URL. Store stripe_account_id on the church.
  • Enabled state: webhook account.updated → when charges_enabled && details_submitted set giving.stripe = enabled (pending until then).
  • Per-group link: create a Payment Link or a Checkout Session (mode=payment, payment_intent_data.on_behalf_of/direct charge with Stripe-Account header) per group with metadata: { church_id, group_id, category }; the visitor chooses Ofrenda/Diezmo (two prices or a custom amount + category metadata). QR encodes the Faith Groups short URL /give/<church>/<group> that creates the Session on demand.
  • Recording payments: webhooks checkout.session.completed and payment_intent.succeeded (verify signature; handle retries) → insert payment keyed by checkout_session.id/payment_intent.id (unique index = idempotency) → add to the group's weekly report for the week of created. Refunds (charge.refunded) subtract.
  • Reporting: weekly report shows Efectivo (manual entry) and En línea (Stripe) (sum of succeeded payments for that group + week) separately, plus the total; the admin summary adds both.
  • External link mode: only store the URL and optional per-group overrides; no webhooks. An integration (e.g., the provider's API/webhook) would be needed to reflect gifts automatically.

Test: python3 _tests/give.py (Stripe states, TEST MODE banner, payments recorded once, cash vs online report rows, external validation/test/save, QR decoded with OpenCV for one WPAC célula in both modes, leader share sheet).

18. Routes, tests, and caveats for sections 12–17

RouteWhat
#admin/formForm editor
#admin/identityChurch identity
#admin/givingOfrendas y diezmos settings
#admin/setupGetting started + invitations
#plansPlans page
#onboarding[/verify|/plan|/giving|/identity|/preview]Registration flow
#join, #join/<CODE>, #join/code, #join/expired, #join/requestJoin my church (phone UI)
#ios/<scene>iPhone scenes: home, messages, invite-a, done-a, safari, appstore, welcome, code, invite-b, done-b, expired, request, rep-saving, rep-saved, rep-offline, rep-reconnect, rep-sent, give
#journey[/1..17]Web ↔ iPhone journey
#give/<church>/<group>, #give-ext/<church>/<group>Visitor giving pages (simulated)
TestCovers
_tests/editor.pyform editor
_tests/onboard.pyplans, onboarding, identity, invitations
_tests/cloud.pycloud states + offline at submit (context.set_offline)
_tests/give.pyofferings & tithes, QR decode
_tests/shots_ext.pyall fe-/ci-/ob-/pl-/cs-/ios-/jr-/og- screenshots
  • Plan feature lists are placeholders for Victor to confirm; prices are $39 / $99 / quote as requested.
  • The WPAC individual questions were not found in the Drive docs; the question set is the prototype's wizard, labeled as such.
  • The leader's 5-step wizard remains hard-coded; the published form drives the preview only (production wizard must render from form_version.fields).
  • Stripe, the App Store page, SMS/e-mail/WhatsApp sending, e-mail verification and the server are all simulated; the QR codes, the color extraction, contrast math, offline queue and idempotency logic are real code.
  • The Faith Groups default teal (#008d9b) is 3.97:1 on white: fine for large text and UI components, below 4.5:1 for small text — decide whether to darken the default.

Faith Layer Software · Yelevo LLC · Prototype and handoff generated from the same source as index.html. Source repo path: /workspace/faithgroups-app-prototype.