Skip to content

Instantly share code, notes, and snippets.

@leonmak
Last active March 22, 2026 11:39
Show Gist options
  • Select an option

  • Save leonmak/9a3b8adc757bb8cf20152ded5c504cd3 to your computer and use it in GitHub Desktop.

Select an option

Save leonmak/9a3b8adc757bb8cf20152ded5c504cd3 to your computer and use it in GitHub Desktop.
Bayes one-pager
<html lang="en">
<head>
<style>
:root {
--bg: #ffffff;
--bg2: #f5f4f0;
--bg3: #eae9e4;
--tx: #1a1a18;
--tx2: #5f5e5a;
--tx3: #888780;
--bd: rgba(0, 0, 0, 0.12);
--bd2: rgba(0, 0, 0, 0.06);
--purple-50: #eeedfe;
--purple-100: #cecbf6;
--purple-200: #afa9ec;
--purple-400: #7f77dd;
--purple-600: #534ab7;
--purple-800: #3c3489;
--purple-900: #26215c;
--teal-50: #e1f5ee;
--teal-100: #9fe1cb;
--teal-200: #5dcaa5;
--teal-400: #1d9e75;
--teal-600: #0f6e56;
--teal-800: #085041;
--teal-900: #04342c;
--coral-50: #faece7;
--coral-100: #f5c4b3;
--coral-200: #f0997b;
--coral-400: #d85a30;
--coral-600: #993c1d;
--coral-800: #712b13;
--blue-50: #e6f1fb;
--blue-100: #b5d4f4;
--blue-200: #85b7eb;
--blue-400: #378add;
--blue-600: #185fa5;
--blue-800: #0c447c;
--amber-50: #faeeda;
--amber-100: #fac775;
--amber-200: #ef9f27;
--amber-400: #ba7517;
--amber-600: #854f0b;
--amber-800: #633806;
--green-50: #eaf3de;
--green-100: #c0dd97;
--green-200: #97c459;
--green-600: #3b6d11;
--green-800: #27500a;
--red-50: #fcebeb;
--red-100: #f7c1c1;
--red-200: #f09595;
--red-400: #e24b4a;
--red-600: #a32d2d;
--red-800: #791f1f;
--gray-50: #f1efe8;
--gray-100: #d3d1c7;
--gray-200: #b4b2a9;
--gray-400: #888780;
--gray-600: #5f5e5a;
--gray-800: #444441;
--pink-50: #fbeaf0;
--pink-100: #f4c0d1;
--pink-200: #ed93b1;
--pink-600: #993556;
--pink-800: #72243e;
--font:
"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--mono: "SF Mono", "Fira Code", "Consolas", monospace;
--radius: 8px;
--radius-lg: 12px;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a18;
--bg2: #2c2c2a;
--bg3: #3d3d3a;
--tx: #66614c;
--tx2: #b4b2a9;
--tx3: #888780;
--bd: rgba(255, 255, 255, 0.12);
--bd2: rgba(255, 255, 255, 0.06);
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font);
background: var(--bg);
color: var(--tx);
line-height: 1.7;
font-size: 15px;
}
.page {
max-width: 780px;
margin: 0 auto;
padding: 2rem 1.5rem 4rem;
}
h1 {
font-size: 28px;
font-weight: 600;
margin-bottom: 0.25rem;
letter-spacing: -0.5px;
}
h2 {
font-size: 20px;
font-weight: 500;
margin: 3rem 0 0.75rem;
padding-top: 2rem;
border-top: 0.5px solid var(--bd);
}
h3 {
font-size: 16px;
font-weight: 500;
margin: 1.5rem 0 0.5rem;
}
p,
.prose {
color: var(--tx2);
margin-bottom: 1rem;
}
.subtitle {
font-size: 15px;
color: var(--tx3);
margin-bottom: 2.5rem;
}
.formula-bar {
background: var(--bg2);
border-radius: var(--radius);
padding: 14px 20px;
font-family: var(--mono);
font-size: 15px;
margin: 1rem 0 1.5rem;
text-align: center;
border: 0.5px solid var(--bd2);
}
.formula-bar .hl-post {
color: var(--purple-600);
font-weight: 600;
}
.formula-bar .hl-like {
color: var(--teal-600);
font-weight: 600;
}
.formula-bar .hl-prior {
color: var(--blue-600);
font-weight: 600;
}
.formula-bar .hl-evid {
color: var(--amber-600);
font-weight: 600;
}
/* Section cards */
.section-card {
background: var(--bg2);
border-radius: var(--radius-lg);
padding: 1.25rem 1.5rem;
margin: 1rem 0;
border: 0.5px solid var(--bd2);
}
/* Grid viz */
.grid {
display: grid;
grid-template-columns: repeat(20, 1fr);
gap: 3px;
margin: 1rem 0;
}
.dot {
width: 100%;
aspect-ratio: 1;
border-radius: 3px;
transition: background 0.4s;
}
.legend {
display: flex;
flex-wrap: wrap;
gap: 14px;
font-size: 12px;
color: var(--tx2);
margin: 0.5rem 0;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
}
.legend-swatch {
width: 12px;
height: 12px;
border-radius: 3px;
}
.stat-row {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 10px;
margin: 1rem 0;
}
.stat {
background: var(--bg);
border-radius: var(--radius);
padding: 10px 12px;
border: 0.5px solid var(--bd2);
}
.stat-label {
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 2px;
}
.stat-val {
font-size: 18px;
font-weight: 500;
}
.eq-line {
font-family: var(--mono);
font-size: 14px;
color: var(--tx2);
text-align: center;
margin: 0.5rem 0;
}
.slider-row {
display: flex;
align-items: center;
gap: 12px;
margin: 6px 0;
}
.slider-row label {
font-size: 12px;
color: var(--tx2);
min-width: 140px;
}
.slider-row input[type="range"] {
flex: 1;
}
.slider-row span {
font-size: 12px;
color: var(--tx2);
min-width: 32px;
text-align: right;
}
/* Tabs */
.tab-row {
display: flex;
gap: 6px;
margin: 0 0 1rem;
flex-wrap: wrap;
}
.tab-btn {
cursor: pointer;
padding: 6px 14px;
font-size: 12px;
border-radius: var(--radius);
background: var(--bg);
color: var(--tx2);
border: 0.5px solid var(--bd);
transition: all 0.2s;
}
.tab-btn.active {
background: var(--blue-50);
color: var(--blue-800);
border-color: var(--blue-200);
}
@media (prefers-color-scheme: dark) {
.tab-btn.active {
background: var(--blue-800);
color: var(--blue-100);
border-color: var(--blue-600);
}
}
.tab-panel {
display: none;
}
.tab-panel.active {
display: block;
}
/* Evidence cards */
.ev-card {
background: var(--bg);
border-radius: var(--radius);
padding: 8px 12px;
margin: 5px 0;
display: flex;
align-items: center;
gap: 10px;
cursor: pointer;
border: 0.5px solid var(--bd2);
transition: opacity 0.3s;
}
.ev-card.off {
opacity: 0.3;
}
.ev-check {
width: 16px;
height: 16px;
border-radius: 3px;
border: 0.5px solid var(--bd);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
color: var(--blue-600);
flex-shrink: 0;
}
.ev-card.on .ev-check {
background: var(--blue-50);
border-color: var(--blue-200);
}
@media (prefers-color-scheme: dark) {
.ev-card.on .ev-check {
background: var(--blue-800);
border-color: var(--blue-600);
color: var(--blue-100);
}
}
.ev-label {
font-size: 12px;
color: var(--tx);
flex: 1;
}
.ev-lr {
font-size: 11px;
font-family: var(--mono);
min-width: 40px;
text-align: right;
}
.bar-bg {
height: 24px;
background: var(--bg3);
border-radius: 12px;
overflow: hidden;
margin: 4px 0;
}
.bar-fill {
height: 100%;
border-radius: 12px;
transition:
width 0.5s,
background 0.5s;
}
.bar-labels {
display: flex;
justify-content: space-between;
font-size: 11px;
color: var(--tx3);
}
.belief-val {
font-size: 22px;
font-weight: 500;
margin: 2px 0;
}
.belief-label {
font-size: 11px;
color: var(--tx3);
}
.step-log {
font-size: 11px;
font-family: var(--mono);
color: var(--tx3);
line-height: 1.7;
margin: 0.5rem 0;
max-height: 120px;
overflow-y: auto;
}
/* Domain rows */
.domain-row {
display: flex;
align-items: center;
gap: 8px;
margin: 6px 0;
}
.domain-tag {
font-size: 10px;
padding: 2px 8px;
border-radius: var(--radius);
min-width: 60px;
text-align: center;
}
.domain-text {
font-size: 12px;
color: var(--tx2);
flex: 1;
}
.domain-lr {
font-size: 11px;
font-family: var(--mono);
padding: 2px 8px;
border-radius: var(--radius);
}
/* Expandable cards */
.exp-card {
background: var(--bg);
border-radius: var(--radius-lg);
padding: 12px 16px;
margin: 8px 0;
border: 0.5px solid var(--bd2);
cursor: pointer;
transition: all 0.2s;
}
.exp-card.active {
border-color: var(--blue-200);
}
@media (prefers-color-scheme: dark) {
.exp-card.active {
border-color: var(--blue-600);
}
}
.exp-head {
display: flex;
justify-content: space-between;
align-items: center;
}
.exp-title {
font-size: 13px;
font-weight: 500;
color: var(--tx);
}
.exp-tag {
font-size: 10px;
padding: 2px 8px;
border-radius: var(--radius);
}
.exp-body {
display: none;
margin-top: 10px;
font-size: 12px;
color: var(--tx2);
line-height: 1.65;
}
.exp-card.active .exp-body {
display: block;
}
.example-box {
background: var(--bg2);
border-radius: var(--radius);
padding: 8px 12px;
margin: 6px 0;
border: 0.5px solid var(--bd2);
}
.example-box .elabel {
font-size: 10px;
color: var(--tx3);
margin-bottom: 2px;
}
.echain {
font-family: var(--mono);
font-size: 11px;
color: var(--tx3);
margin: 4px 0;
}
.eresult {
font-size: 12px;
font-weight: 500;
margin-top: 4px;
}
.note {
font-size: 12px;
color: var(--tx3);
line-height: 1.6;
margin-top: 0.5rem;
}
svg text.th {
font-family: var(--font);
font-size: 14px;
font-weight: 500;
fill: var(--tx);
}
svg text.ts {
font-family: var(--font);
font-size: 12px;
fill: var(--tx2);
}
svg text.t {
font-family: var(--font);
font-size: 14px;
fill: var(--tx);
}
.odds-chain {
font-family: var(--mono);
font-size: 12px;
color: var(--tx2);
margin: 6px 0;
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.odds-chain .op {
color: var(--tx3);
}
.big-result {
font-size: 18px;
font-weight: 500;
color: var(--tx);
margin: 0.75rem 0 0;
}
.toc {
list-style: none;
padding: 0;
margin: 1rem 0 2rem;
}
.toc li {
margin: 4px 0;
}
.toc a {
font-size: 14px;
color: var(--blue-600);
text-decoration: none;
}
.toc a:hover {
text-decoration: underline;
}
@media (prefers-color-scheme: dark) {
.toc a {
color: var(--blue-200);
}
}
</style>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bayes' Theorem — A Visual Guide</title>
</head>
<body>
<div class="page">
<h1>Bayes' Theorem</h1>
<p class="subtitle">
A visual, interactive guide — from formula to real-world reasoning
</p>
<div class="formula-bar">
<span class="hl-post">P(cause | data)</span> =
<span class="hl-like">P(data | cause)</span> &times;
<span class="hl-prior">P(cause)</span> /
<span class="hl-evid">P(data)</span>
</div>
<nav>
<ol class="toc">
<li>
<a href="#s1">1. The population grid — Bayes is just counting</a>
</li>
<li>
<a href="#s2">2. The three terms — a tug of war on your belief</a>
</li>
<li>
<a href="#s3"
>3. The rectangle is a population, not a Venn diagram</a
>
</li>
<li>
<a href="#s4"
>4. Likelihood vs. probability — same formula, different
question</a
>
</li>
<li>
<a href="#s5"
>5. Sequential updating — ratcheting belief with each signal</a
>
</li>
<li><a href="#s6">6. Each signal pulls from its own space</a></li>
<li><a href="#s7">7. Where the numbers come from in practice</a></li>
<li><a href="#s8">8. When Bayes is most valuable</a></li>
</ol>
</nav>
<!-- ============================================================ -->
<h2 id="s1">1. The population grid</h2>
<p>
Each square is one person out of 200. The question "what's
P(cause|data)?" is just: of all the squares that show the data (purple +
coral), what fraction actually have the cause (purple only)?
</p>
<div class="section-card">
<div class="legend" id="leg1">
<div class="legend-item">
<div
class="legend-swatch"
style="background: var(--purple-200)"
></div>
Cause + data (TP)
</div>
<div class="legend-item">
<div
class="legend-swatch"
style="background: var(--teal-200)"
></div>
Cause, no data (FN)
</div>
<div class="legend-item">
<div
class="legend-swatch"
style="background: var(--coral-200)"
></div>
No cause, but data (FP)
</div>
<div class="legend-item">
<div
class="legend-swatch"
style="background: var(--gray-100)"
></div>
Neither (TN)
</div>
</div>
<div class="grid" id="grid1"></div>
<div class="stat-row">
<div class="stat">
<div class="stat-label" style="color: var(--purple-600)">
P(cause|data)
</div>
<div class="stat-val" id="v1-post" style="color: var(--purple-600)">
</div>
</div>
<div class="stat">
<div class="stat-label" style="color: var(--teal-600)">
P(data|cause)
</div>
<div class="stat-val" id="v1-like" style="color: var(--teal-600)">
</div>
</div>
<div class="stat">
<div class="stat-label" style="color: var(--blue-600)">
P(cause)
</div>
<div class="stat-val" id="v1-prior" style="color: var(--blue-600)">
</div>
</div>
<div class="stat">
<div class="stat-label" style="color: var(--amber-600)">
P(data)
</div>
<div class="stat-val" id="v1-evid" style="color: var(--amber-600)">
</div>
</div>
</div>
<div class="eq-line" id="eq1"></div>
<div class="slider-row">
<label>P(cause) = <span id="l1a">10%</span></label
><input
type="range"
min="1"
max="50"
value="10"
step="1"
id="s1a"
oninput="updateGrid()"
/><span></span>
</div>
<div class="slider-row">
<label>P(data|cause) = <span id="l1b">90%</span></label
><input
type="range"
min="5"
max="100"
value="90"
step="1"
id="s1b"
oninput="updateGrid()"
/><span></span>
</div>
<div class="slider-row">
<label>P(data|¬cause) = <span id="l1c">5%</span></label
><input
type="range"
min="0"
max="50"
value="5"
step="1"
id="s1c"
oninput="updateGrid()"
/><span></span>
</div>
<div class="note">
Drag sliders to see how the posterior shifts. A 90%-accurate test
gives only ~17% posterior when the cause is rare (10%).
</div>
</div>
<!-- ============================================================ -->
<h2 id="s2">2. The three terms</h2>
<p>
The prior anchors your starting belief. The likelihood amplifies it (how
well does the cause explain the data?). The marginal dampens it (how
common is the data anyway?). The posterior is where the balance settles.
</p>
<div class="section-card">
<svg
width="100%"
viewBox="0 0 680 290"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="40"
y="20"
width="170"
height="50"
rx="8"
fill="var(--blue-50)"
stroke="var(--blue-200)"
stroke-width="0.5"
/>
<text
class="th"
x="125"
y="38"
text-anchor="middle"
dominant-baseline="central"
fill="var(--blue-800)"
>
Prior P(cause)
</text>
<text
class="ts"
x="125"
y="56"
text-anchor="middle"
dominant-baseline="central"
fill="var(--blue-600)"
>
What you believed before
</text>
<rect
x="255"
y="20"
width="170"
height="50"
rx="8"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="th"
x="340"
y="38"
text-anchor="middle"
dominant-baseline="central"
fill="var(--teal-800)"
>
Likelihood P(d|c)
</text>
<text
class="ts"
x="340"
y="56"
text-anchor="middle"
dominant-baseline="central"
fill="var(--teal-600)"
>
How well cause fits data
</text>
<rect
x="470"
y="20"
width="170"
height="50"
rx="8"
fill="var(--amber-50)"
stroke="var(--amber-200)"
stroke-width="0.5"
/>
<text
class="th"
x="555"
y="38"
text-anchor="middle"
dominant-baseline="central"
fill="var(--amber-800)"
>
Marginal P(data)
</text>
<text
class="ts"
x="555"
y="56"
text-anchor="middle"
dominant-baseline="central"
fill="var(--amber-600)"
>
How common is this data?
</text>
<text
class="ts"
x="125"
y="90"
text-anchor="middle"
fill="var(--blue-600)"
>
anchors belief
</text>
<text
class="ts"
x="340"
y="90"
text-anchor="middle"
fill="var(--teal-600)"
>
amplifies belief
</text>
<text
class="ts"
x="555"
y="90"
text-anchor="middle"
fill="var(--amber-600)"
>
dampens belief
</text>
<rect
x="170"
y="108"
width="340"
height="44"
rx="8"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="th"
x="340"
y="134"
text-anchor="middle"
dominant-baseline="central"
fill="var(--purple-800)"
>
Posterior P(cause|data) — updated belief
</text>
<!-- Balance visual -->
<polygon points="340,218 322,244 358,244" fill="var(--gray-200)" />
<line
x1="170"
y1="218"
x2="510"
y2="218"
stroke="var(--gray-400)"
stroke-width="1.5"
/>
<rect
x="120"
y="178"
width="100"
height="34"
rx="6"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="170"
y="198"
text-anchor="middle"
dominant-baseline="central"
fill="var(--teal-800)"
>
Likelihood
</text>
<text
class="ts"
x="170"
y="260"
text-anchor="middle"
fill="var(--teal-600)"
>
Pulls UP
</text>
<rect
x="460"
y="178"
width="100"
height="34"
rx="6"
fill="var(--amber-50)"
stroke="var(--amber-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="510"
y="198"
text-anchor="middle"
dominant-baseline="central"
fill="var(--amber-800)"
>
Marginal
</text>
<text
class="ts"
x="510"
y="260"
text-anchor="middle"
fill="var(--amber-600)"
>
Pulls DOWN
</text>
<text class="ts" x="340" y="280" text-anchor="middle">
The posterior is where the balance settles
</text>
</svg>
</div>
<!-- ============================================================ -->
<h2 id="s3">3. The rectangle is a population</h2>
<p>
The standard Venn diagram is misleading — cancer isn't a "subset" of
symptoms. They're different dimensions: hidden state (rows) vs.
observable data (columns). Each person falls into one of four cells.
</p>
<div class="section-card">
<div class="tab-row">
<div class="tab-btn active" onclick="showTab('t3', 0)">
Misleading Venn
</div>
<div class="tab-btn" onclick="showTab('t3', 1)">
Correct: 2D table
</div>
<div class="tab-btn" onclick="showTab('t3', 2)">Why it matters</div>
</div>
<div class="tab-panel active" data-group="t3">
<svg
width="100%"
viewBox="0 0 680 220"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="40"
y="20"
width="300"
height="180"
rx="4"
fill="none"
stroke="var(--bd)"
stroke-width="0.5"
/>
<text class="ts" x="50" y="38">All possible outcomes</text>
<ellipse
cx="170"
cy="120"
rx="90"
ry="65"
fill="var(--blue-50)"
stroke="var(--blue-200)"
stroke-width="0.5"
/>
<text
class="th"
x="160"
y="112"
text-anchor="middle"
fill="var(--blue-800)"
>
Symptoms
</text>
<ellipse
cx="210"
cy="115"
rx="45"
ry="35"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="210"
y="118"
text-anchor="middle"
fill="var(--purple-800)"
>
Cancer
</text>
<text class="ts" x="400" y="60" fill="var(--red-600)">
This implies cancer is a
</text>
<text class="ts" x="400" y="78" fill="var(--red-600)">
subset of symptoms
</text>
<text class="ts" x="400" y="110">
But cancer is a hidden cause.
</text>
<text class="ts" x="400" y="128">Symptoms are observations.</text>
<text class="ts" x="400" y="146">
Different categories entirely.
</text>
</svg>
</div>
<div class="tab-panel" data-group="t3">
<svg
width="100%"
viewBox="0 0 680 280"
xmlns="http://www.w3.org/2000/svg"
>
<text class="th" x="340" y="22" text-anchor="middle">
Two independent dimensions per person
</text>
<text
class="ts"
x="60"
y="100"
text-anchor="middle"
fill="var(--purple-800)"
>
Cancer
</text>
<text
class="ts"
x="60"
y="190"
text-anchor="middle"
fill="var(--teal-800)"
>
No cancer
</text>
<text
class="ts"
x="220"
y="54"
text-anchor="middle"
fill="var(--amber-800)"
>
Symptom +
</text>
<text
class="ts"
x="420"
y="54"
text-anchor="middle"
fill="var(--gray-600)"
>
Symptom -
</text>
<rect
x="110"
y="66"
width="200"
height="70"
rx="4"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="th"
x="210"
y="96"
text-anchor="middle"
fill="var(--purple-800)"
>
True positive
</text>
<text
class="ts"
x="210"
y="114"
text-anchor="middle"
fill="var(--purple-600)"
>
Cancer + symptoms
</text>
<rect
x="330"
y="66"
width="200"
height="70"
rx="4"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="th"
x="430"
y="96"
text-anchor="middle"
fill="var(--teal-800)"
>
False negative
</text>
<text
class="ts"
x="430"
y="114"
text-anchor="middle"
fill="var(--teal-600)"
>
Cancer, no symptoms
</text>
<rect
x="110"
y="152"
width="200"
height="70"
rx="4"
fill="var(--coral-50)"
stroke="var(--coral-200)"
stroke-width="0.5"
/>
<text
class="th"
x="210"
y="182"
text-anchor="middle"
fill="var(--coral-800)"
>
False positive
</text>
<text
class="ts"
x="210"
y="200"
text-anchor="middle"
fill="var(--coral-600)"
>
No cancer, but symptoms
</text>
<rect
x="330"
y="152"
width="200"
height="70"
rx="4"
fill="var(--gray-50)"
stroke="var(--gray-100)"
stroke-width="0.5"
/>
<text
class="th"
x="430"
y="182"
text-anchor="middle"
fill="var(--gray-800)"
>
True negative
</text>
<text
class="ts"
x="430"
y="200"
text-anchor="middle"
fill="var(--gray-600)"
>
No cancer, no symptoms
</text>
<text
class="ts"
x="340"
y="258"
text-anchor="middle"
fill="var(--teal-600)"
>
Each cell = people with that combination of state + observation
</text>
</svg>
</div>
<div class="tab-panel" data-group="t3">
<svg
width="100%"
viewBox="0 0 680 200"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="40"
y="20"
width="44"
height="160"
rx="0"
fill="var(--purple-200)"
opacity="0.6"
/>
<text
class="ts"
x="62"
y="12"
text-anchor="middle"
fill="var(--purple-600)"
>
Cancer (1%)
</text>
<rect
x="84"
y="20"
width="556"
height="160"
rx="0"
fill="var(--gray-50)"
/>
<text
class="ts"
x="362"
y="12"
text-anchor="middle"
fill="var(--gray-600)"
>
No cancer (99%)
</text>
<rect
x="40"
y="20"
width="44"
height="144"
fill="var(--purple-200)"
/>
<text
class="ts"
x="62"
y="96"
text-anchor="middle"
fill="var(--purple-800)"
>
TP: 9
</text>
<rect
x="40"
y="164"
width="44"
height="16"
fill="var(--teal-100)"
/>
<rect
x="84"
y="20"
width="92"
height="160"
fill="var(--coral-100)"
/>
<text
class="ts"
x="130"
y="96"
text-anchor="middle"
fill="var(--coral-800)"
>
FP: 45
</text>
<rect
x="176"
y="20"
width="464"
height="160"
fill="var(--gray-50)"
/>
<text
class="ts"
x="408"
y="96"
text-anchor="middle"
fill="var(--gray-600)"
>
TN: 855
</text>
<rect
x="40"
y="20"
width="600"
height="160"
rx="0"
fill="none"
stroke="var(--bd)"
stroke-width="0.5"
/>
<text class="th" x="340" y="200" text-anchor="middle">
P(cancer|symptom+) = 9 / (9 + 45) = 17%
</text>
</svg>
<div class="note">
The Venn diagram hides the enormous no-cancer population producing
false alarms. The contingency table makes the coral zone visible.
</div>
</div>
</div>
<!-- ============================================================ -->
<h2 id="s4">4. Likelihood vs. probability</h2>
<p>
P(data|cause) looks like one thing but plays two different roles
depending on which variable you're moving. Probability sweeps over data
(sums to 1). Likelihood sweeps over causes (does NOT sum to 1).
</p>
<div class="section-card">
<div class="tab-row">
<div class="tab-btn active" onclick="showTab('t4', 0)">
Probability (fix cause)
</div>
<div class="tab-btn" onclick="showTab('t4', 1)">
Likelihood (fix data)
</div>
<div class="tab-btn" onclick="showTab('t4', 2)">Side by side</div>
</div>
<div class="tab-panel active" data-group="t4">
<svg
width="100%"
viewBox="0 0 680 240"
xmlns="http://www.w3.org/2000/svg"
>
<text class="th" x="340" y="22" text-anchor="middle">
Fix the cause, ask "what data do I expect?"
</text>
<rect
x="50"
y="42"
width="110"
height="38"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="th"
x="105"
y="64"
text-anchor="middle"
dominant-baseline="central"
fill="var(--purple-800)"
>
Cancer
</text>
<text
class="ts"
x="105"
y="96"
text-anchor="middle"
fill="var(--purple-600)"
>
Locked in
</text>
<rect
x="220"
y="36"
width="190"
height="40"
rx="6"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="52"
text-anchor="middle"
fill="var(--teal-800)"
>
P(fever|cancer) = 0.60
</text>
<text
class="ts"
x="315"
y="66"
text-anchor="middle"
fill="var(--teal-600)"
>
Fever?
</text>
<rect
x="220"
y="86"
width="190"
height="40"
rx="6"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="102"
text-anchor="middle"
fill="var(--teal-800)"
>
P(weight loss|cancer) = 0.40
</text>
<text
class="ts"
x="315"
y="116"
text-anchor="middle"
fill="var(--teal-600)"
>
Weight loss?
</text>
<rect
x="220"
y="136"
width="190"
height="40"
rx="6"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="152"
text-anchor="middle"
fill="var(--teal-800)"
>
P(fatigue|cancer) = 0.70
</text>
<text
class="ts"
x="315"
y="166"
text-anchor="middle"
fill="var(--teal-600)"
>
Fatigue?
</text>
<rect
x="470"
y="86"
width="170"
height="40"
rx="6"
fill="var(--amber-50)"
stroke="var(--amber-200)"
stroke-width="0.5"
/>
<text
class="th"
x="555"
y="104"
text-anchor="middle"
dominant-baseline="central"
fill="var(--amber-800)"
>
Must sum to 1
</text>
<text
class="ts"
x="555"
y="150"
text-anchor="middle"
fill="var(--amber-600)"
>
Sweeping over all data
</text>
<text
class="ts"
x="555"
y="166"
text-anchor="middle"
fill="var(--amber-600)"
>
for one fixed cause
</text>
</svg>
<div class="note">
Probability mode: "I <em>know</em> the patient has cancer. What
symptoms should I expect?" Generates predictions forward.
</div>
</div>
<div class="tab-panel" data-group="t4">
<svg
width="100%"
viewBox="0 0 680 240"
xmlns="http://www.w3.org/2000/svg"
>
<text class="th" x="340" y="22" text-anchor="middle">
Fix the data, ask "which cause explains it best?"
</text>
<rect
x="50"
y="42"
width="110"
height="38"
rx="6"
fill="var(--teal-50)"
stroke="var(--teal-200)"
stroke-width="0.5"
/>
<text
class="th"
x="105"
y="64"
text-anchor="middle"
dominant-baseline="central"
fill="var(--teal-800)"
>
Fever
</text>
<text
class="ts"
x="105"
y="96"
text-anchor="middle"
fill="var(--teal-600)"
>
Locked in
</text>
<rect
x="220"
y="36"
width="190"
height="40"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="52"
text-anchor="middle"
fill="var(--purple-800)"
>
L(cancer) = 0.60
</text>
<text
class="ts"
x="315"
y="66"
text-anchor="middle"
fill="var(--purple-600)"
>
Cancer?
</text>
<rect
x="220"
y="86"
width="190"
height="40"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="102"
text-anchor="middle"
fill="var(--purple-800)"
>
L(flu) = 0.85
</text>
<text
class="ts"
x="315"
y="116"
text-anchor="middle"
fill="var(--purple-600)"
>
Flu?
</text>
<rect
x="220"
y="136"
width="190"
height="40"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="315"
y="152"
text-anchor="middle"
fill="var(--purple-800)"
>
L(infection) = 0.75
</text>
<text
class="ts"
x="315"
y="166"
text-anchor="middle"
fill="var(--purple-600)"
>
Infection?
</text>
<rect
x="470"
y="86"
width="170"
height="40"
rx="6"
fill="var(--red-50)"
stroke="var(--red-200)"
stroke-width="0.5"
/>
<text
class="th"
x="555"
y="104"
text-anchor="middle"
dominant-baseline="central"
fill="var(--red-800)"
>
Do NOT sum to 1
</text>
<text
class="ts"
x="555"
y="150"
text-anchor="middle"
fill="var(--red-600)"
>
Sweeping over all causes
</text>
<text
class="ts"
x="555"
y="166"
text-anchor="middle"
fill="var(--red-600)"
>
for one fixed observation
</text>
</svg>
<div class="note">
Likelihood mode: "I <em>see</em> fever. Which cause fits best?"
These are scores, not probabilities. 0.85 + 0.60 + 0.75 = 2.20, and
that's fine.
</div>
</div>
<div class="tab-panel" data-group="t4">
<div
style="
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 8px;
"
>
<div
style="
background: var(--teal-50);
border-radius: var(--radius-lg);
padding: 14px;
border: 0.5px solid var(--teal-200);
"
>
<div
style="
font-weight: 500;
font-size: 14px;
color: var(--teal-800);
margin-bottom: 8px;
"
>
Probability
</div>
<div
style="
font-size: 12px;
color: var(--teal-600);
line-height: 1.6;
"
>
Fix: cause<br />Sweep: data<br />Sums to 1<br />"Given cancer,
what will I see?"<br />Forward: cause generates data
</div>
</div>
<div
style="
background: var(--purple-50);
border-radius: var(--radius-lg);
padding: 14px;
border: 0.5px solid var(--purple-200);
"
>
<div
style="
font-weight: 500;
font-size: 14px;
color: var(--purple-800);
margin-bottom: 8px;
"
>
Likelihood
</div>
<div
style="
font-size: 12px;
color: var(--purple-600);
line-height: 1.6;
"
>
Fix: data<br />Sweep: cause<br />Does NOT sum to 1<br />"Given
fever, which cause fits?"<br />Backward: data interrogates
causes
</div>
</div>
</div>
</div>
</div>
<!-- ============================================================ -->
<h2 id="s5">5. Sequential updating</h2>
<p>
Each piece of evidence is its own mini Bayes update. Yesterday's
posterior becomes today's prior. In odds form:
<code>posterior odds = prior odds × likelihood ratio</code>.
</p>
<div class="section-card">
<div class="tab-row">
<div class="tab-btn active" onclick="showTab('t5', 0)">
Is she into me?
</div>
<div class="tab-btn" onclick="showTab('t5', 1)">
Is USA bombing Iran?
</div>
</div>
<div class="tab-panel active" data-group="t5">
<div style="font-size: 12px; color: var(--tx3); margin-bottom: 6px">
Click each signal to observe it. Watch your belief update.
</div>
<div id="ev0"></div>
<div style="margin-top: 12px">
<div class="belief-label">P(she's into me | all evidence)</div>
<div class="belief-val" id="bv0">30%</div>
<div class="bar-bg">
<div
class="bar-fill"
id="bf0"
style="width: 30%; background: var(--purple-200)"
></div>
</div>
<div class="bar-labels">
<span>Not interested</span><span>Definitely interested</span>
</div>
</div>
<div class="step-log" id="log0"><div>Prior: 30%</div></div>
</div>
<div class="tab-panel" data-group="t5">
<div style="font-size: 12px; color: var(--tx3); margin-bottom: 6px">
Click each intelligence signal. Watch the estimate shift.
</div>
<div id="ev1"></div>
<div style="margin-top: 12px">
<div class="belief-label">
P(USA strikes Iran | all intelligence)
</div>
<div class="belief-val" id="bv1">5%</div>
<div class="bar-bg">
<div
class="bar-fill"
id="bf1"
style="width: 5%; background: var(--red-200)"
></div>
</div>
<div class="bar-labels">
<span>Posturing</span><span>Imminent strike</span>
</div>
</div>
<div class="step-log" id="log1"><div>Prior: 5%</div></div>
</div>
</div>
<!-- ============================================================ -->
<h2 id="s6">6. Each signal pulls from its own space</h2>
<p>
The likelihood ratio for "she leans in" is computed entirely within the
body language domain. "Fast replies" lives in texting. They combine
multiplicatively — evidence from unrelated domains converges onto one
belief.
</p>
<div class="section-card">
<div class="domain-row">
<div
class="domain-tag"
style="background: var(--purple-50); color: var(--purple-800)"
>
Body
</div>
<div class="domain-text">
"Leaning in" — how often do interested vs. uninterested people lean
in?
</div>
<div
class="domain-lr"
style="background: var(--purple-50); color: var(--purple-800)"
>
2x
</div>
</div>
<div class="domain-row">
<div
class="domain-tag"
style="background: var(--teal-50); color: var(--teal-800)"
>
Texting
</div>
<div class="domain-text">
"Fast replies" — how often do interested vs. uninterested people
reply fast?
</div>
<div
class="domain-lr"
style="background: var(--teal-50); color: var(--teal-800)"
>
2.5x
</div>
</div>
<div class="domain-row">
<div
class="domain-tag"
style="background: var(--amber-50); color: var(--amber-800)"
>
Social
</div>
<div class="domain-text">
"Friends tease" — how often with vs. without interest?
</div>
<div
class="domain-lr"
style="background: var(--amber-50); color: var(--amber-800)"
>
3.5x
</div>
</div>
<div class="domain-row">
<div
class="domain-tag"
style="background: var(--coral-50); color: var(--coral-800)"
>
Verbal
</div>
<div class="domain-text">
"Mentions boyfriend" — how often with vs. without interest?
</div>
<div
class="domain-lr"
style="background: var(--coral-50); color: var(--coral-800)"
>
0.05x
</div>
</div>
<h3 style="margin-top: 1rem">Multiplication chain (odds form)</h3>
<div class="odds-chain">
<span>Prior 30:70</span> <span class="op">&times;</span>
<span style="color: var(--purple-600)">2</span>
<span class="op">&times;</span>
<span style="color: var(--teal-600)">2.5</span>
<span class="op">&times;</span>
<span style="color: var(--amber-600)">3.5</span>
<span class="op">&times;</span>
<span style="color: var(--coral-600)">0.05</span>
<span class="op">=</span>
<span>0.375 : 1 → P = 27%</span>
</div>
<div class="big-result">
Three positive signals wiped out by one strong negative
</div>
<div class="note">
But beware: if "leans in," "fast replies," and "laughs a lot" all stem
from one hidden trait (she's naturally warm), they're correlated —
multiplying them as independent evidence over-counts. The test: could
one hidden variable explain all of them?
</div>
</div>
<!-- ============================================================ -->
<h2 id="s7">7. Where the numbers come from</h2>
<p>
The prior can range from hard data to a pure guess. The framework
doesn't require precision — it requires explicitness. An explicit bad
guess is more correctable than an invisible one.
</p>
<div class="section-card">
<div class="tab-row">
<div class="tab-btn active" onclick="showTab('t7', 0)">
Source spectrum
</div>
<div class="tab-btn" onclick="showTab('t7', 1)">Correlation trap</div>
<div class="tab-btn" onclick="showTab('t7', 2)">
Why it still works
</div>
</div>
<div class="tab-panel active" data-group="t7">
<div style="display: flex; flex-direction: column; gap: 6px">
<div
style="
background: var(--teal-50);
padding: 10px 14px;
border-radius: var(--radius);
border: 0.5px solid var(--teal-200);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--teal-800);
"
>
Hard data
</div>
<div style="font-size: 12px; color: var(--teal-600)">
Clinical trials, large datasets — P(positive|cancer)=0.90 from
50,000 patients
</div>
</div>
<div
style="
background: var(--blue-50);
padding: 10px 14px;
border-radius: var(--radius);
border: 0.5px solid var(--blue-200);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--blue-800);
"
>
Reference classes
</div>
<div style="font-size: 12px; color: var(--blue-600)">
Historical frequency — P(strike|buildup)=0.08 from 50 past
buildups
</div>
</div>
<div
style="
background: var(--amber-50);
padding: 10px 14px;
border-radius: var(--radius);
border: 0.5px solid var(--amber-200);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--amber-800);
"
>
Expert elicitation
</div>
<div style="font-size: 12px; color: var(--amber-600)">
Average of 10 domain experts — P(recession|yield curve)=0.35
</div>
</div>
<div
style="
background: var(--coral-50);
padding: 10px 14px;
border-radius: var(--radius);
border: 0.5px solid var(--coral-200);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--coral-800);
"
>
Calibrated gut
</div>
<div style="font-size: 12px; color: var(--coral-600)">
Your estimate, checked against track record — P(she's into
me)=0.30
</div>
</div>
<div
style="
background: var(--gray-50);
padding: 10px 14px;
border-radius: var(--radius);
border: 0.5px solid var(--gray-100);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--gray-800);
"
>
Pure guess
</div>
<div style="font-size: 12px; color: var(--gray-600)">
No basis, but at least it's explicit and updatable
</div>
</div>
</div>
</div>
<div class="tab-panel" data-group="t7">
<svg
width="100%"
viewBox="0 0 680 260"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="180"
y="20"
width="320"
height="38"
rx="6"
fill="var(--coral-50)"
stroke="var(--coral-200)"
stroke-width="0.5"
/>
<text
class="th"
x="340"
y="42"
text-anchor="middle"
dominant-baseline="central"
fill="var(--coral-800)"
>
Hidden variable: she's naturally warm
</text>
<line
x1="260"
y1="58"
x2="130"
y2="90"
stroke="var(--coral-200)"
stroke-width="0.5"
/>
<line
x1="340"
y1="58"
x2="340"
y2="90"
stroke="var(--coral-200)"
stroke-width="0.5"
/>
<line
x1="420"
y1="58"
x2="550"
y2="90"
stroke="var(--coral-200)"
stroke-width="0.5"
/>
<rect
x="55"
y="92"
width="150"
height="34"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="130"
y="112"
text-anchor="middle"
dominant-baseline="central"
fill="var(--purple-800)"
>
Leans in
</text>
<rect
x="265"
y="92"
width="150"
height="34"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="340"
y="112"
text-anchor="middle"
dominant-baseline="central"
fill="var(--purple-800)"
>
Fast replies
</text>
<rect
x="475"
y="92"
width="150"
height="34"
rx="6"
fill="var(--purple-50)"
stroke="var(--purple-200)"
stroke-width="0.5"
/>
<text
class="ts"
x="550"
y="112"
text-anchor="middle"
dominant-baseline="central"
fill="var(--purple-800)"
>
Laughs a lot
</text>
<text
class="ts"
x="340"
y="155"
text-anchor="middle"
fill="var(--red-600)"
>
You think: 2x × 2.5x × 2x = 10x boost
</text>
<text
class="ts"
x="340"
y="173"
text-anchor="middle"
fill="var(--red-600)"
>
Reality: all three stem from one trait — effective ~2.5x
</text>
<line
x1="40"
y1="194"
x2="640"
y2="194"
stroke="var(--bd)"
stroke-width="0.5"
/>
<text
class="ts"
x="340"
y="216"
text-anchor="middle"
fill="var(--teal-600)"
>
Truly independent: body language + direct action + third-party
source
</text>
<text
class="ts"
x="340"
y="234"
text-anchor="middle"
fill="var(--teal-600)"
>
Test: if I learned the cause of signal A, would it change my
expectation of B?
</text>
</svg>
</div>
<div class="tab-panel" data-group="t7">
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px">
<div
style="
background: var(--red-50);
padding: 12px;
border-radius: var(--radius);
border: 0.5px solid var(--red-100);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--red-800);
margin-bottom: 6px;
"
>
Without Bayes
</div>
<div
style="font-size: 12px; color: var(--red-600); line-height: 1.5"
>
Priors are hidden<br />Correlations ignored<br />Base rates
forgotten<br />One vivid signal dominates
</div>
</div>
<div
style="
background: var(--teal-50);
padding: 12px;
border-radius: var(--radius);
border: 0.5px solid var(--teal-100);
"
>
<div
style="
font-weight: 500;
font-size: 13px;
color: var(--teal-800);
margin-bottom: 6px;
"
>
With Bayes (even imperfect)
</div>
<div
style="
font-size: 12px;
color: var(--teal-600);
line-height: 1.5;
"
>
Priors explicit, challengeable<br />Self-correcting with data<br />Base
rates anchored<br />Degrades gracefully
</div>
</div>
</div>
<div class="note" style="margin-top: 10px">
The framework's main value isn't producing a precise number. It's
forcing you to ask "what's my base rate?", "how diagnostic is this
signal really?", and "am I double-counting correlated evidence?"
</div>
</div>
</div>
<!-- ============================================================ -->
<h2 id="s8">8. When Bayes is most valuable</h2>
<p>
The prior doesn't need to be low — Bayes catches a different cognitive
mistake at each end of the spectrum.
</p>
<div class="section-card" style="padding-bottom: 0.5rem">
<div class="exp-card active" onclick="toggleExp(this)">
<div class="exp-head">
<div class="exp-title">
Low prior: rare disease, rare crime, rare event
</div>
<div
class="exp-tag"
style="background: var(--purple-50); color: var(--purple-800)"
>
Base rate neglect
</div>
</div>
<div class="exp-body">
Your gut overweights the positive test and forgets how rare the
disease is.
<div class="example-box">
<div class="elabel">Medical screening</div>
Test is 95% accurate. Disease affects 1 in 1000.
<div class="echain">Prior: 0.1% × LR: 19x = Posterior: ~1.9%</div>
<div class="eresult" style="color: var(--purple-600)">
Gut says 95%. Bayes says 1.9%.
</div>
</div>
<div class="example-box">
<div class="elabel">Startup investing</div>
Great pitch, strong team. But 90% of startups fail.
<div class="echain">Prior: 10% × LR ~3x = Posterior: ~25%</div>
<div class="eresult" style="color: var(--purple-600)">
Impressive pitch doesn't overcome the base rate.
</div>
</div>
</div>
</div>
<div class="exp-card" onclick="toggleExp(this)">
<div class="exp-head">
<div class="exp-title">
High prior: when disconfirming signals compound
</div>
<div
class="exp-tag"
style="background: var(--teal-50); color: var(--teal-800)"
>
Confirmation bias
</div>
</div>
<div class="exp-body">
When you're already confident, you dismiss warning signs one by one
— but they multiply.
<div class="example-box">
<div class="elabel">Your company is "doing well"</div>
Prior: 85% healthy. But churn up, NPS drops, key hire leaves.
<div class="echain">85% × 0.4 × 0.5 × 0.6 = ~40%</div>
<div class="eresult" style="color: var(--teal-600)">
Each alone is dismissible. Together they're a pattern.
</div>
</div>
</div>
</div>
<div class="exp-card" onclick="toggleExp(this)">
<div class="exp-head">
<div class="exp-title">
Many hypotheses: which explanation wins?
</div>
<div
class="exp-tag"
style="background: var(--amber-50); color: var(--amber-800)"
>
Narrative fallacy
</div>
</div>
<div class="exp-body">
Your gut picks the most dramatic story and ignores boring
alternatives.
<div class="example-box">
<div class="elabel">Server is slow</div>
DDoS (exciting, 2% prior), memory leak (boring, 30% prior),
traffic spike (50% prior).
<div class="echain">
DDoS: 2%×3x=6% | Leak: 30%×4x=67% | Traffic: 50%×1.5x=27%
</div>
<div class="eresult" style="color: var(--amber-600)">
The boring explanation almost always wins.
</div>
</div>
</div>
</div>
<div class="exp-card" onclick="toggleExp(this)">
<div class="exp-head">
<div class="exp-title">
Noisy signals: when evidence is ambiguous
</div>
<div
class="exp-tag"
style="background: var(--coral-50); color: var(--coral-800)"
>
Overconfidence
</div>
</div>
<div class="exp-body">
Some evidence is consistent with many hypotheses — it barely
discriminates.
<div class="example-box">
<div class="elabel">Hiring interviews</div>
P(great answers|strong hire): 70%. P(great answers|weak hire):
50%.
<div class="echain">
LR = 70/50 = 1.4x — barely moves the needle
</div>
<div class="eresult" style="color: var(--coral-600)">
Interviews feel informative but LR is ~1.4x. A work sample test
might be 5x.
</div>
</div>
</div>
</div>
</div>
<p
style="
margin-top: 2rem;
font-size: 13px;
color: var(--tx3);
text-align: center;
"
>
interactive Bayes' theorem guide
</p>
</div>
<script>
// === Section 1: Grid ===
const N = 200,
grid = document.getElementById("grid1");
for (let i = 0; i < N; i++) {
const d = document.createElement("div");
d.className = "dot";
grid.appendChild(d);
}
const dots = grid.children;
function updateGrid() {
const pC = parseInt(document.getElementById("s1a").value) / 100;
const pDC = parseInt(document.getElementById("s1b").value) / 100;
const pDN = parseInt(document.getElementById("s1c").value) / 100;
document.getElementById("l1a").textContent = Math.round(pC * 100) + "%";
document.getElementById("l1b").textContent =
Math.round(pDC * 100) + "%";
document.getElementById("l1c").textContent =
Math.round(pDN * 100) + "%";
const nC = Math.round(N * pC),
nN = N - nC;
const nTP = Math.round(nC * pDC),
nFN = nC - nTP;
const nFP = Math.round(nN * pDN),
nTN = nN - nFP;
let idx = 0;
for (let i = 0; i < nTP; i++)
dots[idx++].style.background = "var(--purple-200)";
for (let i = 0; i < nFN; i++)
dots[idx++].style.background = "var(--teal-200)";
for (let i = 0; i < nFP; i++)
dots[idx++].style.background = "var(--coral-200)";
for (let i = 0; i < nTN; i++)
dots[idx++].style.background = "var(--gray-100)";
const pD = (nTP + nFP) / N;
const post = pD > 0 ? nTP / (nTP + nFP) : 0;
document.getElementById("v1-post").textContent =
Math.round(post * 100) + "%";
document.getElementById("v1-like").textContent =
Math.round(pDC * 100) + "%";
document.getElementById("v1-prior").textContent =
Math.round(pC * 100) + "%";
document.getElementById("v1-evid").textContent =
Math.round(pD * 100) + "%";
document.getElementById("eq1").innerHTML =
`<span class="hl-post">${Math.round(post * 100)}%</span> = <span class="hl-like">${Math.round(pDC * 100)}%</span> × <span class="hl-prior">${Math.round(pC * 100)}%</span> / <span class="hl-evid">${Math.round(pD * 100)}%</span>`;
}
updateGrid();
// === Tab system ===
function showTab(group, i) {
document
.querySelectorAll(`[data-group="${group}"]`)
.forEach((p, j) => p.classList.toggle("active", j === i));
const btns = document
.querySelectorAll(`[data-group="${group}"]`)[0]
?.parentElement.querySelectorAll(".tab-btn");
if (!btns) {
let el = document.querySelectorAll(`[data-group="${group}"]`)[0]
?.parentElement;
el?.querySelectorAll(".tab-btn").forEach((b, j) =>
b.classList.toggle("active", j === i),
);
}
const card = document
.querySelectorAll(`[data-group="${group}"]`)[0]
?.closest(".section-card");
if (card)
card
.querySelectorAll(".tab-btn")
.forEach((b, j) => b.classList.toggle("active", j === i));
}
// === Section 5: Sequential updating ===
const scenes = [
{
prior: 0.3,
evidence: [
{ label: "She laughs at your bad jokes", lr: 2.5 },
{ label: "She initiates texting first", lr: 3.0 },
{ label: "She mentions her boyfriend", lr: 0.05 },
{ label: "She suggests hanging out 1-on-1", lr: 4.0 },
{ label: "She takes hours to reply", lr: 0.5 },
{ label: "She remembers small details about you", lr: 2.0 },
{ label: "Her friends seem to know about you", lr: 3.5 },
{ label: "She avoids physical proximity", lr: 0.3 },
],
colors: [
"var(--purple-200)",
"var(--purple-400)",
"var(--purple-600)",
],
fillId: "bf0",
valId: "bv0",
logId: "log0",
evId: "ev0",
},
{
prior: 0.05,
evidence: [
{ label: "Carrier group moves to Persian Gulf", lr: 3.0 },
{ label: "Satellite shows ammo depot buildup", lr: 2.5 },
{ label: "Diplomatic backchannel opens", lr: 0.3 },
{ label: "Military families told to leave region", lr: 5.0 },
{ label: "President makes conciliatory speech", lr: 0.4 },
{ label: "Cyber attacks on Iranian infrastructure", lr: 4.0 },
{ label: "Congress briefed on classified war plans", lr: 6.0 },
{ label: "UN announces emergency summit", lr: 0.6 },
],
colors: ["var(--red-200)", "var(--red-400)", "var(--red-600)"],
fillId: "bf1",
valId: "bv1",
logId: "log1",
evId: "ev1",
},
];
let state = [
{ active: new Array(8).fill(false) },
{ active: new Array(8).fill(false) },
];
function renderCards(si) {
const s = scenes[si],
container = document.getElementById(s.evId);
container.innerHTML = "";
s.evidence.forEach((e, i) => {
const on = state[si].active[i];
const card = document.createElement("div");
card.className = "ev-card " + (on ? "on" : "off");
const c = e.lr > 1 ? "var(--teal-600)" : "var(--red-600)";
card.innerHTML = `<div class="ev-check">${on ? "&#10003;" : ""}</div><div class="ev-label">${e.label}</div><div class="ev-lr" style="color:${c}">LR ${e.lr}x</div>`;
card.onclick = () => {
state[si].active[i] = !state[si].active[i];
recalc(si);
};
container.appendChild(card);
});
}
function recalc(si) {
const s = scenes[si];
renderCards(si);
let belief = s.prior;
const log = document.getElementById(s.logId);
log.innerHTML = `<div>Prior: ${Math.round(s.prior * 100)}%</div>`;
state[si].active.forEach((a, i) => {
if (!a) return;
const e = s.evidence[i],
old = belief;
const odds = (belief / (1 - belief)) * e.lr;
belief = Math.max(0.001, Math.min(0.999, odds / (1 + odds)));
const ar = e.lr > 1 ? "&uarr;" : "&darr;";
log.innerHTML += `<div>${ar} "${e.label}" (${e.lr}x): ${Math.round(old * 100)}%→${Math.round(belief * 100)}%</div>`;
});
const pct = Math.round(belief * 100);
document.getElementById(s.valId).textContent = pct + "%";
const bar = document.getElementById(s.fillId);
bar.style.width = pct + "%";
bar.style.background =
pct > 70 ? s.colors[2] : pct > 40 ? s.colors[1] : s.colors[0];
log.scrollTop = log.scrollHeight;
}
renderCards(0);
renderCards(1);
// === Section 8: Expandable cards ===
function toggleExp(el) {
const was = el.classList.contains("active");
el.parentElement
.querySelectorAll(".exp-card")
.forEach((c) => c.classList.remove("active"));
if (!was) el.classList.add("active");
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment