---
name: ffh-recalculate-layer
description: >
  FFH Recalculate Universal Layer architecture, integration patterns, and extension guide. This is
  the platform-wide drift detection and comeback system that any FFH tracker, Bingo card, Quest, or
  Academy module can adopt with two lines of code. Trigger whenever Rob or Lucy mentions "Recalculate,"
  "recalculate tool," "drift," "drift detection," "comeback," "fell off," "got off track," "back on
  track," "off goal," "Comeback Coins," "Recalibrated badge," "GPS metaphor for health," "Ripple
  Coefficient comeback," or references the files recalculate-engine.js, recalculate-ui.css,
  recalculate-demo.html, recalculate-demo-allinone.html, or FFH-Power-Tool-Recalculate.html. Also
  trigger when they say "add recalculate to [tracker name]," "wire Recalculate into the Bingo card,"
  "extend the barrier profiles," "add a new tracker profile," "add resources to Recalculate," "Pace
  needs to wire the Composer events," or any request to integrate, extend, or modify the universal
  drift layer. Use alongside ffh-academy-dev for host-page integration, ffh-nav-bar when building
  standalone Recalculate pages, ffh-coach-lucy for Coach Lucy voice and persona, ffh-virtual-csuite
  for strategic decisions about where Recalculate fits in the platform, and ffh-fundraiser-coach
  when Recalculate drift signals tie into grant narratives or community impact reporting.
---

# FFH Recalculate Universal Layer

This skill provides the architecture, integration patterns, and extension guide for the FFH Recalculate Universal Layer — the platform-wide drift detection and comeback system that lives behind every FFH tracker, Bingo card, Quest, and Academy module.

## What This Is

Recalculate is not a destination — it's a **layer**. The host page (Cardiovascular Bingo card, sleep tracker, Health Is Wealth module, anything with a goal) imports the layer, registers its goal in one call, and inherits the entire comeback experience automatically: drift detection, the Me/We/Ours diagnostic, the Scenic/Direct/Detour route generator, the matched-resource catalog, the Share It kit, the coin and badge economy, the Composer event stream, and the anonymized PHIT community signal.

The core philosophy is borrowed from a GPS: when someone drifts from their health goal, the system never says "you're lost." It says, with Coach Lucy's warmth, "From wherever you are right now, here's how we find a new route." Drift is data, not failure. Comebacks count more than streaks. The Ripple Coefficient gets stronger when people share their recalculations, not when they pretend they never fell off.

## The Five-File Package

The layer ships as five files. Every file has a single responsibility.

**`recalculate-engine.js`** is the brain. ~700 lines, no dependencies. Contains the goal registry, drift watcher, 3-step diagnostic state machine, barrier profiles (movement, mind, nutrition, finance, generic), the resource catalog (80+ barrier × sub-barrier combinations), the route generator, the share kit, the storage layer (localStorage by default, designed to be swapped for an API), and the full Composer event bus emitter. The public API surface is intentionally tiny: `register`, `logAction`, `watch`, `unwatch`, `checkDrift`, `open`, `close`, `dismissBanner`, `addResources`.

**`recalculate-ui.css`** is the shared visual language. ~11 KB. Defines the inline drift banner, the modal sheet (slides up from bottom on mobile, centered on desktop), the Coach Lucy bubble, the Me/We/Ours tag pills, the choice grid, the route cards (Scenic green / Direct blue / Detour gold), the action checklist, the share button cluster, and the coin-burst award visual. Uses the FFH brand variable system (`--ffh-navy`, `--ffh-orange`, `--ffh-green`, `--ffh-gold`, `--ffh-rose`, `--ffh-gray`, `--ffh-light`, `--ffh-mid`). All class names are namespaced with the `ffh-recalc-` prefix to prevent collisions with the host page.

**`recalculate-demo.html`** is the split-file proof and the reference integration. Three mock trackers (movement, mind, finance) wired into the layer with two lines each. Includes a live Composer event log so you can watch every event stream as you click through. **Requires the engine and CSS files in the same folder** — this is the version Pace deploys to a real server. Point new developers to this file first to learn the integration pattern.

**`recalculate-demo-allinone.html`** is the self-contained version for phone testing, partner demos, email previews, and any context where external file loading isn't reliable. The engine and CSS are inlined into a single ~75 KB HTML file. **Critical implementation detail:** when inlining the engine, all occurrences of `</script>` inside the engine's docstring must be escaped to `<\/script>` so the browser's HTML parser doesn't close the inline script tag early. JavaScript treats both forms identically in string literals; only the HTML parser cares. The build pattern is in this skill's appendix.

