#!/usr/bin/env python3
"""Render /succession/index.html from measure.json and data/collapse-events.csv.

Every figure on the page is read from those two files, so the page cannot say a number the
build did not produce. Prose lives here; numbers do not.

    python3 render.py            # writes ../index.html
"""
import csv, html, json, os

HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = os.path.dirname(HERE)
M = json.load(open(os.path.join(ROOT, 'measure.json')))
EV = list(csv.DictReader(open(os.path.join(ROOT, 'data', 'collapse-events.csv'))))
GOM, BO, MA = 'Gates of Mercy', 'Beth Oloth Charitable Organization', 'Moral Arc Foundation'


def d(n): return f'${n:,.0f}'
def mm(n, p=1): return f'${n / 1e6:,.{p}f}M'
def pct(x, p=1): return f'{x * 100:.{p}f}%'
def e(s): return html.escape(str(s))


T = M['test']; N = T['named']; T50 = T['tiers']['50']
F1, F2 = M['first_handoff'], M['second_handoff']
AF = M['after_revocation']; H = M['heir']; LR = M['last_return']; B = M['boards']; R = M['reconciliation']
CC = T['common_cause']
tr = M['transfers']
gom_to_bo = sum(t['dollars'] for t in tr if t['giver'] == GOM and t['receiver'] == BO)
bo_to_gom = sum(t['dollars'] for t in tr if t['giver'] == BO and t['receiver'] == GOM)
ma_to_gom = sum(t['dollars'] for t in tr if t['giver'] == MA and t['receiver'] == GOM)
bo_after = AF[BO]['wholly_after']; gom_after = AF[GOM]['wholly_after']
bad = R['mismatches'][0]
all3 = B['on_all_three'][0]


# ------------------------------------------------------------------ chart 1: incoming gifts, three panels, one scale
def incoming_chart():
    years = list(range(2003, 2025))
    peak = max(v['dollars'] for c in M['incoming'].values() for v in c.values())
    W, Hh, padL, padR = 440, 150, 40, 6
    bw = (W - padL - padR) / len(years)
    rev = {BO: 2019, GOM: 2019}
    out = []
    for name in (GOM, BO, MA):
        series = M['incoming'][name]
        bars = []
        for i, y in enumerate(years):
            x = padL + i * bw
            if y == 2011:
                bars.append(f'<rect class="gap" x="{x + 1:.1f}" y="16" width="{bw - 2:.1f}" height="{Hh - 36}"><title>2011 — CRA published no qualified-donee file for this year</title></rect>')
                continue
            v = series.get(str(y), {'dollars': 0, 'donors': 0})
            h = (Hh - 36) * v['dollars'] / peak
            if v['dollars'] <= 0: continue
            bars.append(f'<g class="bar"><rect class="hit" x="{x:.1f}" y="0" width="{bw:.1f}" height="{Hh - 16}"/>'
                        f'<rect class="m" x="{x + 1:.1f}" y="{Hh - 16 - max(h, 1.5):.1f}" width="{bw - 2:.1f}" height="{max(h, 1.5):.1f}" rx="2"/>'
                        f'<title>{y}: {d(v["dollars"])} from {v["donors"]} registered charities</title></g>')
        if name in rev:
            x = padL + years.index(rev[name]) * bw
            bars.append(f'<line class="rev" x1="{x:.1f}" x2="{x:.1f}" y1="16" y2="{Hh - 16}"/><text class="revt" x="{x - 3:.1f}" y="12" text-anchor="end">revoked</text>')
        ticks = ''.join(f'<text class="yr" x="{padL + i * bw + bw / 2:.1f}" y="{Hh - 3}">{str(y)[2:]}</text>' for i, y in enumerate(years) if y % 4 == 3)
        best = max(series.items(), key=lambda kv: kv[1]['dollars'])
        out.append(f'''<figure class="panel"><figcaption><b>{e(name)}</b><span>peak {mm(best[1]['dollars'])} in {best[0]}</span></figcaption>
<svg viewBox="0 0 {W} {Hh}" role="img" aria-label="{e(name)}: gifts received from registered charities by year, 2003 to 2024, on a shared scale. Peak {mm(best[1]['dollars'])} in {best[0]}.">
<line class="base" x1="{padL}" x2="{W - padR}" y1="{Hh - 16}" y2="{Hh - 16}"/>
<text class="ax" x="{padL - 6}" y="{Hh - 16}">$0</text><text class="ax" x="{padL - 6}" y="24">{mm(peak, 0)}</text>
{''.join(bars)}{ticks}</svg></figure>''')
    rows = []
    for y in years:
        cells = []
        for name in (GOM, BO, MA):
            v = M['incoming'][name].get(str(y))
            cells.append('<td class="num">no file</td>' if y == 2011 else f'<td class="num">{d(v["dollars"]) if v else "—"}</td><td class="num">{v["donors"] if v else "—"}</td>')
        if y == 2011: cells = ['<td class="num" colspan="6">CRA published no qualified-donee file for 2011</td>']
        rows.append(f'<tr><td class="act">{y}</td>{"".join(cells)}</tr>')
    table = f'''<details class="tview"><summary>Show the numbers as a table</summary><div class="twrap"><table>
<caption>Gifts received from registered charities, read from the donors' own returns (T1236)</caption>
<thead><tr><th>Year</th><th class="num">{GOM} $</th><th class="num">donors</th><th class="num">Beth Oloth $</th><th class="num">donors</th><th class="num">Moral Arc $</th><th class="num">donors</th></tr></thead>
<tbody>{''.join(rows)}</tbody></table></div></details>'''
    return '<div class="panels">' + ''.join(out) + '</div>' + table


# ------------------------------------------------------------------ chart 2: every big collapse, one dot each
def strip_chart():
    big = [r for r in EV if int(r['funders']) >= 50]
    W, Hh, padL, padR = 440, 196, 12, 16
    X = lambda s: padL + (W - padL - padR) * s / 0.7
    BASE = 136
    med = T50['median_share']
    dots = []
    lanes = {}
    for r in sorted(big, key=lambda r: float(r['share'])):
        s = float(r['share']); k = round(s * 28)
        lane = lanes.get(k, 0); lanes[k] = lane + 1
        cy = BASE - 12 - lane * 17
        named = bool(r['named_case'])
        lab = f"{r['named_case']}, {r['collapse_year']}" if named else f"A collapse in {r['collapse_year']}"
        dots.append(f'<g class="dot{" hot" if named else ""}"><circle class="hit" cx="{X(s):.1f}" cy="{cy}" r="11"/><circle class="m" cx="{X(s):.1f}" cy="{cy}" r="{8 if named else 6}"/>'
                    f'<title>{e(lab)}: {r["adopters_of_heir"]} of {r["funders"]} funders ({pct(s)}) began funding one new recipient</title></g>')
    named = next(r for r in big if r['named_case'])
    ticks = ''.join(f'<line class="tk" x1="{X(t):.1f}" x2="{X(t):.1f}" y1="{BASE}" y2="{BASE + 5}"/><text class="yr" x="{X(t):.1f}" y="{BASE + 19}">{int(t * 100)}%</text>' for t in (0, .2, .4, .6))
    return f'''<figure class="strip"><svg viewBox="0 0 {W} {Hh}" role="img" aria-label="{len(big)} collapses with 50 or more funders. Median share moving to one recipient {pct(med)}. Beth Oloth to Moral Arc, {pct(float(named['share']))}, is the highest.">
<line class="base" x1="{padL}" x2="{W - padR}" y1="{BASE}" y2="{BASE}"/>{ticks}
<line class="med" x1="{X(med):.1f}" x2="{X(med):.1f}" y1="14" y2="{BASE}"/><text class="medt" x="{X(med) + 5:.1f}" y="14">median {pct(med)}</text>
{''.join(dots)}
<text class="hott" x="{X(float(named['share'])) + 8:.1f}" y="{BASE - 34}" text-anchor="end">Beth Oloth → Moral Arc</text>
<text class="cap" x="{padL}" y="{BASE + 40}">Share of funders who moved to the same new recipient</text>
</svg></figure>'''


transfer_rows = ''.join(f'<tr><td class="act">{t["year"]}</td><td>{e(t["giver"])}</td><td>{e(t["receiver"])}</td><td class="num">{d(t["dollars"])}</td></tr>' for t in tr)
heir_rows = ''.join(f'<tr><td class="act">{y}</td><td class="num">{d(H["revenue_by_year"][y])}</td><td class="num">{d(H["abroad_by_year"][y])}</td><td class="num">{(str(H["admin_share_by_year"][y]) + "%") if y in H["admin_share_by_year"] else "—"}</td></tr>' for y in sorted(H['revenue_by_year']))
cs = H['country_share_2024']
AL = B['arm_flags']

