Add hot-tub-treatment cheatsheet HTML
· 1 month ago
60ec89504e1efef10a054830ae295add92c6e751
Parent:
bdfd97549
Add a standalone hot-tub-treatment.html cheatsheet that provides practical spa maintenance guidance: target chemistry ranges, test-strip interpretation, sanitizer decision tree, interactive dosing calculator, startup and biofilm purge protocols, Pseudomonas folliculitis response, troubleshooting, chemical reference, and maintenance cadence. Includes responsive styles, print-friendly rules, an inline dosing calculator (bleach/bicarb/dichlor/shock/hyperchlorinate/Ahh-some), checklist persistence via localStorage, and controls to expand/collapse/print/reset. Intended as a single-file reference for residential spa owners.
1 file changed +863 −0
- hot-tub-treatment.html +863 −0
Diff
--- /dev/null +++ b/hot-tub-treatment.html @@ -0,0 +1,863 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"/> +<meta name="viewport" content="width=device-width, initial-scale=1.0"/> +<title>Hot Tub Treatment: Water Chemistry, Sanitizer Strategy, and Pseudomonas Remediation</title> +<meta name="description" content="Practical hot tub maintenance reference: target chemistry ranges, interactive dosing calculator, biofilm purge protocol, Pseudomonas folliculitis treatment, and sanitizer strategy decisions."/> +<meta name="keywords" content="hot tub chemistry, spa maintenance, free chlorine, total alkalinity, cyanuric acid, dichlor, sodium bicarbonate, Pseudomonas folliculitis, biofilm purge, Ahh-Some, ozonator, bromine, chlorhexidine, hot tub shock, water testing"/> +<link rel="canonical" href="https://cheatsheets.davidveksler.com/hot-tub-treatment.html"/> + +<meta property="og:title" content="Hot Tub Treatment: Chemistry, Sanitizer, and Pseudomonas Remediation"/> +<meta property="og:description" content="Practical hot tub maintenance: target ranges, dosing calculator, biofilm purge, Pseudomonas folliculitis treatment, sanitizer decisions."/> +<meta property="og:type" content="website"/> +<meta property="og:url" content="https://cheatsheets.davidveksler.com/hot-tub-treatment.html"/> +<meta property="og:image" content="images/hot-tub-treatment.png"/> +<meta property="og:image:alt" content="Hot tub treatment cheatsheet covering water chemistry, sanitizer strategy, and Pseudomonas remediation"/> + +<meta name="twitter:card" content="summary_large_image"/> +<meta name="twitter:title" content="Hot Tub Treatment Cheatsheet"/> +<meta name="twitter:description" content="Chemistry targets, dosing calculator, biofilm purge protocol, and Pseudomonas folliculitis response for hot tub owners."/> +<meta name="twitter:image" content="images/hot-tub-treatment.png"/> +<meta name="twitter:creator" content="@heroiclife"/> + +<script type="application/ld+json"> +{ + "@context": "https://schema.org", + "@type": "TechArticle", + "headline": "Hot Tub Treatment: Water Chemistry, Sanitizer Strategy, and Pseudomonas Remediation", + "description": "Practical reference for hot tub owners covering water chemistry targets, sanitizer strategy decisions, biofilm purge protocols, Pseudomonas folliculitis treatment, and an interactive dosing calculator.", + "author": {"@type": "Person", "name": "David Veksler (AI Generated)"}, + "publisher": {"@type": "Organization", "name": "David Veksler Cheatsheets"}, + "datePublished": "2026-05-20", + "dateModified": "2026-05-20", + "keywords": "hot tub chemistry, spa maintenance, free chlorine, total alkalinity, cyanuric acid, dichlor, sodium bicarbonate, Pseudomonas folliculitis, biofilm purge, Ahh-Some, ozonator, bromine, chlorhexidine" +} +</script> + +<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"/> +<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css" rel="stylesheet"/> + +<style> +:root { + --water-deep: #0a4f6e; + --water-mid: #1387a8; + --water-light: #5cc4e0; + --water-bg: #eaf6fb; + --warning: #d97706; + --danger: #b91c1c; + --ok: #15803d; + --neutral-bg: #f5f9fc; +} + +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + background: linear-gradient(180deg, var(--water-bg) 0%, #ffffff 600px) no-repeat; + color: #1a2433; + line-height: 1.55; +} + +.page-header { + background: linear-gradient(135deg, var(--water-deep) 0%, var(--water-mid) 100%); + color: white; + padding: 2.5rem 1rem 2rem; + margin-bottom: 1.5rem; + border-radius: 0 0 12px 12px; +} +.page-header h1 { font-weight: 700; margin-bottom: 0.5rem; } +.page-header .subtitle { opacity: 0.92; font-size: 1.05rem; max-width: 780px; } +.page-header .meta { font-size: 0.85rem; opacity: 0.75; margin-top: 0.75rem; } + +.section-card { + background: white; + border: 1px solid #d6e4ee; + border-radius: 10px; + margin-bottom: 1rem; + overflow: hidden; + box-shadow: 0 1px 3px rgba(10,79,110,0.05); +} +.section-header { + background: var(--neutral-bg); + padding: 0.85rem 1.15rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid transparent; + transition: background 0.15s; +} +.section-header:hover { background: #e8f1f7; } +.section-header h2 { + font-size: 1.15rem; + margin: 0; + font-weight: 600; + color: var(--water-deep); + display: flex; + align-items: center; + gap: 0.5rem; +} +.section-header .toggle-icon { color: var(--water-mid); transition: transform 0.2s; } +.section-card.open .section-header { border-bottom-color: #d6e4ee; } +.section-card.open .toggle-icon { transform: rotate(180deg); } +.section-body { padding: 1.15rem 1.25rem; display: none; } +.section-card.open .section-body { display: block; } + +.range-table { + width: 100%; + border-collapse: collapse; + margin: 0.5rem 0 1rem; + font-size: 0.92rem; +} +.range-table th { + background: var(--water-deep); + color: white; + padding: 0.55rem 0.7rem; + text-align: left; + font-weight: 600; +} +.range-table td { + padding: 0.55rem 0.7rem; + border-bottom: 1px solid #e3eef5; +} +.range-table tr:last-child td { border-bottom: none; } +.range-table tr:nth-child(even) { background: #fafcfd; } + +.pill { + display: inline-block; + padding: 0.15rem 0.55rem; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 600; + letter-spacing: 0.01em; +} +.pill-ok { background: #dcfce7; color: var(--ok); } +.pill-warn { background: #fef3c7; color: var(--warning); } +.pill-danger { background: #fee2e2; color: var(--danger); } +.pill-info { background: #dbeafe; color: #1e40af; } + +.callout { + border-left: 4px solid; + padding: 0.7rem 1rem; + margin: 0.85rem 0; + border-radius: 4px; + font-size: 0.93rem; +} +.callout-warn { border-color: var(--warning); background: #fffbeb; } +.callout-danger { border-color: var(--danger); background: #fef2f2; } +.callout-info { border-color: var(--water-mid); background: #ecfeff; } +.callout-ok { border-color: var(--ok); background: #f0fdf4; } +.callout strong { display: block; margin-bottom: 0.2rem; } + +.calc-card { + background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); + border: 2px solid var(--water-light); + border-radius: 12px; + padding: 1.25rem; +} +.calc-input-row { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; + flex-wrap: wrap; +} +.calc-input-row label { font-weight: 600; color: var(--water-deep); } +.calc-input-row input, .calc-input-row select { + border: 1px solid var(--water-light); + border-radius: 6px; + padding: 0.4rem 0.65rem; + font-size: 1rem; + width: 120px; +} +.calc-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 0.75rem; + margin-top: 1rem; +} +.calc-item { + background: white; + border-radius: 8px; + padding: 0.85rem; + border: 1px solid #cfe5f0; +} +.calc-item .label { font-size: 0.85rem; color: #5a6c7d; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; } +.calc-item .value { font-size: 1.4rem; font-weight: 700; color: var(--water-deep); margin: 0.2rem 0; } +.calc-item .note { font-size: 0.82rem; color: #5a6c7d; } + +.checklist { list-style: none; padding-left: 0; } +.checklist li { + padding: 0.45rem 0; + display: flex; + align-items: flex-start; + gap: 0.6rem; +} +.checklist input[type="checkbox"] { + margin-top: 0.3rem; + width: 1.05rem; + height: 1.05rem; + cursor: pointer; + accent-color: var(--water-mid); +} +.checklist label { cursor: pointer; flex: 1; } +.checklist li.done label { text-decoration: line-through; opacity: 0.55; } + +.protocol-step { + background: #fafcfd; + border-left: 3px solid var(--water-mid); + padding: 0.7rem 1rem; + margin: 0.5rem 0; + border-radius: 0 6px 6px 0; +} +.protocol-step .step-num { + display: inline-block; + background: var(--water-mid); + color: white; + width: 24px; + height: 24px; + border-radius: 50%; + text-align: center; + font-weight: 700; + font-size: 0.85rem; + line-height: 24px; + margin-right: 0.5rem; +} + +.decision-tree { + background: #fafcfd; + border-radius: 8px; + padding: 1rem; + font-size: 0.95rem; +} +.decision-tree .branch { margin: 0.4rem 0 0.4rem 1rem; padding-left: 0.75rem; border-left: 2px solid var(--water-light); } +.decision-tree .question { font-weight: 600; color: var(--water-deep); } +.decision-tree .answer { color: var(--ok); font-weight: 600; } + +.product-pill { + display: inline-block; + background: #f0f9ff; + border: 1px solid #bae6fd; + padding: 0.2rem 0.5rem; + border-radius: 4px; + font-size: 0.85rem; + font-family: ui-monospace, "SF Mono", Consolas, monospace; + color: var(--water-deep); +} + +.controls-bar { + background: white; + padding: 0.75rem 1rem; + border-radius: 8px; + margin-bottom: 1rem; + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + align-items: center; + border: 1px solid #d6e4ee; +} +.controls-bar button { + border: 1px solid var(--water-light); + background: white; + color: var(--water-deep); + padding: 0.35rem 0.85rem; + border-radius: 6px; + font-size: 0.88rem; + cursor: pointer; + font-weight: 600; + transition: all 0.15s; +} +.controls-bar button:hover { background: var(--water-light); color: white; } + +footer { + margin-top: 2.5rem; + padding: 1.5rem 1rem; + text-align: center; + color: #6b7c8c; + font-size: 0.88rem; + border-top: 1px solid #d6e4ee; +} + +@media print { + body { background: white; } + .page-header { background: var(--water-deep) !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; } + .section-card { break-inside: avoid; page-break-inside: avoid; box-shadow: none; } + .section-body { display: block !important; } + .toggle-icon, .controls-bar { display: none; } + .calc-card { background: white !important; } + .callout { break-inside: avoid; } +} + +@media (max-width: 640px) { + .page-header { padding: 1.5rem 1rem; } + .page-header h1 { font-size: 1.5rem; } + .calc-input-row { flex-direction: column; align-items: flex-start; } + .calc-input-row input, .calc-input-row select { width: 100%; } +} +</style> +</head> + +<body> + +<div class="page-header"> + <div class="container"> + <h1><i class="bi bi-droplet-half"></i> Hot Tub Treatment</h1> + <div class="subtitle">Water chemistry, sanitizer strategy, biofilm remediation, and <em>Pseudomonas</em> folliculitis response. Practical numbers, not fluff.</div> + <div class="meta">Updated 2026-05-20 · Assumes residential spa, 300-500 gal</div> + </div> +</div> + +<div class="container" style="max-width: 1100px;"> + +<div class="controls-bar"> + <button onclick="expandAll()"><i class="bi bi-arrows-expand"></i> Expand All</button> + <button onclick="collapseAll()"><i class="bi bi-arrows-collapse"></i> Collapse All</button> + <button onclick="window.print()"><i class="bi bi-printer"></i> Print</button> + <button onclick="resetChecks()"><i class="bi bi-arrow-counterclockwise"></i> Reset Checklists</button> +</div> + +<!-- TARGET RANGES --> +<div class="section-card open" id="sec-ranges"> + <div class="section-header" onclick="toggleSection('sec-ranges')"> + <h2><i class="bi bi-bullseye"></i> Target Chemistry Ranges</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <table class="range-table"> + <thead> + <tr><th>Parameter</th><th>Low</th><th>Ideal</th><th>High</th><th>Notes</th></tr> + </thead> + <tbody> + <tr><td><strong>pH</strong></td><td>< 7.2</td><td>7.4 - 7.6</td><td>> 7.8</td><td>Drifts up over time from CO₂ off-gassing</td></tr> + <tr><td><strong>Free Chlorine (FC)</strong></td><td>< 1 ppm</td><td>3 - 5 ppm</td><td>> 10 ppm</td><td>Lower if ozone working; never bathe with 0</td></tr> + <tr><td><strong>Bromine (alt to Cl)</strong></td><td>< 2 ppm</td><td>3 - 5 ppm</td><td>> 8 ppm</td><td>Better choice without ozone</td></tr> + <tr><td><strong>Total Alkalinity (TA)</strong></td><td>< 60 ppm</td><td>80 - 120 ppm</td><td>> 180 ppm</td><td>Low TA = pH bouncing; high = scale risk</td></tr> + <tr><td><strong>Calcium Hardness</strong></td><td>< 100 ppm</td><td>150 - 250 ppm</td><td>> 400 ppm</td><td>Below 100 = foaming, equipment etching</td></tr> + <tr><td><strong>Cyanuric Acid (CYA)</strong></td><td>0 ppm</td><td>0 - 20 ppm</td><td>> 30 ppm</td><td>Keep LOW in spas; binds Cl in hot water</td></tr> + <tr><td><strong>Temperature</strong></td><td>< 95°F</td><td>97 - 104°F</td><td>> 104°F</td><td>104°F max for adults; 97-100°F for kids</td></tr> + </tbody> + </table> + <div class="callout callout-info"> + <strong>Why CYA stays low in a spa (vs. high in a pool)</strong> + In 100°F+ water, CYA-bound chlorine is dramatically less effective as a sanitizer. The "stabilizer" you want in an outdoor pool is a liability in a hot tub. Use unstabilized chlorine (bleach or cal-hypo) rather than dichlor or trichlor. + </div> + </div> +</div> + +<!-- TEST STRIP INTERPRETATION --> +<div class="section-card" id="sec-strip"> + <div class="section-header" onclick="toggleSection('sec-strip')"> + <h2><i class="bi bi-eyedropper"></i> Test Strip Interpretation</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <p>Reading a standard 4-way AquaChek-style strip (FC + pH + TA + CYA):</p> + <ol> + <li>Dip the strip in still water for 1 second, remove, hold horizontal (don't shake).</li> + <li>Wait <strong>15 seconds</strong> exactly. Reading too early or late gives bad results.</li> + <li>Compare to bottle chart in <em>natural light</em>, not under yellow indoor light.</li> + <li>End pad = pH. Pad nearest handle = CYA. (Standard layout.)</li> + </ol> + <table class="range-table"> + <thead><tr><th>Pad</th><th>Color shift</th><th>What it tells you</th></tr></thead> + <tbody> + <tr><td>pH</td><td>Yellow → orange → red</td><td>Yellow = acidic, red = alkaline</td></tr> + <tr><td>FC</td><td>White → light purple → dark purple</td><td>White = no sanitizer (do not soak)</td></tr> + <tr><td>TA</td><td>Yellow → green → teal</td><td>Yellow = no buffering capacity</td></tr> + <tr><td>CYA</td><td>Yellow → orange → magenta → purple</td><td>Yellow ≈ 0 ppm (correct for spa)</td></tr> + </tbody> + </table> + <div class="callout callout-warn"> + <strong>Strip limitations</strong> + Strips are accurate to within roughly ±0.5 pH, ±20 ppm TA, ±1 ppm FC. For precise diagnosis (e.g., diagnosing chlorine lock), use a Taylor K-2006 drop test kit instead. + </div> + </div> +</div> + +<!-- SANITIZER STRATEGY --> +<div class="section-card" id="sec-sanitizer"> + <div class="section-header" onclick="toggleSection('sec-sanitizer')"> + <h2><i class="bi bi-shield-check"></i> Sanitizer Strategy Decision</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <div class="decision-tree"> + <div class="question">Q1: Does your spa have a working corona-discharge ozonator?</div> + <div class="branch"> + <div class="question">Yes →</div> + <div class="branch"> + <span class="answer">Run chlorine (bleach). FC residual 1-2 ppm is enough; ozone handles primary oxidation.</span> + </div> + <div class="question">No (or unknown) → Q2</div> + <div class="branch"> + <div class="question">Q2: Do you run the tub at 102°F+?</div> + <div class="branch"> + <span class="answer">Yes → Switch to bromine. Better stability in hot water, less off-gassing, less daily fiddling.</span> + </div> + <div class="question">No (running 97-100°F) →</div> + <div class="branch"> + <span class="answer">Either works. Bleach is cheaper and simpler if you'll dose daily.</span> + </div> + </div> + </div> + </div> + <h5 class="mt-3">Ozonator lifespan check</h5> + <p>Corona-discharge ozone cells typically last 2-5 years. Test:</p> + <ol> + <li>Run circulation pump only (jets off), cover on, 30+ minutes.</li> + <li>Lift cover and sniff near lowest jet. Sharp chlorine-like bite = working.</li> + <li>No smell = dead. Replacement modules run $150-300 and are usually plug-and-play.</li> + </ol> + <table class="range-table"> + <thead><tr><th></th><th>Chlorine (bleach)</th><th>Bromine</th><th>Dichlor</th></tr></thead> + <tbody> + <tr><td>Cost/month</td><td>$5-10</td><td>$10-15</td><td>$10-15</td></tr> + <tr><td>Adds CYA?</td><td>No <span class="pill pill-ok">good</span></td><td>No</td><td>Yes <span class="pill pill-danger">bad in spa</span></td></tr> + <tr><td>Hot water stability</td><td>Moderate</td><td>High <span class="pill pill-ok">best</span></td><td>Moderate</td></tr> + <tr><td>Daily attention</td><td>Yes (2-3 oz daily)</td><td>Minimal (floater)</td><td>Yes</td></tr> + <tr><td>Best for</td><td>Cooler spa, ozone-equipped</td><td>Hot spa, no ozone</td><td>Avoid in spa</td></tr> + </tbody> + </table> + </div> +</div> + +<!-- DOSING CALCULATOR --> +<div class="section-card open" id="sec-calc"> + <div class="section-header" onclick="toggleSection('sec-calc')"> + <h2><i class="bi bi-calculator"></i> Interactive Dosing Calculator</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <div class="calc-card"> + <div class="calc-input-row"> + <label for="tubVol">Tub volume:</label> + <input type="number" id="tubVol" value="400" min="100" max="3000" step="25" oninput="updateCalc()"/> + <span>gallons</span> + </div> + <div class="calc-input-row"> + <label for="currentFC">Current FC:</label> + <input type="number" id="currentFC" value="0" min="0" max="20" step="0.5" oninput="updateCalc()"/> + <span>ppm</span> + + <label for="targetFC">Target FC:</label> + <input type="number" id="targetFC" value="5" min="1" max="30" step="0.5" oninput="updateCalc()"/> + <span>ppm</span> + </div> + <div class="calc-input-row"> + <label for="currentTA">Current TA:</label> + <input type="number" id="currentTA" value="60" min="0" max="300" step="10" oninput="updateCalc()"/> + <span>ppm</span> + + <label for="targetTA">Target TA:</label> + <input type="number" id="targetTA" value="100" min="60" max="180" step="10" oninput="updateCalc()"/> + <span>ppm</span> + </div> + + <div class="calc-grid"> + <div class="calc-item"> + <div class="label">Baking Soda (TA up)</div> + <div class="value" id="bicarbDose">—</div> + <div class="note" id="bicarbNote">Costco sodium bicarbonate = same as "Alkalinity Up"</div> + </div> + <div class="calc-item"> + <div class="label">Bleach 8.25% (FC up)</div> + <div class="value" id="bleachDose">—</div> + <div class="note">Plain unscented Clorox. NO "splash-less" or scented.</div> + </div> + <div class="calc-item"> + <div class="label">Dichlor alternative (FC up)</div> + <div class="value" id="dichlorDose">—</div> + <div class="note" id="dichlorNote">Avoid in spas; builds CYA</div> + </div> + <div class="calc-item"> + <div class="label">Shock dose (10 ppm FC)</div> + <div class="value" id="shockDose">—</div> + <div class="note">Use for weekly oxidation or post-heavy-use</div> + </div> + <div class="calc-item"> + <div class="label">Hyperchlorinate (30 ppm FC)</div> + <div class="value" id="hyperDose">—</div> + <div class="note">For biofilm remediation only. Do not bathe.</div> + </div> + <div class="calc-item"> + <div class="label">Ahh-some purge dose</div> + <div class="value" id="ahhDose">—</div> + <div class="note">16 oz liquid concentrate, 2 oz per 100 gal</div> + </div> + </div> + </div> + <div class="callout callout-info mt-3"> + <strong>Math basis</strong> + Bicarb: 1.4 oz raises TA ~10 ppm per 1000 gal. Bleach 8.25%: 1 oz raises FC ~6 ppm per 100 gal. Dichlor 56%: 0.16 oz raises FC ~10 ppm per 100 gal (also adds ~9 ppm CYA). + </div> + </div> +</div> + +<!-- STARTUP PROTOCOL --> +<div class="section-card" id="sec-startup"> + <div class="section-header" onclick="toggleSection('sec-startup')"> + <h2><i class="bi bi-play-circle"></i> Startup Protocol (New Fill / After Drain)</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <div class="callout callout-danger"> + <strong>Critical: do not bathe within 24 hours of fresh fill</strong> + A tub that sat empty harbors <em>Pseudomonas</em> biofilm in plumbing. First-fill water has zero sanitizer history. Wait until FC has been ≥3 ppm for 24 hours minimum. + </div> + <ul class="checklist" data-storage-key="startup"> + <li><input type="checkbox" id="su1"/><label for="su1">Wipe shell with diluted bleach (1:10) before filling</label></li> + <li><input type="checkbox" id="su2"/><label for="su2">If tub sat empty > 1 month: run Ahh-some purge during first fill</label></li> + <li><input type="checkbox" id="su3"/><label for="su3">Fill with garden hose through the filter compartment (reduces air gaps)</label></li> + <li><input type="checkbox" id="su4"/><label for="su4">Install clean/new filter cartridge</label></li> + <li><input type="checkbox" id="su5"/><label for="su5">Test alkalinity first; adjust to 80-120 ppm with baking soda</label></li> + <li><input type="checkbox" id="su6"/><label for="su6">Test pH; adjust to 7.4-7.6 (dry acid down, soda ash up)</label></li> + <li><input type="checkbox" id="su7"/><label for="su7">Shock to 10 ppm FC with bleach; jets on 60 min</label></li> + <li><input type="checkbox" id="su8"/><label for="su8">Set heater to target temp; let stabilize 4-8 hr</label></li> + <li><input type="checkbox" id="su9"/><label for="su9">Retest FC at 24 hr; verify holding ≥ 3 ppm</label></li> + <li><input type="checkbox" id="su10"/><label for="su10">Only then: bathers OK</label></li> + </ul> + <h5 class="mt-3">Order matters</h5> + <p>Adjust TA <strong>before</strong> pH. Bicarb raises both, so chasing pH first wastes chemicals. Same with shock — never add sanitizer when pH is below 7.0 (chlorine off-gasses as Cl₂) or above 8.0 (mostly inactive OCl⁻ form).</p> + </div> +</div> + +<!-- BIOFILM PURGE --> +<div class="section-card" id="sec-purge"> + <div class="section-header" onclick="toggleSection('sec-purge')"> + <h2><i class="bi bi-water"></i> Biofilm Purge Protocol</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <p><strong>When to purge:</strong> every water change (3-4 months), after any contamination event (folliculitis outbreak, cloudy water that won't clear), after extended downtime (winter storage), or when buying a used tub.</p> + + <h5>Why a regular shock isn't enough</h5> + <p>Hot tub plumbing develops biofilm — a polysaccharide matrix that bacteria secrete to shield themselves from sanitizer. Chlorine kills planktonic (free-floating) bacteria in seconds but can't penetrate the biofilm matrix. A surfactant-based purge product is required to physically disrupt the matrix so chlorine can reach what's inside.</p> + + <h5>Product comparison</h5> + <table class="range-table"> + <thead><tr><th>Product</th><th>Form</th><th>Effectiveness</th><th>Cost/purge</th></tr></thead> + <tbody> + <tr><td>Ahh-some (Unique Solutions / King Tech)</td><td>Gel or 16 oz liquid</td><td><span class="pill pill-ok">Best</span></td><td>$3-5</td></tr> + <tr><td>Oh Yuk</td><td>Liquid</td><td><span class="pill pill-warn">Good</span> (2-cycle)</td><td>$4-6</td></tr> + <tr><td>Natural Chemistry Pipe Cleanse</td><td>Liquid</td><td><span class="pill pill-warn">Moderate</span></td><td>$5-8</td></tr> + <tr><td>TSP + bleach (DIY)</td><td>Powder + liquid</td><td><span class="pill pill-warn">Acceptable</span></td><td>$3-5</td></tr> + </tbody> + </table> + + <h5>Full purge sequence (Ahh-some 16 oz liquid)</h5> + <div class="protocol-step"><span class="step-num">1</span>Remove filter cartridge. Set aside in trash; do not reinstall.</div> + <div class="protocol-step"><span class="step-num">2</span>Add 2 oz Ahh-some liquid per 100 gal (8 oz for 400 gal). Pour into water near a jet inlet.</div> + <div class="protocol-step"><span class="step-num">3</span>Add 20 oz bleach 8.25% (per 400 gal) simultaneously to hyperchlorinate to ~30 ppm during the purge.</div> + <div class="protocol-step"><span class="step-num">4</span>Run all jets + air on high, 60 minutes. Expect heavy foam and brown/gray gunk surfacing — that's biofilm release.</div> + <div class="protocol-step"><span class="step-num">5</span>Drain completely via the spa's drain plug. Run pump dry briefly to clear lines.</div> + <div class="protocol-step"><span class="step-num">6</span>Wipe shell with 1:10 bleach solution. Pay attention to waterline, jet faces, filter well.</div> + <div class="protocol-step"><span class="step-num">7</span>Refill, install new filter, restart chemistry per Startup Protocol.</div> + + <div class="callout callout-warn"> + <strong>Heavy contamination = double purge</strong> + For known pathogen events (folliculitis outbreak, after long winter storage), run two consecutive Ahh-some cycles separated by a drain/refill. Each cycle releases progressively less material — that's the visual confirmation it worked. + </div> + </div> +</div> + +<!-- FOLLICULITIS --> +<div class="section-card" id="sec-foll"> + <div class="section-header" onclick="toggleSection('sec-foll')"> + <h2><i class="bi bi-bandaid"></i> Pseudomonas Folliculitis Response</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <p><em>Pseudomonas aeruginosa</em> folliculitis ("hot tub rash") presents 1-4 days after exposure as red, itchy bumps concentrated in swimsuit-covered areas. Self-limiting in 7-14 days but actively treatable to shorten duration and prevent worsening.</p> + + <h5 class="mt-3">First-line interventions (do all)</h5> + <table class="range-table"> + <thead><tr><th>Intervention</th><th>Mechanism</th><th>Protocol</th></tr></thead> + <tbody> + <tr> + <td><span class="product-pill">Chlorhexidine 4% (Hibiclens)</span></td> + <td>Hospital-grade antiseptic, kills <em>Pseudomonas</em> on skin and superficial follicles</td> + <td>Lather on affected areas in shower, 1-2 min contact, rinse. Once daily, 3-5 days. Avoid eyes, ears, mucous membranes.</td> + </tr> + <tr> + <td>Acetic acid compresses (1:1 white vinegar + water)</td> + <td><em>Pseudomonas</em> cannot survive at low pH — same principle as swimmer's ear treatment</td> + <td>Soak washcloth, apply 10-15 min to affected areas, 2-3× daily until rash clears.</td> + </tr> + <tr> + <td>Wash all exposed fabric (hot + bleach)</td> + <td>Prevents re-inoculation; <em>Pseudomonas</em> survives on damp fabric for days</td> + <td>Swimsuits, towels, bath mats, robes. Hot wash, full detergent, 1/2 cup bleach if fabric allows. Sun-dry if possible.</td> + </tr> + <tr> + <td>Keep affected skin dry & uncovered</td> + <td>Occlusion + moisture + warmth = the conditions that caused this</td> + <td>Loose cotton, no tight elastic over rash, change if sweaty.</td> + </tr> + </tbody> + </table> + + <h5 class="mt-3">Do NOT do</h5> + <ul> + <li><strong>No OTC topical antibiotics</strong> (Neosporin, bacitracin, triple-antibiotic). They don't cover <em>Pseudomonas</em> and may select for resistance.</li> + <li><strong>No popping, picking, or scratching</strong> — introduces secondary Staph and drives bacteria deeper.</li> + <li><strong>No shaving, waxing, depilatories</strong> on affected areas until fully resolved.</li> + <li><strong>No pools, hot tubs, lake/river swimming, shared baths</strong> until rash is gone.</li> + <li><strong>No sharing towels</strong> between affected individuals.</li> + </ul> + + <div class="callout callout-danger"> + <strong>Escalate to physician immediately if:</strong> + Fever > 100.4°F · expanding redness around any lesion (draw a Sharpie ring around the worst spot, check at 12 hr) · individual bump becoming firm/nodular/larger than ~5 mm · child or adult acting genuinely unwell (lethargy, deep pain rather than itch) · symptoms still worsening after 4-5 days of treatment. Oral ciprofloxacin or other anti-pseudomonal antibiotics may be needed. + </div> + + <h5 class="mt-3">Prevention going forward</h5> + <ol> + <li>Shower with soap immediately after every soak — don't sit in a contaminated swimsuit.</li> + <li>Maintain FC ≥ 3 ppm always; never bathe if test shows 0 ppm.</li> + <li>Replace filter every 12 months or after any contamination event.</li> + <li>Full Ahh-some purge every 3-4 months minimum.</li> + <li>Drain and refill every 3 months (calculation: refill when TDS reaches roughly 1500 ppm above source water, or quarterly, whichever first).</li> + </ol> + </div> +</div> + +<!-- TEMPERATURE STRATEGY --> +<div class="section-card" id="sec-temp"> + <div class="section-header" onclick="toggleSection('sec-temp')"> + <h2><i class="bi bi-thermometer-half"></i> Temperature Strategy</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <table class="range-table"> + <thead><tr><th>Scenario</th><th>Setpoint</th><th>Chemistry Implications</th></tr></thead> + <tbody> + <tr><td>Adult relaxation</td><td>102 - 104°F</td><td>Highest sanitizer demand, fastest pH drift up</td></tr> + <tr><td>Mixed family (kids)</td><td>97 - 100°F</td><td>~25% lower chlorine demand than 104°F; bromine advantage shrinks</td></tr> + <tr><td>Therapy / soak time only</td><td>100 - 102°F</td><td>Standard setpoint, predictable chemistry</td></tr> + <tr><td>Storage between uses (1+ weeks)</td><td>Off or floor setpoint</td><td>~3-4× longer chlorine residual; minimal heating cost</td></tr> + <tr><td>Decontamination wait period</td><td>Off / lowest possible</td><td>Slows bacterial growth, preserves chlorine</td></tr> + </tbody> + </table> + <div class="callout callout-info"> + <strong>Arrhenius math</strong> + Reaction rates roughly double per 10°C (18°F). Dropping from 104°F to 97°F cuts most chemistry reactions (including bacterial growth AND chlorine decomposition) by ~25-30%. Net: cooler water means longer-lasting sanitizer AND slower pathogen multiplication. + </div> + <div class="callout callout-warn"> + <strong>97-104°F is the <em>Pseudomonas</em> sweet spot</strong> + Lower temperatures don't make a hot tub bacteria-free — they just slow growth. Sanitizer levels still need to be maintained at every setpoint above ~80°F. Running cooler is a chemistry win, not a sanitizer-skip license. + </div> + </div> +</div> + +<!-- TROUBLESHOOTING --> +<div class="section-card" id="sec-trouble"> + <div class="section-header" onclick="toggleSection('sec-trouble')"> + <h2><i class="bi bi-tools"></i> Troubleshooting Common Issues</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <table class="range-table"> + <thead><tr><th>Symptom</th><th>Likely Cause</th><th>Fix</th></tr></thead> + <tbody> + <tr> + <td>Cloudy water</td> + <td>High bather load, dead chlorine, biofilm release, or high TA/calcium</td> + <td>Test all parameters; shock to 10 ppm; clean filter; if persistent → purge plumbing</td> + </tr> + <tr> + <td>Foaming</td> + <td>Body oils, soap residue, low calcium hardness, or surfactant accumulation</td> + <td>Run purge product; raise calcium to 150+ ppm; long-term: rinse off before soaking</td> + </tr> + <tr> + <td>Strong chlorine smell</td> + <td>Chloramines (combined chlorine), not free chlorine. Counter-intuitive: indicates UNDER-chlorination</td> + <td>Shock to 10× combined chlorine reading; "breakpoint chlorination"</td> + </tr> + <tr> + <td>Rotten egg / musty smell</td> + <td>Anaerobic bacteria in plumbing (biofilm)</td> + <td>Full purge + drain + refill. Don't try to chlorinate through it.</td> + </tr> + <tr> + <td>Skin irritation, rash 1-4 days after use</td> + <td><em>Pseudomonas</em> folliculitis (see dedicated section)</td> + <td>Hibiclens protocol + remediate tub before reuse</td> + </tr> + <tr> + <td>Itching during/right after soak</td> + <td>High chloramines, low pH, or chemical sensitivity</td> + <td>Verify pH 7.4-7.6, FC 3-5 (not higher), shock if combined Cl present</td> + </tr> + <tr> + <td>Green or pink-tinted water</td> + <td>Green = algae (rare in covered spa); pink = Methylobacterium biofilm</td> + <td>Shock + scrub + purge cycle</td> + </tr> + <tr> + <td>Chlorine won't hold (drops to 0 fast)</td> + <td>Chlorine lock from CYA, ammonia from bather waste, or biofilm consuming sanitizer</td> + <td>Test CYA; if >30 partial drain; if biofilm suspected → purge</td> + </tr> + <tr> + <td>pH keeps drifting up</td> + <td>Normal CO₂ off-gassing. High aeration (waterfall, air jets) accelerates it</td> + <td>Lower TA to 80 ppm; pH will be more stable. Reduce aeration when not in use.</td> + </tr> + <tr> + <td>Scale on heater / waterline</td> + <td>High calcium + high pH + high temp = CaCO₃ precipitation</td> + <td>Lower pH to 7.2-7.4, drain partial if Ca >400 ppm, descale with citric acid</td> + </tr> + </tbody> + </table> + </div> +</div> + +<!-- CHEMICAL REFERENCE --> +<div class="section-card" id="sec-chems"> + <div class="section-header" onclick="toggleSection('sec-chems')"> + <h2><i class="bi bi-flask"></i> Chemical Quick Reference</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <table class="range-table"> + <thead><tr><th>Brand-name</th><th>Real chemical</th><th>Cheap source</th><th>Per-lb cost</th></tr></thead> + <tbody> + <tr><td>"Alkalinity Up", "TA Increaser"</td><td>Sodium bicarbonate (NaHCO₃)</td><td>Costco baking soda 12 lb</td><td>$0.50/lb vs $3-5/lb branded</td></tr> + <tr><td>"pH Up"</td><td>Sodium carbonate (Na₂CO₃) / soda ash</td><td>Arm & Hammer Super Washing Soda</td><td>$1/lb vs $4/lb branded</td></tr> + <tr><td>"pH Down", "Dry Acid"</td><td>Sodium bisulfate (NaHSO₄)</td><td>Pool store / hardware store</td><td>$2/lb (same product across brands)</td></tr> + <tr><td>"Chlorine Shock", "Pool Shock"</td><td>Calcium hypochlorite (Cal-hypo) or sodium dichloroisocyanurate (dichlor)</td><td>For spa: avoid dichlor; use bleach</td><td>—</td></tr> + <tr><td>"Liquid Chlorine"</td><td>Sodium hypochlorite (NaOCl) 12.5%</td><td>Pool store; commercial bleach</td><td>$3-5/gal</td></tr> + <tr><td>Household bleach</td><td>Sodium hypochlorite 6% or 8.25%</td><td>Costco / Walmart Clorox</td><td>$5-7/gal</td></tr> + <tr><td>"Calcium Increaser"</td><td>Calcium chloride (CaCl₂)</td><td>Damprid refill, ice melt (food-grade)</td><td>$1/lb vs $5/lb branded</td></tr> + <tr><td>"Stabilizer", "Conditioner"</td><td>Cyanuric acid</td><td>Avoid in spa — do not buy</td><td>—</td></tr> + <tr><td>"Non-chlorine shock", "MPS"</td><td>Potassium monopersulfate</td><td>Pool store, same product all brands</td><td>$4-6/lb</td></tr> + </tbody> + </table> + <div class="callout callout-info"> + <strong>Markup reality</strong> + Spa store chemicals are typically 3-5× the cost of the same raw chemical bought as a household product. The active ingredient is identical — the only thing you're paying for is the spa-branded label and smaller packaging. The exceptions: bromine tabs, MPS, and Ahh-some have no household equivalent. + </div> + </div> +</div> + +<!-- WEEKLY MAINTENANCE --> +<div class="section-card" id="sec-weekly"> + <div class="section-header" onclick="toggleSection('sec-weekly')"> + <h2><i class="bi bi-calendar-check"></i> Maintenance Cadence</h2> + <i class="bi bi-chevron-down toggle-icon"></i> + </div> + <div class="section-body"> + <ul class="checklist" data-storage-key="cadence"> + <li><input type="checkbox" id="m1"/><label for="m1"><strong>Daily:</strong> visual check (clarity, odor); dose 1-2 oz bleach if used yesterday</label></li> + <li><input type="checkbox" id="m2"/><label for="m2"><strong>2-3× weekly:</strong> test FC and pH with strip; adjust as needed</label></li> + <li><input type="checkbox" id="m3"/><label for="m3"><strong>Weekly:</strong> shock to 10 ppm FC; rinse filter cartridge with hose</label></li> + <li><input type="checkbox" id="m4"/><label for="m4"><strong>Monthly:</strong> full test (TA, CYA, calcium); deep-clean filter in TSP solution overnight</label></li> + <li><input type="checkbox" id="m5"/><label for="m5"><strong>Quarterly:</strong> Ahh-some purge + drain + refill + new chemistry</label></li> + <li><input type="checkbox" id="m6"/><label for="m6"><strong>Annually:</strong> replace filter cartridge; inspect cover; check ozonator function</label></li> + <li><input type="checkbox" id="m7"/><label for="m7"><strong>Every 2-5 years:</strong> replace ozonator module if equipped</label></li> + </ul> + </div> +</div> + +</div> <!-- end container --> + +<footer> + <p><strong>Hot Tub Treatment Cheatsheet</strong> · Information only, not medical advice · <a href="https://cheatsheets.davidveksler.com/">More cheatsheets</a></p> + <p style="margin-top: 0.5rem; opacity: 0.7;">For any symptom progression beyond mild self-limiting folliculitis, consult a physician.</p> +</footer> + +<script> +function toggleSection(id) { + document.getElementById(id).classList.toggle('open'); +} +function expandAll() { + document.querySelectorAll('.section-card').forEach(c => c.classList.add('open')); +} +function collapseAll() { + document.querySelectorAll('.section-card').forEach(c => c.classList.remove('open')); +} + +// Dosing calculator +function updateCalc() { + const vol = parseFloat(document.getElementById('tubVol').value) || 400; + const curFC = parseFloat(document.getElementById('currentFC').value) || 0; + const tgtFC = parseFloat(document.getElementById('targetFC').value) || 5; + const curTA = parseFloat(document.getElementById('currentTA').value) || 60; + const tgtTA = parseFloat(document.getElementById('targetTA').value) || 100; + + // Baking soda: 1.4 oz raises TA 10 ppm per 1000 gal + const taDelta = Math.max(0, tgtTA - curTA); + const bicarbOz = (taDelta / 10) * 1.4 * (vol / 1000); + const bicarbTbsp = bicarbOz * 2; // ~0.5 oz per tbsp + document.getElementById('bicarbDose').textContent = + taDelta === 0 ? 'No change needed' : `${bicarbOz.toFixed(1)} oz (${bicarbTbsp.toFixed(1)} tbsp)`; + + // Bleach 8.25%: 1 oz raises FC ~6 ppm per 100 gal + const fcDelta = Math.max(0, tgtFC - curFC); + const bleachOz = (fcDelta / 6) * (vol / 100); + const bleachCups = bleachOz / 8; + document.getElementById('bleachDose').textContent = + fcDelta === 0 ? 'No change needed' : `${bleachOz.toFixed(1)} oz (${bleachCups.toFixed(2)} cups)`; + + // Dichlor 56%: 0.16 oz raises FC ~10 ppm per 100 gal; adds 0.9 ppm CYA per ppm FC + const dichlorOz = (fcDelta / 10) * 0.16 * (vol / 100); + const cyaAdded = fcDelta * 0.9; + document.getElementById('dichlorDose').textContent = + fcDelta === 0 ? '—' : `${dichlorOz.toFixed(2)} oz`; + document.getElementById('dichlorNote').textContent = + `Adds ~${cyaAdded.toFixed(1)} ppm CYA. Avoid in spa.`; + + // Shock to 10 ppm with bleach + const shockOz = (10 / 6) * (vol / 100); + document.getElementById('shockDose').textContent = `${shockOz.toFixed(1)} oz bleach`; + + // Hyperchlorinate to 30 ppm + const hyperOz = (30 / 6) * (vol / 100); + const hyperCups = hyperOz / 8; + document.getElementById('hyperDose').textContent = `${hyperOz.toFixed(0)} oz (${hyperCups.toFixed(1)} cups)`; + + // Ahh-some 16 oz liquid: 2 oz per 100 gal + const ahhOz = 2 * (vol / 100); + document.getElementById('ahhDose').textContent = `${ahhOz.toFixed(1)} oz`; +} + +// Checklist persistence with localStorage +function initChecklists() { + document.querySelectorAll('.checklist').forEach(list => { + const key = list.dataset.storageKey; + if (!key) return; + const saved = JSON.parse(localStorage.getItem('htt-' + key) || '{}'); + list.querySelectorAll('input[type="checkbox"]').forEach(cb => { + if (saved[cb.id]) { + cb.checked = true; + cb.closest('li').classList.add('done'); + } + cb.addEventListener('change', () => { + cb.closest('li').classList.toggle('done', cb.checked); + const state = JSON.parse(localStorage.getItem('htt-' + key) || '{}'); + state[cb.id] = cb.checked; + localStorage.setItem('htt-' + key, JSON.stringify(state)); + }); + }); + }); +} + +function resetChecks() { + document.querySelectorAll('.checklist').forEach(list => { + const key = list.dataset.storageKey; + if (key) localStorage.removeItem('htt-' + key); + list.querySelectorAll('input[type="checkbox"]').forEach(cb => { + cb.checked = false; + cb.closest('li').classList.remove('done'); + }); + }); +} + +// Init +updateCalc(); +initChecklists(); +</script> + +</body> +</html>