**`INTEGRATION-GUIDE.md`** is Pace's one-pager. Covers the two-line install, full event reference, suggested coin/badge wire-up, storage schema for backend swap, PHIT pipeline, and the production-readiness checklist.

There is also a separate standalone Power Tool page (`FFH-Power-Tool-Recalculate.html`) that exists as a marketing destination for users who want to actively recalculate without waiting for a drift trigger. It uses the same visual language but is self-contained — useful for shares, embeds, and onboarding flows.

## The Two-Line Install Pattern

Every FFH page that has a goal follows this exact pattern. There are no exceptions and no variations.

```html
<link rel="stylesheet" href="recalculate-ui.css">
<script src="recalculate-engine.js"></script>
```

Then in the host page's existing script block:

```js
FFH.Recalculate.register({
  goalId:         'walk-30x5',
  label:          'Walk 30 min, 5 days/week',
  tracker:        'sports-health',
  thresholdDays:  7,
  barrierProfile: 'movement',
  unit:           'walks'
});
FFH.Recalculate.watch();
```

When the user logs progress anywhere on the page (a Bingo square checked, a tracker entry, a Quest action), the host calls `FFH.Recalculate.logAction(goalId)` to reset the drift clock. That is the entire integration. The host page does not need to know about the diagnostic flow, the resources, the routes, the share kit, or the events. The layer handles everything.

## The Goal Registry Schema

```js
{
  goalId:         'unique-id',         // required, stable across sessions
  label:          'Human-readable',    // shown in banner and sheet
  tracker:        'tracker-namespace', // e.g. 'sports-health', 'mental-wellness', 'health-is-wealth'
  thresholdDays:  7,                   // days inactive before drift fires
  barrierProfile: 'movement',          // 'movement' | 'mind' | 'nutrition' | 'finance' | 'generic'
  unit:           'walks',             // optional, used in route descriptions
  onCommit:       function(p) { ... }  // optional callback when user commits a plan
}
```

The `tracker` field is critical because it is what routes anonymized barrier signals to the right PHIT bucket. Always set it. Never leave it blank.

## The Five Barrier Profiles

The diagnostic state machine selects a barrier profile based on the registered goal. Each profile has 6 primary barriers (each tagged Me, We, or Ours) and 4 secondary options per primary, producing a 3-step branching flow with 24+ leaf paths per profile.

**`movement`** — Sports Health, Cardiovascular Bingo, walking/running/strength/movement trackers. Primary barriers: body, resources, place, people, motivation, other.

**`mind`** — Mental health module, sleep, anxiety, mood, isolation, screen time. Primary barriers: sleep, stress, mood, anxiety, isolation, screens.

**`nutrition`** — Eating, food access, cooking, cravings, social eating, planning. Primary barriers: access, skills, cravings, social, emotional, planning.

**`finance`** — Health Is Wealth module, money check-ins, debt, savings, financial literacy. Primary barriers: income, expenses, habits, knowledge, pressure, overwhelm.

**`generic`** — Fallback for any new tracker that does not yet have a custom profile. Uses Me/We/Ours-tagged primaries (body, resources, place, people, motivation, other) but with shallower sub-barriers. **Always start a new tracker with `generic` and graduate to a custom profile once the content is authored.**

## Adding a New Barrier Profile

When Rob or Lucy wants a new domain (faith life, civic engagement, learning, etc.), the profile gets added to the `barrierProfiles` object near the top of `recalculate-engine.js`. The shape is rigid:

```js
profileName: {
  primary: [
    { id: 'kebab-id', icon: '🦵', label: 'Short label', sub: 'one-line description', tag: 'me|we|ours' },
    // exactly 6 entries
  ],
  secondary: {
    'kebab-id-of-primary-1': [
      { id: 'sub-id', icon: '⚡', label: 'Short', sub: 'description' },
      // exactly 4 entries
    ],
    // one secondary array per primary id
  }
}
```

Then add resources for each `primaryId.subId` pair to the `resourceMap` object below. Six primaries × four sub-barriers = 24 resource entries per new profile. Use `FFH.Recalculate.addResources({ ... })` to extend without editing the engine file when only adding resources (not new profiles).

## The Composer Event Surface

The engine emits events to `window.FFH.bus.emit(eventName, payload)` and falls back to `console.log` if no bus is wired. This is Pace's integration point.

