<style>
tr.drill-parent { cursor: pointer; }
tr.drill-parent:hover > td { background: rgba(32,107,196,.06); }
.drill-arrow { display:inline-block; width:14px; transition: transform .15s; color: var(--tblr-primary); }
tr.drill-agg   > td { background: var(--m-surface-2) !important; }
tr.drill-claim > td { background: var(--m-surface-2) !important; font-size:.8rem; }
tr.drill-claim td.lbl code { font-size:.78rem; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>
<script>
/* Multi-level inline drill-down (e.g. Carrier ▸ Year ▸ Month ▸ Claim).
   Each expandable row carries: data-key (unique), data-depth (index into the
   chain for the NEXT fetch) and data-c (JSON of accumulated constraints).
   The page sets window.MDRILL = { endpoint, id, tab, cols, labelCol,
   map:{claims,charge,allowed,paid,insbal,patbal}, filters, chain:['year','month','claim'] }.
   Each level loads lazily on first expand. */
(function(){
    var MONTHS = ['','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    function C(){ return window.MDRILL || {}; }
    function money(v){ return '$' + Number(v||0).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2}); }
    function esc(s){ var d=document.createElement('div'); d.textContent=(s==null?'':s); return d.innerHTML; }
    function usdate(s){ if(!s) return ''; var m=String(s).match(/^(\d{4})-(\d{2})-(\d{2})/); return m ? (m[2]+'/'+m[3]+'/'+m[1]) : esc(s); }
    function pad(depth){ return (10 + depth*18) + 'px'; }
    function escSel(k){ return (window.CSS && CSS.escape) ? CSS.escape(k) : k; }

    function valCells(vals){
        var c=C(), m=c.map||{}, split=(c.money==='split'), cells=[];
        for (var i=0;i<c.cols;i++) cells[i]='';
        function num(idx, html){ if(idx!=null) cells[idx]='<td class="num">'+html+'</td>'; }
        function mon(idx, val){
            if(idx==null) return;
            if(split){ cells[idx]='<td class="dollar-col">$</td>'; cells[idx+1]='<td class="dollar-num">'+Number(val||0).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2})+'</td>'; }
            else { cells[idx]='<td class="num">'+money(val)+'</td>'; }
        }
        if (m.claims!=null)   num(m.claims, vals.claims!=null?Number(vals.claims).toLocaleString():'1');
        if (m.volume!=null)   num(m.volume, Number(vals.volume||0).toLocaleString());
        if (m.patients!=null && vals.patients!=null) num(m.patients, Number(vals.patients).toLocaleString());
        if (m.facilities!=null && vals.facilities!=null) num(m.facilities, Number(vals.facilities).toLocaleString());
        if (m.cpts!=null && vals.cpts!=null) num(m.cpts, Number(vals.cpts).toLocaleString());
        if (m.units!=null)    num(m.units, Number(vals.units||0).toLocaleString());
        if (m.paidn!=null && vals.paid_n!=null)   num(m.paidn, Number(vals.paid_n).toLocaleString());
        if (m.unpaidn!=null && vals.claims!=null && vals.paid_n!=null) num(m.unpaidn, Number(vals.claims-vals.paid_n).toLocaleString());
        if (m.charge!=null)   mon(m.charge, vals.charge);
        if (m.allowed!=null)  mon(m.allowed, vals.allowed);
        if (m.expected!=null) mon(m.expected, vals.expected);
        if (m.pri!=null)      mon(m.pri, vals.pri);
        if (m.sec!=null)      mon(m.sec, vals.sec);
        if (m.ter!=null)      mon(m.ter, vals.ter);
        if (m.patpaid!=null)  mon(m.patpaid, vals.pat_paid);
        if (m.inspaid!=null)  mon(m.inspaid, (vals.total_paid!=null) ? vals.total_paid : (((vals.ins_paid!=null) ? vals.ins_paid : ((vals.pri||0)+(vals.sec||0)+(vals.ter||0)))+(vals.pat_paid||0)));
        if (m.paid!=null)     mon(m.paid, (vals.total_paid!=null) ? vals.total_paid : (((vals.paid!=null)?vals.paid:((vals.pri||0)+(vals.sec||0)+(vals.ter||0)))+(vals.pat_paid||0)));
        if (m.totalpaid!=null){ var _tp=(vals.total_paid!=null) ? vals.total_paid : (((vals.ins_paid!=null) ? vals.ins_paid : ((vals.pri||0)+(vals.sec||0)+(vals.ter||0)))+(vals.pat_paid||0)); mon(m.totalpaid, _tp); }
        // Expected to be Paid = Fees Scheduled Projection (expected) − Total Paid. Reuses the total-paid fallback above.
        if (m.exptopaid!=null){ var _etp=(vals.total_paid!=null) ? vals.total_paid : (((vals.ins_paid!=null) ? vals.ins_paid : ((vals.pri||0)+(vals.sec||0)+(vals.ter||0)))+(vals.pat_paid||0)); mon(m.exptopaid, (Number(vals.expected)||0) - _etp); }
        if (m.perpaid!=null)    { var _pp = (vals.ins_paid!=null) ? vals.ins_paid : ((vals.pri!=null||vals.sec!=null||vals.ter!=null) ? ((vals.pri||0)+(vals.sec||0)+(vals.ter||0)) : (vals.paid||0)); mon(m.perpaid,    (vals.paid_n>0)?(_pp/vals.paid_n):0); }
        if (m.peroverall!=null) { var _po = (vals.ins_paid!=null) ? vals.ins_paid : ((vals.pri!=null||vals.sec!=null||vals.ter!=null) ? ((vals.pri||0)+(vals.sec||0)+(vals.ter||0)) : (vals.paid||0)); mon(m.peroverall, (vals.claims>0)?(_po/vals.claims):0); }
        if (m.insbal!=null)   mon(m.insbal, vals.ins_balance);
        if (m.patbal!=null)   mon(m.patbal, vals.pat_balance);
        if (m.primpend!=null && vals.prim_pend_n!=null) num(m.primpend, Number(vals.prim_pend_n).toLocaleString());
        if (m.secpend!=null && vals.sec_pend_n!=null)   num(m.secpend, Number(vals.sec_pend_n).toLocaleString());
        if (m.outstn!=null && vals.outst_n!=null)       num(m.outstn, Number(vals.outst_n).toLocaleString());
        if (m.primoutst!=null) mon(m.primoutst, vals.prim_outst);
        if (m.secoutst!=null)  mon(m.secoutst, vals.sec_outst);
        if (m.collpct!=null) {
            // Collection % = paid claim count ÷ total claim count.
            var claims = vals.claims || 0;
            var paidN  = (vals.paid_n != null) ? vals.paid_n : 0;
            var p = (claims > 0) ? (paidN / claims * 100) : 0;
            num(m.collpct, p.toFixed(1) + '%');
        }
        if (m.variance!=null) { var v=(vals.expected>0)?((vals.paid-vals.expected)/vals.expected*100):0; num(m.variance, v.toFixed(1)+'%'); }
        if (m.avgunit!=null)  mon(m.avgunit, (vals.units>0)?(vals.paid/vals.units):0);
        if (m.unpaid!=null && vals.unpaid_n!=null) num(m.unpaid, Number(vals.unpaid_n).toLocaleString());
        if (m.unpaidpct!=null){ var u=(vals.volume>0)?(vals.unpaid_n/vals.volume*100):0; num(m.unpaidpct, Math.round(u)+'%'); }
        if (m.avgdays!=null && vals.avg_days!=null) num(m.avgdays, Math.round(vals.avg_days)+' d');
        for (var j=0;j<c.cols;j++) if (!cells[j]) cells[j]='<td class="num"></td>';
        return cells;
    }
    function spanRow(parentKey, depth, html, cls){
        var c=C(), tr=document.createElement('tr');
        tr.setAttribute('data-parent', parentKey);
        tr.className='drill-claim'+(cls?' '+cls:'');
        tr.innerHTML='<td colspan="'+c.cols+'" style="padding-left:'+pad(depth)+'" class="'+(cls||'')+'">'+html+'</td>';
        return tr;
    }
    function aggRow(parentKey, parentDepth, level, ag, parentCons){
        var c=C(), childDepth=parentDepth+1;
        var childKey=parentKey+'_'+level.charAt(0)+String(ag.bucket).replace(/[^a-z0-9]/gi,'').slice(0,24);
        var cons=Object.assign({}, parentCons); cons[level]=ag.bucket;
        var label=(level==='year') ? String(ag.bucket)
                : (level==='month') ? (MONTHS[ag.bucket]+' '+(parentCons.year||''))
                : String(ag.bucket);
        var expandable=!!(c.chain||[])[childDepth];
        var tr=document.createElement('tr');
        tr.setAttribute('data-parent', parentKey);
        tr.className='drill-agg'+(expandable?' drill-parent':'');
        tr.setAttribute('data-key', childKey);
        tr.setAttribute('data-depth', childDepth);
        tr.setAttribute('data-c', JSON.stringify(cons));
        if (expandable) tr.setAttribute('onclick','mDrillToggle(this)');
        var cells=valCells(ag);
        var arrow=expandable ? '<i class="ti ti-chevron-right drill-arrow"></i> ' : '<span style="display:inline-block;width:14px"></span>';
        cells[c.labelCol||0]='<td class="lbl" style="text-align:left;padding-left:'+pad(childDepth)+'">'+arrow+'<strong>'+esc(label)+'</strong></td>';
        tr.innerHTML=cells.join('');
        return tr;
    }
    function claimRow(parentKey, cl, depth){
        var c=C(), tr=document.createElement('tr');
        tr.setAttribute('data-parent', parentKey); tr.className='drill-claim';
        var cells=valCells({claims:1, charge:cl.charge, allowed:cl.allowed, paid:cl.paid, ins_balance:cl.ins_balance, pat_balance:cl.pat_balance});
        var parts=[];
        if(cl.dos)      parts.push(usdate(cl.dos));
        if(cl.facility) parts.push(esc(cl.facility));
        if(cl.provider) parts.push(esc(cl.provider));
        var label='<span class="text-secondary">&#8627;</span> <span class="text-secondary">'+parts.join(' &middot; ')+'</span>';
        cells[c.labelCol||0]='<td class="lbl" style="text-align:left;padding-left:'+pad(depth)+'">'+label+'</td>';
        tr.innerHTML=cells.join('');
        return tr;
    }

    window.mDrillToggle = function(row){
        var c=C();
        var key=row.getAttribute('data-key');
        var depth=parseInt(row.getAttribute('data-depth')||'0',10);
        var tbody=row.parentNode;
        var arrow=row.querySelector('.drill-arrow');
        var kids=tbody.querySelectorAll('tr[data-parent="'+escSel(key)+'"]');

        if (row.classList.contains('drill-open')) {                       // collapse (recursively)
            row.classList.remove('drill-open');
            if(arrow) arrow.classList.replace('ti-chevron-down','ti-chevron-right');
            kids.forEach(function(el){ if(el.classList.contains('drill-open')) mDrillToggle(el); el.style.display='none'; });
            return;
        }
        row.classList.add('drill-open');
        if(arrow) arrow.classList.replace('ti-chevron-right','ti-chevron-down');
        if (kids.length){ kids.forEach(function(el){ el.style.display=''; }); return; }   // already loaded

        var level=(c.chain||[])[depth] || 'claim';
        var cons={}; try { cons=JSON.parse(row.getAttribute('data-c')||'{}'); } catch(e){}
        var qs=''; Object.keys(cons).forEach(function(k){ qs+='&'+encodeURIComponent(k)+'='+encodeURIComponent(cons[k]); });
        var url=c.endpoint+'?id='+encodeURIComponent(c.id)+'&tab='+encodeURIComponent(c.tab)+'&level='+encodeURIComponent(level)+(c.filters||'')+qs;

        var loading=spanRow(key, depth+1, '<i class="ti ti-loader-2" style="animation:spin 1s linear infinite"></i> Loading…');
        row.after(loading);
        fetch(url,{credentials:'same-origin'})
          .then(function(r){ return r.json(); })
          .then(function(j){
              loading.remove();
              if(!j.ok){ row.after(spanRow(key, depth+1, 'Error: '+esc(j.error||'failed'),'text-danger')); return; }
              if(!j.rows.length){ row.after(spanRow(key, depth+1, 'No data.','text-secondary')); return; }
              var frag=document.createDocumentFragment();
              if(j.level==='claim'){
                  j.rows.forEach(function(cl){ frag.appendChild(claimRow(key, cl, depth+1)); });
                  if(j.total>j.shown) frag.appendChild(spanRow(key, depth+1, 'Showing top '+j.shown.toLocaleString()+' of '+j.total.toLocaleString()+' claims (highest total balance first).','text-center text-secondary'));
              } else {
                  j.rows.forEach(function(ag){ frag.appendChild(aggRow(key, depth, level, ag, cons)); });
              }
              row.after(frag);
          })
          .catch(function(e){ loading.remove(); row.after(spanRow(key, depth+1, 'Error: '+esc(e.message),'text-danger')); });
    };
})();
</script>