PAGE = f'''<!doctype html>
<html lang="en-CA">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>The Succession — one revocation, {N['funders']} funders, one heir | Follow the Money</title>
<meta name="description" content="When CRA revoked Beth Oloth Charitable Organization in January 2019, {N['adopters']} of the {N['funders']} registered charities that funded it began funding one foundation within two years. Tested against every charity collapse in 25 years of CRA releases, it is the largest coordinated move on record.">
<link rel="canonical" href="https://followthemoney.felineunion.org/succession/">
<meta name="robots" content="index,follow,max-image-preview:large">
<meta name="theme-color" content="#0d1b2e">
<meta name="color-scheme" content="dark">
<meta property="og:type" content="article">
<meta property="og:site_name" content="Follow the Money">
<meta property="og:title" content="The Succession — one revocation, {N['funders']} funders, one heir">
<meta property="og:description" content="{N['adopters']} of {N['funders']} funders moved to one foundation after a revocation. Ranked against every collapse in 25 years of CRA data, nothing else comes close. The Act guards a revoked charity's assets and its directors. It says nothing about its donors.">
<meta property="og:url" content="https://followthemoney.felineunion.org/succession/">
<meta property="og:image" content="https://followthemoney.felineunion.org/succession/share/og-succession.png">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:type" content="image/png">
<meta property="og:locale" content="en_CA">
<meta property="og:image:alt" content="A broadcast card reading THE SUCCESSION: {N['adopters']} of {N['funders']} funders, one heir.">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="The Succession — {N['adopters']} of {N['funders']} funders, one heir">
<meta name="twitter:description" content="The Act guards a revoked charity's assets and its directors. It says nothing about its donors. Measured on 25 years of CRA releases.">
<meta name="twitter:image" content="https://followthemoney.felineunion.org/succession/share/og-succession.png">
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png">
<link rel="manifest" href="/site.webmanifest">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=IBM+Plex+Mono:wght@400;500;600&family=Source+Serif+4:ital,opsz,wght@0,8..60,300;0,8..60,400;0,8..60,600;1,8..60,400&display=swap">
<link rel="stylesheet" href="/ledger/ledger.css?v=2">
<link rel="stylesheet" href="/channel/channel.css?v=1">
<link rel="stylesheet" href="/succession/succession.css?v=3">
<link rel="stylesheet" href="/assets/broadcast.css?v=3">
<script defer src="/assets/broadcast.js?v=6"></script>
<script type="application/ld+json">
{json.dumps({"@context": "https://schema.org", "@type": "Dataset", "name": "The Succession — funder movement after Canadian charity revocations, 2000–2024",
  "description": f"Every collapse in charity-to-charity funding in the CRA List of charities releases {M['generated_from'][-9:]}, scored by how many of the collapsed charity's funders began funding one new recipient. Includes the reconciliation of Howe & Sylvestre (2022) Tables 1–3 against the T3010.",
  "url": "https://followthemoney.felineunion.org/succession/", "inLanguage": "en-CA", "isAccessibleForFree": True,
  "isBasedOn": ["https://open.canada.ca/data/en/dataset/80c00cdb-1358-415c-bb8b-0de7f12675b8", "https://doi.org/10.1177/2631309X221138974"],
  "distribution": [{"@type": "DataDownload", "encodingFormat": "application/json", "contentUrl": "https://followthemoney.felineunion.org/succession/measure.json"},
                   {"@type": "DataDownload", "encodingFormat": "text/csv", "contentUrl": "https://followthemoney.felineunion.org/succession/data/collapse-events.csv"}],
  "creator": {"@type": "Organization", "name": "Feline Union"}}, ensure_ascii=False)}
</script>
</head>
<body>
<a class="skip" href="#test">Skip to the test</a>

<header class="topbar">
  <a class="netid" href="/">FTM<small>FOLLOW<br>THE MONEY</small></a>
  <span class="slug">THE SUCCESSION · T1236 · T3010 · ITA s.188 · s.149.1</span>
  <a class="up" href="/">The argument</a>
</header>

<div class="mast">
  <p class="kicker"><b>THE HEIR</b> <span>One revocation · {N['funders']} funders · one new home</span></p>
  <h1>The Succession</h1>
  <p class="dek">The Channel ended on a question it could not answer from its own five years of data:
  after CRA revokes a charity, do the charities that funded it move to one particular new organisation
  more than they move anywhere else? Twenty-five years of CRA releases answer it. When
  <b>Beth Oloth Charitable Organization</b> was revoked in January 2019, <b>{N['adopters']} of the
  {N['funders']}</b> registered charities that had funded it began funding <b>one</b> foundation within
  two years &mdash; a foundation that had reported {d(H['dormant_assets'][0])} in assets for years. Scored
  against every comparable collapse in the country, nothing else comes close. The Act guards a revoked
  charity's assets and its directors. <b>It has no rule about its donors.</b></p>
  <button class="hero-listen" data-listen type="button">&#9673; Listen &mdash; four voices, twelve chapters</button>
</div>

<div class="counters">
  <div><p class="n">{N['adopters']}<small>/{N['funders']}</small></p><p class="k">Funders who followed to one heir</p></div>
  <div><p class="n">1<small>st</small></p><p class="k">Of {T50['events']} collapses with 50+ funders &middot; median {pct(T50['median_share'], 0)}</p></div>
  <div><p class="n">{mm(H['abroad_2019_24'])}</p><p class="k">Sent abroad by the heir, 2019&ndash;2024</p></div>
  <div><p class="n">{d(bo_after['dollars'])}</p><p class="k">Reported as gifts to Beth Oloth after it stopped being a charity</p></div>
</div>

<nav class="destsw" aria-label="Related pages"><span class="lbl">Worked examples</span><a href="/ledger/"><b>The Ledger</b> Bill C-44, law to outcome</a><a href="/channel/"><b>The Channel</b> One flow, four doorways</a><span aria-current="page"><b>The Succession</b> One revocation, one heir</span></nav>

<!-- ================================================================ -->
<section id="paper" data-chapter="0">
<div class="seghead"><span class="tag">The study it starts from</span>
<h2>Three charities, one pattern, no control group</h2>
<p class="sub">In 2022 Miles Howe and Paul Sylvestre, writing in the <i>Journal of White Collar and
Corporate Crime</i>, tracked three Toronto charities across twenty years of CRA data and called
the pattern a &ldquo;burner charity&rdquo;: a dormant registration activated, run at scale, revoked,
and replaced by the next one, with the same funders moving each time.</p></div>

<div class="find">
  <h3>What the record established before this page</h3>
  <div class="split">
    <div class="half"><p class="lab">CRA's own findings</p>
      <p><b>Beth Oloth</b> was revoked effective <b>12 January 2019</b> and <b>Gates of Mercy</b>
      effective <b>13 April 2019</b>, each by notice in the <i>Canada Gazette</i>. CRA's case against
      Beth Oloth, released in a 94-page letter, cited a &ldquo;lack of direction and control&rdquo;
      over money sent to non-qualified donees, support for Israel's armed forces through pre-army
      academies, about $1.2 million to projects in the occupied territories, and inadequate books and
      records. Neither charity appealed, the study reports.</p></div>
    <div class="half"><p class="lab">What the study could not show</p>
      <p>That the funders' movement was unusual. It picked its &ldquo;anchor foundations&rdquo;
      because they gave across all three charities, then cited that consistency as the evidence. It
      never asked how often funders move together after <em>any</em> collapse. That is the test The
      Channel listed as its sixth unfinished join.</p></div>
  </div>
  <p class="join">SOURCES · CANADA GAZETTE PART I, 12 JAN 2019 &amp; 13 APR 2019 · THE CJN, 29 JAN 2019 · HOWE &amp; SYLVESTRE (2022)</p>
</div>

<div class="blk" style="margin-top:1rem">
<h3>Their tables, checked cell by cell against the T3010</h3>
<dl>
  <dt>Revenue, expenditures and money sent abroad</dt>
  <dd><b>{R['exact']} of {R['cells']}</b> cells match CRA lines 4700, 5100 and 200 to the dollar.</dd>
  <dt>The one that does not</dt>
  <dd>Beth Oloth, {bad['year']} expenditures: the paper prints {d(bad['paper'])}; the return says
  {d(bad['cra'])}.</dd>
  <dt>The figure the study called inexplicable</dt>
  <dd>The study found the third charity's 2019 foundation gifts larger than its 2019 revenue. That is a
  calendar, not a mystery: its fiscal year ends on 31 May, most donors close theirs in December, so
  a donor's &ldquo;2019&rdquo; is mostly the recipient's 2020.</dd>
</dl>
<p class="csq">The study's numbers are sound. Its headline was not: the &ldquo;$400 million&rdquo; it
called unlawful includes the third charity, which it also wrote it &ldquo;cannot conclusively&rdquo;
call a conduit. <b>This page publishes only what the returns show.</b></p>
</div>
<div class="cite"><a href="https://doi.org/10.1177/2631309X221138974" target="_blank" rel="noopener">Howe &amp; Sylvestre, 2022, CC BY-NC 4.0</a><a href="https://www.gazette.gc.ca/rp-pr/p1/2019/2019-01-12/html/commis-eng.html" target="_blank" rel="noopener">Gazette, 12 Jan 2019</a><a href="https://www.gazette.gc.ca/rp-pr/p1/2019/2019-04-13/html/commis-eng.html" target="_blank" rel="noopener">Gazette, 13 Apr 2019</a><a href="https://thecjn.ca/?p=180998" target="_blank" rel="noopener">The CJN</a></div>
</section>

<!-- ================================================================ -->
<section id="chain" data-chapter="1">
<div class="seghead"><span class="tag">Three registrations, one audience</span>
<h2>The money arrived in turns</h2>
<p class="sub">What each charity received from other registered charities, year by year, read from the
<em>donors'</em> returns &mdash; so a charity that stopped filing still shows up. One scale across all
three panels. Tap or hover a bar for the year.</p></div>
{incoming_chart()}
<div class="cite"><span>CRA qualified-donee files (T1236), releases 2003&ndash;2024</span><span>Hatched: 2011, a year CRA published no gift file for</span><span>The third registration was named Jewish Heritage Foundation of Canada until CRA's 2017 release</span></div>
</section>

<!-- ================================================================ -->
<section id="test" data-chapter="2">
<div class="seghead"><span class="tag">The reading of the funder list</span>
<h2>Every collapse in the country, scored the same way</h2>
<p class="sub">The rule was fixed before the named charities were looked up, then run over all
{M['generated_from'][-9:]} of CRA's gift files. A <b>collapse</b> is any charity that had been receiving
at least $1 million a year from other charities and then lost at least half of it over the next two
years. Its <b>funders</b> are the charities that gave it $5,000 or more in the two years before. A funder
<b>follows</b> when it starts giving to a recipient it had not given to in the previous four years.</p></div>

{strip_chart()}

<div class="pair">
  <section><h3>Beth Oloth, 2019</h3><p class="j">The named case</p>
  <dl><dt>Funders before the revocation</dt><dd><b>{N['funders']}</b></dd>
      <dt>Began funding Moral Arc Foundation within two years</dt><dd><b>{N['adopters']}</b> &middot; {pct(N['share'])}</dd>
      <dt>The next most popular new recipient among the same funders</dt><dd>{N['runner_adopters']} funders</dd>
      <dt>Given to Moral Arc by those funders in those two years</dt><dd>{mm(N['heir_dollars_from_funders'])}</dd></dl>
  <p class="verdict no">FIRST OF {T50['events']} · {T['heir_to_runner_ratio']}&times; THE RUNNER-UP</p></section>
  <section><h3>Every other collapse</h3><p class="j">The control</p>
  <dl><dt>Collapses scored, all sizes</dt><dd><b>{T['events']}</b></dd>
      <dt>With 50 or more funders</dt><dd>{T50['events']} &middot; median share {pct(T50['median_share'])} &middot; 90th percentile {pct(T50['p90_share'])}</dd>
      <dt>Largest move to one recipient anywhere else</dt><dd>{T['next_largest_move']['adopters']} funders, out of {T['next_largest_move']['funders']}</dd>
      <dt>Typical heir against its runner-up</dt><dd>{T['median_ratio_50plus']}&times;</dd></dl>
  <p class="verdict ok">WHAT A COLLAPSE NORMALLY LOOKS LIKE</p></section>
</div>
<p class="note-line" style="margin-top:.8rem">The ranking holds at every size cut tested: first of {T['tiers']['30']['events']} collapses with 30+ funders, first of {T['tiers']['100']['events']} with 100+.</p>
</section>

<!-- ================================================================ -->
<section id="control" data-chapter="3">
<div class="seghead"><span class="tag">What a common cause looks like</span>
<h2>The other way funders move together</h2>
<p class="sub">Coordination is not the only thing that sends many funders to one place. A shared
outside event does it too, and the data shows exactly what that looks like.</p></div>
<div class="find">
  <h3>{CC['year']}&ndash;{CC['year'] + 1}: {e(CC['heir'].title().replace('Canada-Ukraine', 'Canada-Ukraine'))}</h3>
  <div class="split">
    <div class="half"><p class="lab">The shape</p>
      <p>It was the top new recipient for the funders of <b>{CC['events']}</b> unrelated collapses in
      the same window &mdash; a United Way, a university, a food bank network, a children's hospital
      foundation. Funders everywhere gave to Ukraine relief. The largest single move was
      <b>{CC['largest_single_move']}</b> funders.</p></div>
    <div class="half"><p class="lab">The difference</p>
      <p>A common cause pulls a few funders each out of many charities. The Beth Oloth move pulled
      <b>{N['adopters']}</b> funders out of <b>one</b>. Moral Arc was also the top new recipient for
      {len(T['heir_elsewhere'])} smaller collapses in the same years, at {min(x['adopters'] for x in T['heir_elsewhere'])} to
      {max(x['adopters'] for x in T['heir_elsewhere'])} funders each &mdash; the shape of a common cause, around one charity.</p></div>
  </div>
  <p class="join">A PATTERN IS ONLY A FINDING ONCE YOU KNOW WHAT THE BASELINE LOOKS LIKE</p>
</div>
</section>

<!-- ================================================================ -->
<section id="first" data-chapter="4">
<div class="seghead"><span class="tag">The first handoff</span>
<h2>Gates of Mercy to Beth Oloth: visible, and not rankable</h2>
<p class="sub">The study's first succession is real in the returns. It cannot be put through the same
test, for two reasons the page will not paper over.</p></div>
<div class="blk">
<h3>What the returns show</h3>
<dl>
  <dt>Charities that funded Gates of Mercy, 2008&ndash;2010</dt><dd>{F1['gom_funders_2008_10']}</dd>
  <dt>Charities that began funding Beth Oloth, 2012&ndash;2013</dt><dd>{F1['bo_new_funders_2012_13']}</dd>
  <dt>On both lists</dt><dd><b>{F1['overlap']}</b> &middot; {pct(F1['overlap'] / F1['bo_new_funders_2012_13'], 0)} of Beth Oloth's new funders, {mm(F1['overlap_dollars'])} of its {mm(F1['bo_new_dollars'])} in new money</dd>
</dl>
<p class="csq">It is not a collapse. Gates of Mercy kept operating, and kept receiving millions, until
2019; its funders moved while it was still open. And the move runs straight through <b>2011</b>, the one
year CRA's open release has no gift file for, so &ldquo;new in 2012&rdquo; cannot be told apart from
&ldquo;started in 2011&rdquo;. <b>Reported as a description, not ranked.</b></p>
</div>
</section>

<!-- ================================================================ -->
<section id="paid" data-chapter="5">
<div class="seghead"><span class="tag">The family accounts</span>
<h2>They paid each other, and said so on their own returns</h2>
<p class="sub">Every row below is a gift one of the three charities reported giving to another, on its
own qualified-donee schedule.</p></div>
<div class="twrap"><table>
<caption>Gifts among the three registrations, as reported by the giver</caption>
<thead><tr><th>Year</th><th>Giver</th><th>Receiver</th><th class="num">Amount</th></tr></thead>
<tbody>{transfer_rows}</tbody></table></div>
<div class="blk" style="margin-top:1rem">
<h3>Totals and boards</h3>
<dl>
  <dt>Gates of Mercy to Beth Oloth, 2012&ndash;2018</dt><dd><b>{d(gom_to_bo)}</b> &middot; and {d(bo_to_gom)} the other way</dd>
  <dt>Moral Arc to Gates of Mercy, fiscal year ending 31 May 2019</dt><dd><b>{d(ma_to_gom)}</b> &mdash; the fiscal year in which Gates of Mercy was revoked</dd>
  <dt>People on more than one of the three boards, 2000&ndash;2024</dt><dd>{B['shared_people']}</dd>
  <dt>On all three</dt><dd>One director: Beth Oloth {all3[BO][0]}&ndash;{all3[BO][1]}, Gates of Mercy {all3[GOM][0]}&ndash;{all3[GOM][1]}, the future Moral Arc {all3[MA][0]}&ndash;{all3[MA][1]}</dd>
  <dt>Names on Moral Arc's board from the 2018 release on that appear on either other board</dt><dd><b>{B['ma_overlap_from_2018']}</b></dd>
</dl>
<p class="csq">Since {AL['first_year_asked']}, when the return began asking, <b>{AL['flagged_Y']} of
{AL['director_years']}</b> director-years across the three boards are flagged &ldquo;at arm's
length&rdquo; &mdash; including every year one person sat on all three. The exception is
{AL['flagged_N'][0]['charity']}'s {AL['flagged_N'][0]['year']} return, which marks all
{AL['flagged_N'][0]['count']} of its directors as not. <b>The boards separated before the funders
arrived.</b> {F2['overlap']} of Moral Arc's {F2['ma_new_funders_2019_20']} new funders in
2019&ndash;2020 had funded Beth Oloth in 2017&ndash;2018, and they brought {pct(F2['overlap_dollars'] / F2['ma_new_dollars'], 0)} of its new money.
{M['funded_all_three']} charities funded all three registrations.</p>
</div>
</section>

<!-- ================================================================ -->
<section id="after" data-chapter="6">
<div class="seghead"><span class="tag">Gifts to a registration that no longer existed</span>
<h2>{d(bo_after['dollars'])} to Beth Oloth after the Gazette</h2>
<p class="sub">A revoked charity is no longer a qualified donee. Gifts to it kept appearing on other
charities' returns anyway.</p></div>
<div class="find">
  <h3>The schedule has a recipient, an amount, and no date</h3>
  <div class="split">
    <div class="half"><p class="lab">Placeable</p>
      <p>Fiscal years that <em>began</em> after 12 January 2019: <b>{bo_after['returns']}</b> returns
      from <b>{bo_after['donors']}</b> charities report <b>{d(bo_after['dollars'])}</b> to Beth Oloth,
      the last for a year ending {bo_after['last_fpe']}. Two charities account for nearly all of it,
      year after year. Gates of Mercy: {d(gom_after['dollars'])}, from {'one charity' if gom_after['donors'] == 1 else str(gom_after['donors']) + ' charities'}.</p></div>
    <div class="half"><p class="lab">Not placeable</p>
      <p>Fiscal years that <em>straddle</em> the revocation: {AF[BO]['straddling']['returns']} returns,
      {mm(AF[BO]['straddling']['dollars'])} to Beth Oloth; {AF[GOM]['straddling']['returns']} returns,
      {mm(AF[GOM]['straddling']['dollars'])} to Gates of Mercy. A gift on 2 January and a gift on
      2 December are the same row.</p></div>
  </div>
  <p class="join">THE FORM KNOWS THE RECIPIENT'S NUMBER · IT IS NEVER CHECKED AGAINST THE REGISTER</p>
  <p class="note">The donee's registration number sits on the row. The register of revoked charities is
  public. Matching one against the other at filing time would have flagged every placeable row. The file also carries negative &ldquo;gifts&rdquo; to Beth Oloth, which no form should accept.</p>
</div>
</section>

<!-- ================================================================ -->
<section id="law" data-chapter="7">
<div class="seghead"><span class="tag">The will</span>
<h2>The Act guards the estate and the executors. Not the mourners.</h2>
<p class="sub">When a charity is revoked, the <i>Income Tax Act</i> follows three things out of it.
Read the provisions in a row and the gap is plain.</p></div>
<ol class="spec">
  <li><h3>The assets &mdash; s.188</h3>
    <p>A revoked charity owes a revocation tax equal to what it holds, plus what it takes in while
    winding up, less debts, its own charitable spending, and transfers to an &ldquo;eligible
    donee&rdquo;. An eligible donee must have more than half its board at arm's length from the revoked
    charity's board (s.188(1.3)). The Act foresaw a related successor &mdash; for the property.</p>
    <p class="needs">At its last return: Beth Oloth {d(LR[BO]['assets'])} in assets ({LR[BO]['fpe']}) &middot; Gates of Mercy {d(LR[GOM]['assets'])} against {d(LR[GOM]['liabilities'])} in liabilities ({LR[GOM]['fpe']})</p></li>
  <li><h3>The people &mdash; s.149.1(1) and (4.1)(e)</h3>
    <p>A director who served while a charity &ldquo;engaged in conduct that can reasonably be considered
    to have constituted a serious breach&rdquo;, and was revoked for it, is an &ldquo;ineligible
    individual&rdquo; for five years. A charity with one on its board, or controlled or managed by one,
    can be revoked.</p>
    <p class="needs">Moral Arc's board, from the 2018 release on: <b>no name in common</b> with either revoked charity</p></li>
  <li><h3>Gifts between charities &mdash; s.149.1(4.1)(a) and (b)</h3>
    <p>A gift between charities whose purpose is &ldquo;to avoid or unduly delay the expenditure of
    amounts on charitable activities&rdquo; is a ground for revocation, for both sides. It is written
    against parking money, not against moving it.</p>
    <p class="needs">Aimed at <b>delay</b>, not at destination</p></li>
  <li><h3>The funders &mdash; nothing</h3>
    <p>No provision addresses where a revoked charity's donors give next. A pass-through charity holds
    almost nothing, so the asset rule bites on almost nothing. Its directors can step off a board years
    before the revocation. <b>What it actually has is its funder list, and the funder list walks.</b></p>
    <p class="needs">The one asset of a conduit is <b>the one thing the Act does not follow</b></p></li>
</ol>
<div class="cite"><a href="https://laws-lois.justice.gc.ca/eng/acts/I-3.3/section-188.html" target="_blank" rel="noopener">ITA s.188</a><a href="https://laws-lois.justice.gc.ca/eng/acts/I-3.3/section-149.1.html" target="_blank" rel="noopener">ITA s.149.1</a><span>Consolidated text, read 23 October 2026</span></div>
</section>

<!-- ================================================================ -->
<section id="heir" data-chapter="8">
<div class="seghead"><span class="tag">The heir, six years on</span>
<h2>{mm(H['revenue_2019_24'])} in, {mm(H['abroad_2019_24'])} out, in six years</h2>
<p class="sub">Moral Arc Foundation is a registered public foundation, listed in CRA's 2024 release. CRA
has published no finding against it. These are its own returns since the funders arrived.</p></div>
<div class="twrap"><table>
<caption>Moral Arc Foundation (BN 118974294RR0001), T3010 as filed</caption>
<thead><tr><th>Fiscal year ending 31 May</th><th class="num">Revenue (4700)</th><th class="num">Spent outside Canada (200)</th><th class="num">Management &amp; admin share</th></tr></thead>
<tbody>{heir_rows}</tbody></table></div>
<div class="blk" style="margin-top:1rem">
<h3>Scale against the two it followed</h3>
<dl>
  <dt>Sent outside Canada, line 200</dt><dd>Gates of Mercy {mm(M['abroad'][GOM])} &middot; Beth Oloth {mm(M['abroad'][BO])} &middot; <b>Moral Arc {mm(M['abroad'][MA])}</b></dd>
  <dt>Intermediaries itemised on its 2024 return</dt><dd><b>{H['intermediaries_2024']}</b> &middot; Israel {cs['IL']}% &middot; United States {cs['US']}% of the amount</dd>
  <dt>Before 2019</dt><dd>Total assets of {d(H['dormant_assets'][0])}, return after return</dd>
</dl>
<p class="csq">Nearly twice what Beth Oloth moved in its whole life, in six fiscal years, with management and
administration at a fraction of one percent. Whether that spending is under the &ldquo;direction and
control&rdquo; the Act requires is a question only an audit answers. <b>What the public file shows is
that the audience inherited the stage.</b></p>
</div>
</section>

<!-- ================================================================ -->
<section id="dropped" data-chapter="9">
<div class="seghead"><span class="tag">A test we built and dropped</span>
<h2>The mirror test made everything look like a handoff</h2>
<p class="sub">The obvious second test runs the logic backwards. It failed, and why it failed is the
most likely mistake for anyone repeating this.</p></div>
<div class="correction">
  <h3>The test that looked right</h3>
  <div class="body">
    <p class="tempting">&ldquo;For every charity whose funding surged from nothing to millions, find the
    one charity whose funder list supplied most of its new funders.&rdquo;</p>
    <p>It ran over {M['mirror_test']['surges']} surges. The <b>median</b> surge scored
    {pct(M['mirror_test']['median_share'], 0)}: more than half of almost any new charity's funders already
    gave to <em>some</em> large, popular charity &mdash; a university, the Salvation Army, a hospital
    foundation &mdash; so every surge appeared to have a &ldquo;source&rdquo;. A test that convicts the
    median is not a test.</p>
    <p>It also could not score the named charities at all: their surges sit next to the missing 2011
    file, or follow years of small receipts. <b>Dropped.</b> The collapse test above survives because it
    starts from one charity's own funders and asks where <em>they</em> went, which a popular bystander
    cannot fake.</p>
  </div>
</div>
</section>

<!-- ================================================================ -->
<section id="spec" data-chapter="10">
<div class="seghead"><span class="tag">What would close it</span>
<h2>Five joins, and what each one needs</h2>
<p class="sub">Every field below already exists in a file CRA publishes.</p></div>
<ol class="spec">
  <li><h3>Flag the heir</h3>
    <p>When a revoked charity's funders move, in numbers, to one recipient, review the recipient. The
    test on this page ran in under three minutes over every gift file CRA has published.</p>
    <p class="needs">Needs <b>a join CRA can already run</b> &mdash; the code is linked below</p></li>
  <li><h3>Check the donee against the register at filing</h3>
    <p>The qualified-donee schedule carries the recipient's registration number. Refuse a gift row whose
    number belongs to a revoked charity, and refuse a negative gift outright.</p>
    <p class="needs">Needs <b>a validation rule at submission</b></p></li>
  <li><h3>Date the gift</h3>
    <p>A schedule with no date cannot say whether a gift was made before or after a revocation. One
    field ends the straddle.</p>
    <p class="needs">Needs <b>one column on T1236</b></p></li>
  <li><h3>Publish the missing year</h3>
    <p>The 2011 release has identification, financial and director files, and no gift file. It is the
    year a succession in this record passed through.</p>
    <p class="needs">Needs <b>a file CRA holds</b></p></li>
  <li><h3>Read the funder list the way the Act reads the board</h3>
    <p>s.188(1.3) already asks whether the receiving charity's board is at arm's length from the revoked
    one. The same question can be asked of the receiving charity's funders.</p>
    <p class="needs">Needs <b>a policy decision</b> &mdash; the only one of the five that does</p></li>
</ol>
</section>

<!-- ================================================================ -->
<section id="notfindings" data-chapter="11">
<div class="seghead"><span class="tag">What this is not</span>
<h2>The claims this page refuses to make</h2>
<p class="sub">Each is within reach of the data above. None follows from it.</p></div>
<div class="find">
  <h3>That Moral Arc Foundation is a conduit</h3>
  <div class="split">
    <div class="half"><p class="lab">Supported</p><p>That it received the largest coordinated funder move
    in CRA's record, from the funders of a charity revoked for lack of direction and control.</p></div>
    <div class="half"><p class="lab">Not supported</p><p>Anything about how it directs its own spending.
    That requires an audit. CRA has published none.</p></div>
  </div>
  <p class="join">INHERITING THE AUDIENCE IS NOT INHERITING THE FINDING</p>
</div>
<div class="find">
  <h3>That the move was planned</h3>
  <div class="split">
    <div class="half"><p class="lab">Supported</p><p>That it happened together, at a scale no other
    collapse approaches, to a registration with a shared history of boards and gifts.</p></div>
    <div class="half"><p class="lab">Not supported</p><p>Who decided it. Intent needs a record of the
    decision. The public files hold the result.</p></div>
  </div>
  <p class="join">A FUNCTIONAL READING · NOT A STATEMENT OF PURPOSE</p>
</div>
<div class="find">
  <h3>That any funder broke the law</h3>
  <div class="split">
    <div class="half"><p class="lab">What is published</p><p>Counts of funders, and dollars, exactly as
    their own returns report them.</p></div>
    <div class="half"><p class="lab">What is not</p><p>The funders' names. A gift to a registered charity
    is lawful; naming {N['adopters']} charities on a page about a revocation would imply what the data
    does not show.</p></div>
  </div>
  <p class="join">THE SUBJECT HERE IS THE FORM, NOT THE FILER</p>
</div>
</section>

<!-- ================================================================ -->
<section id="method">
<div class="seghead"><span class="tag">Method and limits</span>
<h2>How this was built</h2></div>
<div class="method">
<h3>What a machine did</h3>
<p>Every figure on this page is computed by <a href="/succession/build/measure.py">measure.py</a> from
CRA's annual <a href="https://open.canada.ca/data/en/dataset/80c00cdb-1358-415c-bb8b-0de7f12675b8" target="_blank" rel="noopener">List
of charities</a> releases, {M['generated_from'][-9:]}, and written to
<a href="/succession/measure.json">measure.json</a>. The page itself is rendered from that file by
<a href="/succession/build/render.py">render.py</a>, so it cannot state a number the build did not produce.</p>
<h3>The rule, as fixed</h3>
<p>A gift counts at $5,000 or more in a year, read from the giver's qualified-donee schedule and dated by
the release year of the giver's return. A collapse needs at least $1 million a year from charities before,
and half of it gone over the next two years; only the first year of a collapse is scored, and only if the
charity had at least ten funders. A follower is a funder that starts giving to a recipient it had not
given to in the previous four years. Thresholds were set before the named charities were scored.</p>
<h3>The missing year</h3>
<p>CRA's 2011 release contains no qualified-donee file. A missing year looks like a collapse, and a gift
made in 2011 looks like a new one in 2012, so <b>every event whose window touches 2011 is dropped</b>
rather than filled. That is why the first succession is described, not ranked.</p>
<h3>What a person did</h3>
<p>Matched the Gazette notices, read the Act, and checked the study's tables. Director names are
compared as written on the returns, which is the only identity the public file carries.</p>
<h3>The data and the code</h3>
<p><a href="/succession/measure.json">measure.json</a> holds every figure.
<a href="/succession/data/collapse-events.csv">collapse-events.csv</a> is all {T['events']} scored collapses
by size and share, with only the named case labelled.
<a href="/succession/data/three-charities.csv">three-charities.csv</a> is the three registrations' filed
lines, year by year. <a href="/succession/build/README.md">build/README.md</a> is the recipe.</p>
</div>
</section>

<!-- ================================================================ -->
<section id="share" class="share-seg">
  <div class="seghead">
    <span class="tag">Pass it on</span>
    <h2>Pick a card. Post it.</h2>
    <p class="sub">Tap a card, then choose where to post it. The link shows that card and opens the page at the part it is about.</p>
  </div>
  <div class="share-grid" id="shareGrid" data-cards="/succession/share/cards.json"
       data-url="https://followthemoney.felineunion.org/succession/"
       data-title="The Succession &mdash; {N['adopters']} of {N['funders']} funders, one heir"
       data-text="When CRA revoked a charity in 2019, {N['adopters']} of its {N['funders']} charity funders moved to one foundation. Against every collapse in 25 years of CRA data, nothing comes close. The Act guards a revoked charity's assets and directors, not its donors."
       data-tile-base="/succession/share"
       data-tile-captions="[&quot;{N['adopters']} of {N['funders']} funders, one heir&quot;,&quot;First of {T50['events']} collapses with 50+ funders&quot;,&quot;What a common cause looks like instead&quot;,&quot;They paid each other on their own returns&quot;,&quot;{d(bo_after['dollars'])} to a charity that no longer existed&quot;,&quot;The Act guards the assets and the directors&quot;,&quot;{mm(H['abroad_2019_24'])} abroad in five years&quot;,&quot;The test we dropped&quot;]">
    <a data-share="twitter" href="#" rel="noopener"><b>&#120143;</b><span>X / Twitter</span></a>
    <a data-share="bluesky" href="#" rel="noopener"><b>&#9729;</b><span>Bluesky</span></a>
    <a data-share="mastodon" href="#" rel="noopener"><b>&#9673;</b><span>Mastodon</span></a>
    <a data-share="facebook" href="#" rel="noopener"><b>f</b><span>Facebook</span></a>
    <a data-share="linkedin" href="#" rel="noopener"><b>in</b><span>LinkedIn</span></a>
    <a data-share="whatsapp" href="#" rel="noopener"><b>&#9711;</b><span>WhatsApp</span></a>
    <a data-share="telegram" href="#" rel="noopener"><b>&#10148;</b><span>Telegram</span></a>
    <a data-share="reddit" href="#" rel="noopener"><b>r/</b><span>Reddit</span></a>
    <a data-share="email" href="#"><b>&#9993;</b><span>Email</span></a>
    <button data-share="copy" type="button"><b>&#8865;</b><span>Copy link</span></button>
  </div>
  <p class="note-line">Eight share cards &middot; 1080&times;1080 &middot; tap to open, long-press or right-click to save</p>
  <ul class="tile-strip" id="tileStrip"></ul>
</section>

<footer>
  <span class="ftm">FTM</span>
  <span>The Succession · CRA releases {M['generated_from'][-9:]}</span>
  <a href="/">The argument</a>
  <a href="/ledger/">The Ledger</a>
  <a href="/channel/">The Channel</a>
  <a href="/succession/measure.json">measure.json</a>
  <a href="https://directory.felineunion.org/">Directory</a>
  <span>Feline Union</span>
</footer>

<aside class="narrator" id="narrator" aria-label="Spatial narration player"
       data-manifest="/succession/audio/manifest.json">
  <button class="listen" id="listen"><i aria-hidden="true"></i><span>LISTEN</span></button>
  <div class="nplayer" id="nplayer" hidden>
    <div class="nbar">
      <span class="onair"><i aria-hidden="true"></i>ON AIR</span>
      <span class="now"><small>NOW SPEAKING &middot; 4 VOICES IN SPACE</small><b id="trackTitle">&mdash;</b></span>
      <button id="flowBtn" aria-pressed="true" title="Follow the page as the narration moves">AUTO&nbsp;&darr;</button>
      <button id="chaptersBtn" aria-expanded="false" aria-controls="nchapters">CHAPTERS</button>
      <button id="closeBtn" aria-label="Close the narration player">&times;</button>
    </div>
    <p class="ncaption" id="ncaption" aria-live="polite"><span class="ncap-hint">Captions appear here as each voice speaks.</span></p>
    <div class="ncontrols">
      <button id="prevBtn" aria-label="Previous chapter">&#9664;&#9664;</button>
      <button id="playBtn" class="play" aria-label="Play or pause">&#9654;</button>
      <button id="nextBtn" aria-label="Next chapter">&#9654;&#9654;</button>
      <input id="seekBar" type="range" min="0" max="1000" value="0" aria-label="Narration progress">
      <time id="clockRead">0:00</time>
    </div>
    <ol class="nchapters" id="nchapters" hidden aria-label="Narration chapters"></ol>
  </div>
</aside>
</body>
</html>
'''
# ------------------------------------------------------------------ the art: hero behind the masthead, one plate per segment
import re
ART = json.load(open(os.path.join(HERE, 'art.json')))
def plate(p, cls='plate'):
    return (f'<figure class="{cls}"><img src="/succession/art/{p["file"]}" width="{p["w"]}" height="{p["h"]}" '
            f'loading="lazy" decoding="async" alt="{e(p["alt"])}"><figcaption>{p["label"]}</figcaption></figure>')