| Event | Payload | When |
|---|---|---|
| `recalc.drift.detected` | `{ goalId, daysSinceLastAction, threshold }` | Watch loop finds inactivity ≥ threshold |
| `recalc.session.started` | `{ goalId, sessionId }` | User opens Recalculate sheet |
| `recalc.barrier.tagged` | `{ goalId, tier, value }` | Step 1 or 2 answered (tier = `'primary'` or `'secondary'`) |
| `recalc.pace.selected` | `{ goalId, pace }` | Step 3 answered (`'tiny'` / `'moderate'` / `'full'` / `'different'`) |
| `recalc.route.selected` | `{ goalId, route }` | User picks Scenic / Direct / Detour |
| `recalc.action.completed` | `{ goalId, index, done }` | User checks/unchecks a tiny action |
| `recalc.shared` | `{ goalId, channel }` | User taps a Share button |
| `recalc.plan.committed` | `{ sessionId, goalId, tracker, route, pace, barrierTags, timestamp }` | User locks in plan |
| `recalc.session.dismissed` | `{ goalId, reason }` | User dismisses banner |
| `phit.pattern.signal` | `{ tracker, barrierTags, route, pace, timestamp }` | Always fires alongside `plan.committed` — anonymized |

The critical events are `recalc.plan.committed` (the canonical "comeback" event — trigger coin and badge awards here) and `phit.pattern.signal` (the anonymized community-intelligence event — pipe to PHIT aggregate).

## The PHIT Loop

This is the strategic punchline. The Recalculate layer is the first place in the FFH platform where the *recovery* dimension is captured at scale. The Force Index measures Members × Actions. The Ripple Coefficient measures household behavior transmission. Recalculate adds a third measurement: **the resilience of the network to bring people back when they drift.**

Aggregate barrier signals piped to PHIT enable civic-planning queries like:

- "Top 5 barriers in zip 17601 for movement goals, last 30 days"
- "Recalibration rate by tracker, by quarter"
- "Communities where `place.safety` is the dominant barrier" → walkability advocacy signal
- "Communities where `resources.shoes` is the dominant barrier" → shoe-drive partnership target

These are exactly the kinds of insights Geisinger Health Plan, Lisa Davis at Penn State Office of Rural Health, and the Arizona / Pennsylvania Rural Health Transformation Program awards need to see. The Recalculate signal turns individual drift into community intelligence and grant-ready evidence. When Rob talks about Recalculate to funders or partners, emphasize this loop.

## Default Drift Thresholds

These are the working defaults until Lucy specifies otherwise per program:

| Tracker family | Threshold (days) |
|---|---|
| Daily movement goals | 7 |
| Sleep / mental wellness check-ins | 10 |
| Weekly health-is-wealth check-ins | 14 |
| Quarterly preventive screenings | 90 |
| Annual physical / wellness visit | 365 |

When registering a new goal, pick the closest reasonable threshold. Err on the side of patience — a banner that fires too often feels nagging, not warm.

## Coach Lucy Voice Rules

Every piece of copy in the diagnostic flow is in Coach Lucy's voice, which means: warm, non-judgmental, never preachy, never clinical, never alarmist. Drift is acknowledged kindly. The user is never "behind" or "off-track" or "failing." They are simply "drifted" and the system is here to "find a new route."

When authoring or revising copy for the Recalculate layer:

- Lead with the human, not the metric. "Hey friend, no judgment at all" before any data.
- Frame barriers as information, not weakness. "Pain is information, not weakness."
- Frame place barriers as community problems, not personal ones. "This isn't on you — it's on the place."
- Frame mental health barriers as real health. "Mental wellness is health. This counts too."
- Always offer a "Something else" or "Not sure" option at every step. Never force the user to misclassify themselves.
- Never name a clinical condition (depression, anxiety disorder, etc.) — describe the experience instead ("low mood," "anxious feelings").
- For motivation.lowmood and mood.hopeless paths, always include a resource that points to mental health crisis information. The user may be telling the layer something they have not told anyone else.

When in doubt, ask: would this sound like Mr. Rogers if Coach Lucy were saying it? If not, soften it.

## Comebacks, Not Streaks — Core Design Principle

**Never display streak metrics in any Recalculate UI surface.** Every other tracker on the internet shows streaks; FFH does not. The streak is the shame mechanic that drives users to quit when they fall off — it tells them their accumulated work has been erased. The Recalculate layer was built to invert that pattern.

