The LAST civic screen — when the village needs to speak to everyone at once. Implements S14-decision-sheet.md verbatim (✅ designed; 🚫/⏳ absent; 🔨/⚠️ in captions/foot). A1: ONE unified broadcast CARD LANGUAGE — event / official broadcast / channel / alert are all the REAL post card (DECISIONS #11, refs/real-place-feed-post.png) with a wrapper-level left-border urgency accent (S6-C9 pattern): red alert · amber official · teal event · neutral channel — one Flutter widget, variant props, never a redesigned card. A2: placement differs by class — events in feed date-sorted, broadcasts/channels labeled in feed, alerts = PINNED banner above the header + 🚫 NO browsable alert feed (a scroll of past alarms trains catastrophizing). A3: the reader never sees the taxonomy — only WHO + HOW-URGENT. Trust governs alerts: class gates + corroboration + honesty labels + mandatory retraction push; 🔴 E14 kill-switch (per-alert kill + Region.alertsFrozen + CMS dashboard) ships BEFORE any alert UI. Resolves the S8-B8 channels-in-feed, S13-D9 broadcast-in-feed and S6 D15 share-card deferrals. Depends on S13 verifiedCivic live.
Implements A1 — all four broadcast-class cards are the REAL post card (DECISIONS #11: .s6-post anatomy 1:1 from refs/real-place-feed-post.png) + a WRAPPER-level left-border accent (S6-C9): teal EVENT ("Gram Sabha · Tomorrow 11am · Panchayat Bhawan", जाऊंगा + "12 going" social proof B5), amber OFFICIAL (seat display name "MLA · Marhaura" + verified badge, S13-D9 unlocked), neutral CHANNEL (Bheldi Mandi Bhav, S8-B8 unlocked), and a NORMAL post for contrast — one Flutter widget, variant props, 🚫 never a new card. A2 the active alert is a PINNED dismissible red banner ABOVE the header (G17 — too urgent for the personal strip slot); 🚫 no browsable alert feed — only the 7-day collapsed "recent alerts" audit row at feed bottom. A3 the reader never sees the class taxonomy — only WHO + HOW-URGENT; 🚫 tabs per class (2G tap cost). Feed order = A1's ONE query: upcoming events (<7d, date-sorted) → recent broadcasts → normal recency. 🔨 getPlaceBroadcastFeed = ONE query (active alerts by severity → events by startDateTime → broadcasts), not three · channel posts need the NEW Post.groupId sparse field (separate pre-build decision; circleId wall stands per S8-B7) · alert = Post w/ NEW ALERT source.provider + denormalized {class,severity,verified,killAt} + AlertOverlay collection (C9).
Implements B4 first-class event type — EVENT POST_TYPE already exists; 🔨 extend PostEvent: endDateTime + recurrence{freq,until} (weekly haat / monthly gram sabha materialize the NEXT occurrence — the "हर सोमवार haat" row) + rsvpCount; regionIds REQUIRED on civic events (today they inherit silently); 🚫 calendar view v1 — no village persona uses one, feed-of-upcoming IS the calendar at ~3 events/month · A2 feed placement is PRIMARY (date-sorted, <7d, above normal posts), the pull-list is the SECONDARY migrant surface — "what's happening at home next month" · B5 the 5-second detail sheet: KAB (relative date — today/tomorrow/N-days, never ISO) · KAHAN (landmark name, never coords) · KYA/KAUN (what's expected + WHO called it — sarpanch vs BDO changes attendance, role chip); RSVP verb renders per locale (hi जाऊंगा / नहीं जाऊंगा shown as the canonical label — 🚫 the word "RSVP" never renders; en key "I'll go"); RSVP = reminder CONSENT → T-24h + T-1h SmartNotif (server-cron, not client timers), de-RSVP cancels; याद दिलाएं = reminder without commitment; count = social proof · B6 OPEN creation + role chip (official/steward events badged, steward can PIN one); recurring official events fan out via S13 VCM. 🔨 EventRsvp = NEW collection — 🚫 reuse PostLike (attend ≠ like, different Gorse signal). Metric: event view→share ≥20% on home-place events (share = understood + valued; RSVP is vanity here).
Implements C7 the CLASS GATE — who may raise + TTL per class; Class-E (communal / "suspicious person" / violence) is ABSENT BY DESIGN — BANNED v1 ⚠️: not gated, not configurable, does not EXIST in the picker (the WhatsApp-rumor-to-mob vector — a simultaneous push inverts speed into amplified violence; DM/SP use S13 broadcast for genuine law-order); 🚫 free-text/"Other" subtype (a Class-E proxy) · C8 CORROBORATION (utility class only): raiser → PENDING (visible to raiser + stewards ONLY, no wide push) → "Is this true in your area?" SmartNotif to the 50 most-recently-active residents → 3 unique confirms (distinct, ≥7d accounts, in-VCM, not raiser/followers) → LIVE + push; 45-min window else silent expire; 🔨 AlertCorroboration NEW collection (🚫 PostLike — Gorse corruption + honesty break) · C9 alert = Post (🔨 NEW ALERT source.provider + denormalized {class,severity,verified,killAt}) + 🔨 AlertOverlay (corroborationCount, verificationState, regionScope, killAt/By, notifBatchId) — mirrors issue=post; NAMED always, 🚫 anonymous (village social accountability is the false-alert brake) · C10 honesty labels verbatim — 🚫 EVER labeling a resident alert "Verified"; staleness decay "Active 2h ago — may no longer be current" · missing person = POLICE-OFFICIAL only + FIR ref ⚠️, photo → NSFW pipeline · 🔨 D12 FULL alert text IN the push payload (offline-render — "you have an alert" + 8s feed load costs lives), NEW CIVIC_ALERTS Android channel priority:10, iOS time_sensitive wired, critical/DND-break needs the Apple entitlement ⚠️ owner action; emergency severity BYPASSES N1 bundling + preference checks ⚠️.
Implements C11 RETRACTION: raiser self-retract <60 min (utility class); steward anytime WITH a reason chip; official/admin for official classes; every retraction fires a MANDATORY "corrected" push to the original recipients — silence after a flood warning is ambiguous; 🔨 OneSignal collapse_id on the original so the retraction collapses the unread alert (Android); 🚫 auto-retract via flag count — 3 flags = review + "Under review" label, never auto-kill (a coordinated group could silence a real emergency, S13-C8) · resolved state + F16 "tell neighbours Bheldi is safe" D15 share = the ONLY alert-as-acquisition path — 🚫 auto-share or reshare button on a LIVE/unverified alert (don't platform-amplify possible falsehood; 🚫 alert reshare button at all, E15) · H18 ONE-DIRECTIONAL alert→issue: "File as a civic issue →" pre-fills the S7 composer (category + body, rides S6-D14 placeId wiring) — urgent panic becomes a durable civic record; an issue never becomes an alert · 🔴 E14 BUILD BEFORE ANY ALERT UI ⚠️ (app can't depict admin — this caption is the carrier): (a) per-alert kill → KILLED + retraction push <2 min (b) Region.alertsFrozen sparse boolean — place-level freeze kills pending+live and blocks new = the mob-situation switch, one field + one check (c) class-level feature flags (d) CMS dashboard of all live alerts. Alerts CANNOT ship without (a)+(b)+(d). E15 graduated penalty ⚠️: utility cap 2/place/7d; 2 retractions/30d → RAISE_CIVIC_ALERT cooldown 60d; 3+ → admin review. 🔨 TTL lazy-expire + SOFT_DELETE cron.
Implements F16 — the D15 share/invite card, finally designed (resolves the S2/S3/S6 deferrals): 9:16 WhatsApp-status format, CLIENT-rendered off the already-loaded landing payload (S10-C8 pattern, zero server cost, theme-independent export); place name + HONEST count (the SAME 7-day figure as the Today strip — Law #7) + next-event OR open-issues line; all-zero → the card is SUPPRESSED — no ghost-town card ever leaves the app. Fires from exactly three triggers: quiet-feed CTA (S6-E17 / S3-C10b destination) · post-RSVP "invite to {event}" · resolved-alert "Bheldi is safe" (S14·D); 🚫 auto-share of any live/unverified alert. The S10 user-achievement card stays a DISTINCT artifact. G17 arbitrates the S3 cell-③ conflict: an active alert is NEVER a strip cell — it's the dismissible banner ABOVE the header (too urgent for the personal slot); cell ③ priority ladder = RSVP'd-event-within-24h → upcoming-event-count (7d) → Seva delta → completeness. 🔨 nextEvent + activeAlert optional fields on GetPlaceLandingResponse (one query each, ride the 10-min cache BUT invalidatePlaceDetail on every alert write — and the client double-gates killAt<now against a stale cache). Metric: weekly alert/event engagement (RSVP ∪ alert-tap ∪ D15-share ÷ visitors) ≥25% in 90d; place_share_card_sent{trigger}.
DECISIONS #8: ONE language per screen via i18n — this frame renders S14·A end-to-end in hi. Vocabulary locked by the sheet's i18n block: alert banner = चेतावनी (disaster) vs सूचना (info); event tag = कार्यक्रम (मेला/सभा per type); official = घोषणा; channel = चैनल; RSVP = जाऊंगा / नहीं जाऊंगा, reminder = याद दिलाएं — 🚫 raw "Alert" / "Event" / "RSVP" / "Broadcast" anywhere on the hi locale. Honesty labels translate fully: "निवासियों की सूचना — आधिकारिक पुष्टि नहीं", staleness "2 घंटे पहले — अभी भी हो ज़रूरी नहीं", official "ज़िलाधिकारी कार्यालय की ओर से". Going-count = "12 जा रहे हैं". Numerals, ₹ rates, handles and the brand "Nyburs" stay Latin. Layout, components, accents and ordering identical to S14·A. 🔨 full hi i18n batch ships WITH the screen (alert push payload text is also localized — the push IS the alert on 2G, D12).
Journey position: S14 closes the civic journey (S1 entry → … → S13 authority → S14 megaphone). The feed/banner frames render inline on the place page (Feed tab, Place branch root); composer/picker/detail surfaces are pushed sheets. One language per screen (DECISIONS #7/#8): en frames + the S14·F hi check. Build ledger (S14 = mostly net-new, sequenced): 🔴 FIRST — per-alert kill + Region.alertsFrozen + class feature-flags + CMS live-alert dashboard (E14; alerts CANNOT ship without a+b+d; depends on S13 verifiedCivic production-ready) · AlertOverlay + AlertCorroboration NEW collections (C8/C9, 🚫 PostLike reuse) · NEW ALERT source.provider + denormalized {class,severity,verified,killAt} marker · PostEvent extend endDateTime/recurrence/rsvpCount + EventRsvp NEW collection (B4/B6) · getPlaceBroadcastFeed ONE unified query (A1) · Post.groupId sparse field for channel posts (A3 — separate pre-build decision; the S8-B7 circleId wall stands) · CIVIC_ALERTS Android channel priority:10 + FULL alert text in the push payload (offline render, D12) + iOS time_sensitive (wired) + Apple critical-push entitlement (⚠️ owner action) + emergency-severity preference/bundling BYPASS (⚠️) · TTL lazy-expire + SOFT_DELETE cron · nextEvent/activeAlert on GetPlaceLandingResponse + invalidatePlaceDetail on alert write + client killAt double-gate (G17) · mutateAlertScope (regionIds ancestor-chain expansion, D13) + delta push + multi-region cache invalidation · OneSignal collapse_id retraction (C11) · D15 client-rendered share card (F16) · alert→issue composer pre-fill rides S6-D14 placeId wiring (H18) · LadderAlert app stub → real · full hi i18n batch. ⚠️ Owner-veto window (design-frozen until vetoed/confirmed): Class-E communal/suspicious-person ban (C7 — lead strongly recommends hold) · missing-person police-only + FIR (C7) · Apple critical-push entitlement (D12, owner action) · emergency-severity preference-bypass (D12) · utility cap 2/place/7d + retraction cooldowns (E15) · alertsFrozen/kill-switch-first (E14 — strongly recommend hold). Metrics: event view→share ≥20% (home-place) · weekly alert/event engagement ≥25% in 90d · alert→issue conversion ≥10% · events: place_event_rsvp/created/share · alerts: place_alert_raised{class,role}/corroborated/label_seen/resolved/false_flagged/converted_to_issue · place_share_card_sent{trigger}.