h = ART['hero']
PAGE = PAGE.replace('<div class="mast">', f'<div class="stage"><img class="stage-art" src="/succession/art/{h["file"]}" width="{h["w"]}" height="{h["h"]}" alt="" fetchpriority="high" decoding="async">\n<div class="mast">', 1)
PAGE = PAGE.replace('</div>\n\n<div class="counters">', '</div>\n</div>\n\n<div class="counters">', 1)
for sid, p in ART['plates'].items():
    m = re.search(rf'<section id="{sid}"[^>]*>\n(<div class="seghead">.*?</div>)', PAGE, re.S)
    assert m, sid
    PAGE = PAGE[:m.start(1)] + '<div class="segtop">' + m.group(1) + plate(p) + '</div>' + PAGE[m.end(1):]
if ART.get('og'):
    assert PAGE.count('/succession/share/og-succession.png') == 2
    PAGE = PAGE.replace('/succession/share/og-succession.png', '/succession/share/' + ART['og'])
    PAGE = PAGE.replace('content="A broadcast card reading THE SUCCESSION:', 'content="THE SUCCESSION over two sealed registry volumes whose pages stream into a third, lit one:')
cal = ART['extra']['paper_calendars']
blk = '<div class="blk" style="margin-top:1rem">\n<h3>Their tables, checked cell by cell against the T3010</h3>'
assert blk in PAGE
i = PAGE.index(blk); j = PAGE.index('</div>\n<div class="cite"><a href="https://doi.org', i) + len('</div>')
PAGE = PAGE[:i] + '<div class="segtop blkrow">' + PAGE[i:j] + plate(cal) + '</div>' + PAGE[j:]

open(os.path.join(ROOT, 'index.html'), 'w').write(PAGE)
print('wrote index.html', len(PAGE))