In place of streaks, the layer surfaces a **comeback counter** — the number of times a user has recalibrated this specific goal. A first comeback reads "Your first comeback — the start of a resilience pattern." Subsequent comebacks read "Comeback #N for this goal. Comebacks count more than streaks." This is operationalized in the engine via `getGoalState(goalId).comebacks` and incremented on every `recalc.plan.committed`.

When building any new FFH UI that involves the Recalculate layer or any tracked goal:

- Do not add streak displays, streak counters, streak-broken animations, or streak-protection mechanics.
- If a host page already shows a streak, the Recalculate layer's banner gently overrides it — the banner appears below or above the streak, not next to it, so the streak's "0" or broken-icon state is not the dominant visual.
- The Comeback Coin economy and the Recalibrated badge are the rewards. Streak-recovery mechanics from other platforms (streak freezes, streak repair, etc.) are explicitly not part of the FFH model. We do not protect streaks. We celebrate comebacks.

This principle was set on May 16, 2026 when Rob asked what the life-ring symbol meant in the early standalone Power Tool. The original design used "Streak: — 🛟" as a soft signal that the streak was "paused." It failed because it read as ambiguous. The replacement — a Comebacks counter — is unambiguous, strategically aligned with the platform's resilience-measurement story, and now permanent.

When Rob talks to funders, partners, or board about the Recalculate layer, the Comebacks-not-Streaks principle is part of the differentiation story: *Every health tracker on Earth measures streaks. We measure comebacks. That is the difference between a tracker that punishes drift and a network that builds resilience.*

## Mental Health Safety Pathway

The `mind` profile includes barriers that may surface genuine mental health crises — particularly `mood.hopeless`, `anxiety.avoid`, and any `motivation.lowmood` path in the movement profile. For these specific paths, the resource list always includes:

- "Mental health crisis resources"
- "Suicide prevention info"
- "Talk to someone now"

These resources should link to real, current crisis support — the 988 Suicide and Crisis Lifeline, Crisis Text Line, and FFH-vetted local resources. When updating the resource catalog, **never remove or downgrade the crisis pathway from these specific barrier combinations.** This is a non-negotiable safety property of the layer.

If Rob wants to extend the safety pathway (additional crisis resources, regional helplines, integration with his Mental Health First Aid framework), edit the relevant entries in the `resourceMap` object in `recalculate-engine.js` or extend via `FFH.Recalculate.addResources()`.

## Awards and Gamification

Standard awards (suggested defaults, configurable via Composer):

| Trigger | Award |
|---|---|
| `recalc.plan.committed` | +25 Comeback Coins, Recalibrated badge (once per goalId per user) |
| `recalc.shared` | +10 Coins, +1 Ripple credit |
| `recalc.action.completed` (done=true) | +5 Coins per action |

The Recalibrated badge is intentionally available once per goal, not once per user, so a person who has recalibrated walking, sleep, and money each gets the badge three times — visible in their profile as evidence of resilience across domains. This compounds the message: comebacks count.

In the Reality Health Games and Prevention Bingo systems, a Recalculate event can also be its own bonus square or its own "Comeback Quest" challenge. Talk to Lucy before adding gamification surfaces that depend on Recalculate, since they tie into the broader Bingo and Quest economies.

## When To Build A Standalone Recalculate Page

The standalone page (`FFH-Power-Tool-Recalculate.html`) is for cases where the user actively chooses to recalculate without waiting for a drift trigger. Build a standalone page when:

- Rob or Lucy wants a shareable URL for marketing or partner demos
- A new program launch wants Recalculate as an onboarding feature
- A grant narrative or pitch deck needs a clickable demo
- A specific tracker wants a "Recalculate now" button that opens a full page instead of the inline sheet

For all other cases — and this is the vast majority — use the layer in inline mode. The inline experience is faster, less disruptive, and meets the user where they already are.

## Storage and Backend Swap

Storage uses `localStorage` under the key `ffh.recalc.v1` and `sessionStorage` for banner dismissals. The schema:

```json
{
  "walk-30x5": {
    "lastActionISO": "2026-05-08T14:22:11.000Z",
    "drifts": 2,
    "comebacks": 1,
    "lastSession": { "...plan.committed payload..." }
  }
}
```

When Pace wires the production backend, replace the four storage helpers at the top of the engine (`readStore`, `writeStore`, `getGoalState`, `setGoalState`) with API calls. The rest of the engine never touches storage directly, so nothing else changes.

