digodigo / index.html
ICC99's picture
Upload index.html
4325cd4 verified
Raw
History Blame Contribute Delete
11.5 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Digoxin Dosing Demo</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@500&display=swap" rel="stylesheet">
<style>
:root{
--bg:#0f1417; --panel:#161d22; --panel-2:#1d262c; --line:#2a363d;
--ink:#e8eef0; --ink-soft:#9fb0b6; --accent:#3fb7a4; --accent-deep:#2a8a7c;
--warn:#e0a03f; --danger:#d96b5b; --ok:#5cc08e;
}
*{box-sizing:border-box}
body{
margin:0; background:var(--bg);
background-image:radial-gradient(circle at 15% 0%,rgba(63,183,164,.07),transparent 45%);
color:var(--ink); font-family:"IBM Plex Sans",sans-serif; line-height:1.5;
padding:32px 16px;
}
.wrap{max-width:880px;margin:0 auto}
header{margin-bottom:24px}
.kicker{font-family:"IBM Plex Mono",monospace;font-size:12px;letter-spacing:.18em;
text-transform:uppercase;color:var(--accent)}
h1{font-family:"Fraunces",serif;font-weight:600;font-size:38px;margin:.15em 0 .1em;letter-spacing:-.01em}
.sub{color:var(--ink-soft);font-size:15px;max-width:60ch}
.demo-flag{display:inline-block;margin-top:12px;background:rgba(217,107,91,.13);
border:1px solid rgba(217,107,91,.4);color:var(--danger);
font-size:12px;font-family:"IBM Plex Mono",monospace;padding:6px 11px;border-radius:6px}
.grid{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:680px){.grid{grid-template-columns:1fr}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:22px}
.card h2{font-family:"Fraunces",serif;font-weight:600;font-size:20px;margin:0 0 16px}
label{display:block;font-size:13px;color:var(--ink-soft);margin:0 0 5px;font-weight:500}
.field{margin-bottom:14px}
input,select{width:100%;background:var(--panel-2);border:1px solid var(--line);
color:var(--ink);border-radius:8px;padding:10px 12px;font-size:15px;font-family:inherit}
input:focus,select:focus{outline:none;border-color:var(--accent)}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.seg{display:flex;gap:6px;flex-wrap:wrap}
.seg button{flex:1;min-width:64px;background:var(--panel-2);border:1px solid var(--line);
color:var(--ink-soft);padding:9px 6px;border-radius:8px;cursor:pointer;font-size:13px;font-family:inherit}
.seg button.on{background:var(--accent-deep);border-color:var(--accent);color:#fff}
.calc{width:100%;margin-top:6px;background:var(--accent);color:#06201c;border:none;
padding:14px;border-radius:10px;font-size:16px;font-weight:600;cursor:pointer;font-family:inherit;
letter-spacing:.02em}
.calc:hover{background:#4fd0bb}
#out{margin-top:18px}
.result-head{font-family:"Fraunces",serif;font-size:15px;color:var(--ink-soft);margin-bottom:4px}
.dose-big{font-family:"Fraunces",serif;font-weight:700;font-size:34px;color:var(--accent);line-height:1.1}
.dose-unit{font-size:16px;color:var(--ink-soft);font-weight:400}
.block{border-top:1px solid var(--line);padding:16px 0}
.block:first-child{border-top:none;padding-top:0}
.pk{font-family:"IBM Plex Mono",monospace;font-size:12.5px;color:var(--ink-soft);
background:var(--panel-2);padding:12px;border-radius:8px;margin-top:10px;white-space:pre-wrap}
.note{display:flex;gap:9px;align-items:flex-start;font-size:13.5px;margin:7px 0;color:var(--ink)}
.note .dot{flex:none;width:7px;height:7px;border-radius:50%;margin-top:7px;background:var(--accent)}
.note.warn .dot{background:var(--warn)} .note.danger .dot{background:var(--danger)}
.placeholder{color:var(--ink-soft);font-size:14px;text-align:center;padding:30px 10px}
footer{margin-top:26px;font-size:12px;color:var(--ink-soft);line-height:1.7}
footer code{font-family:"IBM Plex Mono",monospace;color:var(--accent)}
</style>
</head>
<body>
<div class="wrap">
<header>
<div class="kicker">Pharmacokinetic Demo</div>
<h1>Digoxin Dosing</h1>
<p class="sub">Estimates a steady-state maintenance dose (Jusko / Koup-Jusko 1-compartment model) and a weight-based loading dose, with administration notes.</p>
<div class="demo-flag">DEMONSTRATION ONLY — NOT FOR CLINICAL USE</div>
</header>
<div class="grid">
<div class="card">
<h2>Patient</h2>
<div class="row2">
<div class="field"><label>Age (years)</label><input id="age" type="number" value="68"></div>
<div class="field"><label>Weight (kg)</label><input id="wt" type="number" value="80"></div>
</div>
<div class="row2">
<div class="field"><label>Height (cm)</label><input id="ht" type="number" value="170"></div>
<div class="field"><label>S. creatinine (mg/dL)</label><input id="scr" type="number" step="0.1" value="1.0"></div>
</div>
<div class="field">
<label>Sex</label>
<div class="seg" id="sex"><button data-v="m" class="on">Male</button><button data-v="f">Female</button></div>
</div>
<div class="field">
<label>Indication</label>
<div class="seg" id="ind"><button data-v="hf" class="on">Heart failure</button><button data-v="af">Atrial fib.</button><button data-v="both">Both</button></div>
</div>
<div class="field">
<label>Dosage form</label>
<div class="seg" id="form"><button data-v="tab" class="on">Tablet</button><button data-v="iv">IV</button><button data-v="elx">Elixir</button></div>
</div>
<div class="field">
<label>Target serum level (ng/mL)</label>
<input id="target" type="number" step="0.1" value="0.7">
</div>
<button class="calc" onclick="calc()">Calculate dose</button>
</div>
<div class="card">
<h2>Result</h2>
<div id="out"><div class="placeholder">Enter patient data and press Calculate.</div></div>
</div>
</div>
<footer>
Maintenance: Jusko Vd = 226 + [(298·CrCl)/(29.1+CrCl)], steady-state dose = (Css·Cl·&tau;)/F. CrCl by Cockcroft-Gault; lean weight by Devine. Bioavailability F: tablet 0.75, IV 1.0, elixir 0.8. Loading: weight-based (PO ~10 mcg/kg, IV ~8–12 mcg/kg lean weight). Therapeutic range 0.5–2 ng/mL; ≤0.9 ng/mL preferred.<br>
To host: create a HuggingFace Space (SDK = <code>Static</code>) and upload this file as <code>index.html</code>.<br>
Not valid for dialysis, acute/unstable renal function, or interacting drugs (amiodarone, verapamil, quinidine, macrolides). Demonstration only.
</footer>
</div>
<script>
const pick=(id)=>{const el=document.getElementById(id);
el.querySelectorAll('button').forEach(b=>b.onclick=()=>{
el.querySelectorAll('button').forEach(x=>x.classList.remove('on'));b.classList.add('on');});};
['sex','ind','form'].forEach(pick);
const sel=(id)=>document.getElementById(id).querySelector('.on').dataset.v;
const round=(n,d=4)=>Math.round(n*Math.pow(10,d))/Math.pow(10,d);
// nearest practical daily dose from available strengths (mg)
const strengths=[0.0625,0.125,0.1875,0.25,0.375,0.5];
function nearest(mg){return strengths.reduce((a,b)=>Math.abs(b-mg)<Math.abs(a-mg)?b:a);}
function calc(){
const age=+age_v('age'), wt=+age_v('wt'), ht=+age_v('ht'), scr=+age_v('scr'), target=+age_v('target');
const sex=sel('sex'), ind=sel('ind'), form=sel('form');
const out=document.getElementById('out');
if(!(age>0&&wt>0&&ht>0&&scr>0&&target>0)){out.innerHTML='<div class="placeholder">Please fill every field with a positive number.</div>';return;}
// Devine ideal body weight (kg) from height in inches
const inch=ht/2.54;
let ibw=(sex==='m'?50:45.5)+2.3*Math.max(0,(inch-60));
ibw=round(ibw,1);
const dosingWt=Math.min(wt,ibw); // use lower of actual / ideal
// Cockcroft-Gault CrCl (mL/min)
let crcl=((140-age)*dosingWt)/(72*scr); if(sex==='f') crcl*=0.85;
crcl=Math.max(0,round(crcl,1));
// Jusko volume of distribution (L), BSA-normalized term omitted (per-1.73 simplification)
const vd=round(226+(298*crcl)/(29.1+crcl),1); // L (approx, normalized)
// Clearance (L/h): renal + non-renal. HF lowers non-renal clearance.
const F={tab:0.75,iv:1.0,elx:0.8}[form];
const nonRenal=(ind==='hf')?20:40; // mL/min, HF reduces non-renal clearance
const clMlMin=1.303*crcl+nonRenal;
const clLh=round(clMlMin*60/1000,3); // L/h
const tau=24; // h
// Steady-state maintenance dose (mg/day). Css in ng/mL == mcg/L; result mcg -> mg
const doseMcg=(target*clLh*tau)/F; // mcg/day
const doseMg=round(doseMcg/1000,4);
const practical=nearest(doseMg);
// Loading dose (lean weight based), only flag for AF / both; HF usually none
const ldPerKg=(form==='iv')?10:12; // mcg/kg midpoint
const ldTotal=round(ibw*ldPerKg/1000,3); // mg
const showLoad=(ind!=='hf');
// Notes
let notes=[];
if(crcl<30) notes.push(['danger','CrCl below 30 mL/min — high accumulation risk. Model not validated here; expect a reduced dose and closer monitoring.']);
else if(crcl<60) notes.push(['warn','Reduced renal function (CrCl '+crcl+' mL/min) — digoxin is renally cleared, so dose runs lower.']);
if(wt<ibw*0.9 || wt>ibw*1.3) notes.push(['warn','Actual weight differs from ideal; lean weight ('+ibw+' kg) used for dosing.']);
if(target>0.9) notes.push(['warn','Target above 0.9 ng/mL. Current guidance prefers ≤0.9 ng/mL for heart failure.']);
notes.push(['','Narrow therapeutic index. Check a serum level ≥6 h post-dose; steady state takes ~1 week (longer if renal function reduced).']);
if(form==='iv') notes.push(['','IV form: oral dose × 1.25 ≈ IV equivalent when switching.']);
if(ind!=='hf') notes.push(['','Loading splits as ½ now, then ¼ every 6–8 h ×2; reassess rate and toxicity between doses.']);
let html='';
html+='<div class="block"><div class="result-head">Maintenance dose</div>'+
'<div class="dose-big">'+practical+' <span class="dose-unit">mg / day</span></div>'+
'<div style="font-size:13px;color:var(--ink-soft);margin-top:4px">nearest available strength · model estimate '+doseMg+' mg/day</div></div>';
if(showLoad){
html+='<div class="block"><div class="result-head">Loading dose (total)</div>'+
'<div style="font-size:22px;font-family:Fraunces,serif;font-weight:600;color:var(--ok)">'+ldTotal+' <span class="dose-unit">mg</span></div>'+
'<div style="font-size:13px;color:var(--ink-soft);margin-top:4px">'+(form==='iv'?'IV':'oral')+', lean-weight based · give as ½ + ¼ + ¼</div></div>';
} else {
html+='<div class="block"><div class="result-head">Loading dose</div>'+
'<div style="font-size:15px;color:var(--ink)">Not routinely used for heart failure (per ACC/AHA).</div></div>';
}
html+='<div class="block"><div class="result-head">Administration notes</div>';
notes.forEach(n=>html+='<div class="note '+(n[0])+'"><span class="dot"></span><span>'+n[1]+'</span></div>');
html+='</div>';
html+='<div class="block"><div class="result-head">Derived values</div><div class="pk">'+
'Lean (Devine) weight : '+ibw+' kg\n'+
'Dosing weight : '+dosingWt+' kg\n'+
'CrCl (Cockcroft-G.) : '+crcl+' mL/min\n'+
'Volume of dist. (Vd) : '+vd+' L\n'+
'Clearance : '+clLh+' L/h\n'+
'Bioavailability (F) : '+F+
'</div></div>';
out.innerHTML=html;
}
function age_v(id){return document.getElementById(id).value;}
</script>
</body>
</html>