## Testing Helpers

Three private functions exist for demos and testing. Guard them behind a flag in production:

```js
FFH.Recalculate._simulateDrift(goalId, days)   // backdates lastActionISO, fires drift
FFH.Recalculate._clearDismissed(goalId)        // clears the session-dismiss flag
FFH.Recalculate._reset()                       // wipes all stored state
```

These are how the demo page "Simulate drift" buttons work. Useful for showing the flow to Lucy, Rob, partners, and funders without waiting for real time to pass.

## File Locations

When working on Recalculate, expect to find:

- `recalculate-engine.js` — at the FFH Academy root or in `/Power Tools/Recalculate/`
- `recalculate-ui.css` — same folder as the engine
- `recalculate-demo.html` — split-file version, same folder
- `recalculate-demo-allinone.html` — self-contained version for phone and embed testing
- `INTEGRATION-GUIDE.md` — same folder
- `FFH-Power-Tool-Recalculate.html` — the standalone destination page, at the FFH Academy root
- Deploy-ready copies under `FFH Academy/Deploy Ready/recalculate-layer/` and `FFH Academy/Deploy Ready/recalculate-power-tool/`

## How To Use This Skill

When starting a session involving Recalculate work:

1. Read this skill first to load the architecture, conventions, and integration patterns.
2. Use the `ffh-academy-dev` skill alongside this one when integrating Recalculate into a specific host page (Bingo card, profile page, tracker, etc.).
3. Use the `ffh-coach-lucy` skill when authoring or revising any user-facing copy in the diagnostic flow. The voice rules above are a summary; the Coach Lucy skill has the full persona.
4. Use the `ffh-nav-bar` skill when building a standalone Recalculate destination page that needs the standard FFH header.
5. Use the `deploy-ready` skill when packaging the layer for the FFH Vercel + GitHub pipeline.

When Rob or Lucy asks to "add Recalculate to [some tracker]," the answer is almost always: (a) include the two layer files, (b) call `register` and `watch`, (c) call `logAction` from the tracker's existing log-progress code path. Resist the urge to write a custom integration — the whole point of the layer is that the integration is two lines.

When Rob or Lucy asks to "extend Recalculate" (new barrier profile, new resources, new gamification surface), make the change inside the engine's clearly-marked extension points (`barrierProfiles` object, `resourceMap` object, public `addResources` API). Never branch the engine into per-tracker variants.

## Appendix: Inlining Pattern for the All-in-One Demo

When building or rebuilding `recalculate-demo-allinone.html` from the split-file sources, the engine contains literal `</script>` strings inside its top-of-file docstring (as integration examples for developers). These must be escaped before inlining, or the browser's HTML parser will close the inline script tag early and the engine will silently fail to load. Python build pattern:

```python
with open('recalculate-engine.js') as f: engine = f.read()
with open('recalculate-ui.css')   as f: css    = f.read()
with open('recalculate-demo.html') as f: html  = f.read()

# Escape </script -> <\/script inside the engine
engine_safe = engine.replace('</script>', '<\\/script>').replace('</script', '<\\/script')

# Replace external includes with inline blocks
html = html.replace(
  '<link rel="stylesheet" href="recalculate-ui.css">\n<script src="recalculate-engine.js"></script>',
  f'<style>\n{css}\n</style>\n<script>\n{engine_safe}\n</script>'
)

with open('recalculate-demo-allinone.html','w') as f: f.write(html)
```

The same pattern applies to any future LearnDash widget, email preview, or embed context where the layer needs to live in a single file.

## Appendix: The Strategic Narrative

When Rob discusses Recalculate with funders, partners, or board, frame it this way:

> Most health platforms measure two things: who showed up, and what they did. We measure a third thing nobody else captures: **what happens when they fall off — and how the network brings them back.** Drift is universal. Recovery is the real skill of a healthy community. Every other health tracker on the planet measures streaks — the shame mechanic that punishes people for falling off and erases their accumulated work. We measure comebacks. The Recalculate layer turns every individual setback into community intelligence: where the barriers are, which neighborhoods need different resources, and which programs are actually building resilience versus just measuring streaks. That data, captured ethically and anonymously through our PHIT data governance framework, is what civic planners and health plans have been missing. The Force Index tells you reach. The Ripple Coefficient tells you transmission. Recalculate tells you resilience. Together they describe a healthy community.

That paragraph is the elevator pitch. Memorize it.
