/* SAB Eligibility Checker v6 — full-screen Apple-style takeover.
v6 (2026-10-19) = Mohamed's locked design, built after the freeze lifted:
nine tap-first questions → REVEAL → "claim your guide" finish.
(1) NEEDS chips (live "kinds of help found" counter in the chrome) →
(2) ZIP → (3) HOUSEHOLD number taps → (4) SITUATIONS in the home →
(5) ALREADY GETTING HELP → (6) AGE range → (7) MONEY SOURCE →
(8) INCOME range (the engine's existing brackets/values, "I'm not sure") →
(9) HOME SITUATION → REVEAL (kinds count + top matches, problem-first,
before any personal info) → CLAIM (a) name+birthday (b) address with
autocomplete, city/state/ZIP pre-filled from their ZIP (c) email AND
phone, both required, trust line "We never sell your information." →
tap screens: job situation, citizenship, language, best time, how they
heard + the optional "anything else going on?" box → RESULTS.
The engine (match) is untouched from v4; its situation inputs are DERIVED
by deriveSit(): senior = 65+ chip or age range; youngchild = kids-under-5
or pregnant chip; kids = youngchild or the child-care need; unemployed =
"none right now" money source or the unemployed job situation. Extra
answers (already-getting, money source, tenure, bill help) ride the lead's
situations string as letter-only flag keys for the owner alert + guides.
Server contract (sab-tracker) honored: valid email AND phone, real DOB,
street, city, a named job situation + citizenship, >=1 need; an "I'm not
sure" income is sent as amount 0/month (not stated) so leads never bounce.
Funnel beacons keep the tracker's stage meanings (household = zip done,
situations = household done, home = income done, reveal = front done;
completion event on submit) vs the 44 starts → 19 finishes baseline.
Returning-visitor memory + on-device in-progress resume kept.
Deploy: upload AS eligibility-checker-js-v6.txt to the Media Library
(PHP/JS uploads are blocked) and point the loader snippet's enqueue URL at it. */
(function(){
"use strict";
/* ---------- verified program data (from programs_db.py, research 2026-09-26/27) ---------- */
var PROGRAMS = [
{id:"snap", name:"SNAP Food Benefits (Texas HHSC)", what:"Monthly grocery money on a Lone Star Card — up to $994/mo for a family of 4 (FY2026 max).", phone:"2-1-1 or 877-541-7905", apply:"https://www.yourtexasbenefits.com/Learn/Home", est:"snap"},
{id:"wic", name:"WIC — Women, Infants & Children", what:"Healthy food, nutrition classes and breastfeeding support for pregnant women and kids under 5. No citizenship requirement.", phone:"800-942-3678", apply:"https://texaswic.org/apply"},
{id:"medicaid", name:"Texas Medicaid", what:"FREE health coverage for kids, pregnant women, seniors and people with disabilities.", phone:"2-1-1 or 1-877-541-7905", apply:"https://www.yourtexasbenefits.com/Learn/Home"},
{id:"chip", name:"Texas CHIP — Children's Health Insurance", what:"Low-cost health coverage for kids 18 & under — enrollment $50 or less per year, co-pays $3–$35.", phone:"2-1-1", apply:"https://www.yourtexasbenefits.com/Learn/Home"},
{id:"tanf", name:"TANF Cash Help", what:"Monthly cash for families with kids and little to no income (family of 3 ≈ $382/mo).", phone:"2-1-1 or 877-541-7905", apply:"https://www.yourtexasbenefits.com/Learn/Home", est:"tanf"},
{id:"opportunity_home", name:"Opportunity Home — Public Housing & Section 8", what:"Rent based on income (around 30%). Apply the moment a waitlist opens — they fill fast.", phone:"210-477-6000", apply:"https://homesa.org/housing/find-a-home/apply-for-housing/"},
{id:"hap", name:"City of San Antonio Housing Assistance (HAP)", what:"Up to 3 months of rent (max ~$3,500) + up to $1,500 in utility help, paid straight to your landlord. Time-sensitive: enrollment windows fill fast.", phone:"210-207-6459", apply:"https://www.sa.gov/Directory/Departments/NHSD/Housing-Support/Renter-Support/About-HAP", hot:true},
{id:"trla_rtc", name:"Right to Counsel — Free Eviction Lawyer", what:"A FREE attorney if you're facing eviction. Call immediately if you have court papers. Time-sensitive.", phone:"210-212-3703", apply:"https://www.trla.org/", hot:true},
{id:"cps_discount", name:"CPS Energy Affordability Discount", what:"Up to 25% off your electric bill every month (35% if you're 60+).", phone:"1-888-207-7434", apply:"https://www.sa.gov/Directory/Departments/DHS/Financial-Assistance/Utility-Assistance", est:"utility"},
{id:"sa_utility", name:"City of San Antonio Utility Assistance", what:"Help paying energy bills — priority for households with kids, disabilities, or adults 60+.", phone:"210-207-8198", apply:"https://www.sa.gov/Directory/Departments/DHS/Financial-Assistance/Utility-Assistance"},
{id:"vita", name:"VITA — Free Tax Preparation", what:"IRS-certified volunteers do your taxes FREE (Jan–Apr) — including EITC and Child Tax Credit.", phone:"210-207-6575", apply:"https://vitasa.org/"},
{id:"twc_unemp", name:"Texas Unemployment Benefits", what:"Partial income replacement if you lost your job or had hours cut — roughly $75–$605/week.", phone:"800-939-6631", apply:"https://www.twc.texas.gov/services/apply-benefits"},
{id:"wioa", name:"WIOA — Free Career Training (Workforce Solutions Alamo)", what:"FREE career counseling, job training and certifications at 16 Alamo-area career centers.", phone:"210-438-0581", apply:"https://www.workforcesolutionsalamo.org/information-sessions/"},
{id:"ccs", name:"Child Care Scholarships (CCS)", what:"Helps working parents pay for child care. Apply early — there can be a waitlist.", phone:"1-888-265-6461", apply:"https://childcare.twc.texas.gov/find/register?mode=signup"},
{id:"va", name:"VA Benefits", what:"Health care, disability compensation, GI Bill, home loans and more for veterans.", phone:"1-800-827-1005", apply:"https://benefits.va.gov/benefits/applying.asp"},
{id:"tvc", name:"Texas Veterans Commission", what:"FREE help filing VA disability claims, job placement and education benefits.", phone:"1-800-252-8387", apply:"https://tvc.texas.gov/"},
{id:"hud_vash", name:"HUD-VASH — Housing for Homeless Veterans", what:"Rental voucher + VA case management for veterans facing homelessness. Time-sensitive.", phone:"877-424-3838", apply:"https://www.va.gov/south-texas-health-care/health-services/homeless-veteran-care/", hot:true},
{id:"foodbank", name:"San Antonio Food Bank — Free Food + Application Help", what:"Free groceries AND free one-on-one help applying for SNAP, Medicaid and TANF. No citizenship requirement for food.", phone:"210-431-8326", apply:"https://safoodbank.org/our-programs/benefits-assistance/"},
{id:"trla", name:"Texas RioGrande Legal Aid", what:"FREE civil legal help — housing, family, benefits denials. English and Spanish.", phone:"(833) 329-8752", apply:"https://www.trla.org/"},
{id:"via_link", name:"VIA Link — Rides On Demand + VIA Buses", what:"VIA's on-demand vans pick you up near your home and take you anywhere inside your zone for $1.30 a ride (65¢ with a reduced fare). Book up to 7 days ahead — good for job interviews and appointments. Regular VIA buses cover the rest of the city.", phone:"210-655-5465", apply:"https://www.viainfo.net/Link/"},
{id:"lifeline", name:"Lifeline — Discount Phone & Internet", what:"A federal program that takes $9.25 a month off your phone or internet bill — some companies turn it into a completely free phone plan. You qualify with SNAP, Medicaid or SSI, or income at or below 135% of the federal poverty line.", phone:"1-800-234-9473", apply:"https://www.lifelinesupport.org/"},
{id:"211", name:"2-1-1 Texas Helpline", what:"One free call connects you to thousands of local services. 24/7, 100+ languages.", phone:"2-1-1", apply:"https://www.211texas.org/"}
];
/* Inline SVG icons (no emoji anywhere in the UI) */
var SVG_PHONE = '';
var SVG_CLOCK = '';
/* 2025 federal poverty guidelines (48 states), monthly */
var FPL_M = {1:1304.17, 2:1762.50, 3:2220.83, 4:2679.17, 5:3137.50, 6:3595.83, 7:4054.17, 8:4512.50};
var SNAP_MAX = {1:298, 2:548, 3:783, 4:994, 5:1158, 6:1390, 7:1536, 8:1755}; /* FY2026 max allotments */
function fpl(hh, pct){ var b = FPL_M[Math.min(hh,8)] || 4512.50; if(hh>8) b += (hh-8)*478.33; return b*pct; }
function initChecker(root){
if(!root || root.dataset.init) return; root.dataset.init = "1";
var $ = function(s){ return root.querySelector(s); };
var $$ = function(s){ return Array.prototype.slice.call(root.querySelectorAll(s)); };
var REDUCED = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
var leadSent = false; /* guard: never send the same lead twice (e.g. back-nav + resubmit) */
var state = { zip:"", hh:3, income:3000, incomeUnknown:false, sit:{}, sitRaw:{}, flags:{}, ageRange:"", moneySrc:"", tenure:"", focus:null, name:"", phone:"", email:"", pref:"", consent:false,
dob:"", street:"", city:"", employment:"", income_amount:"", income_period:"", citizenship:"", top_needs:[], language:"en", contact_time:"", hear_about:"", notes:"" };
/* v6: the engine's situation inputs are derived from the answers.
sitRaw holds the Q4 chip states (youngchild/pregnant/senior/disabled/
veteran); state.sit is the engine map match() has always read; state.flags
holds the extra letter-only keys (already-getting, money source, tenure,
bill help) that ride the lead's situations string for the owner + guides. */
var FLAG_KEYS = ["getsnap","getmedicaid","gethousing","srcjob","srcbenefits","srcmix","srcnone","renting","ownhome","withfamily","utilbills"];
function deriveSit(){
var raw = state.sitRaw || {};
state.sit.veteran = !!raw.veteran;
state.sit.disabled = !!raw.disabled;
state.sit.youngchild = !!raw.pregnant || !!raw.youngchild;
state.sit.senior = !!raw.senior || state.ageRange === "65plus";
state.sit.kids = state.sit.youngchild || state.top_needs.indexOf("childcare") !== -1;
state.sit.unemployed = state.moneySrc === "srcnone" || state.employment === "unemployed";
}
try {
var sc = root.closest('[data-sabck-focus]');
if (sc) state.focus = sc.getAttribute('data-sabck-focus');
} catch(e){}
/* ---------- returning-visitor memory (v4.3 — device-only, nothing sent anywhere) ---------- */
var WB_KEY = "sabck_saved_v1";
var quizLive = false; /* a quiz run is underway in this page session */
function wbLoad(){
try{
var raw = window.localStorage.getItem(WB_KEY);
if(!raw) return null;
var d = JSON.parse(raw);
if(!d || typeof d !== "object" || !d.zip || !d.matched || !d.matched.length) return null;
return d;
}catch(e){ return null; }
}
function wbSave(res){
try{
var sit = {};
Object.keys(state.sit).forEach(function(k){ if(state.sit[k]) sit[k] = true; });
Object.keys(state.flags).forEach(function(k){ if(state.flags[k]) sit[k] = true; });
var d = { v:2, zip: state.zip, hh: state.hh, income: state.income,
incomeUnknown: !!state.incomeUnknown, sit: sit, top_needs: state.top_needs.slice(),
matched: res.map(function(r){ return r.p.id; }), ts: Date.now() };
window.localStorage.setItem(WB_KEY, JSON.stringify(d));
}catch(e){}
}
function wbForget(){
try{ window.localStorage.removeItem(WB_KEY); }catch(e){}
}
function wbResetQuiz(){
state.zip = ""; state.hh = 3; state.income = null; state.incomeUnknown = false; state.sit = {};
state.sitRaw = {}; state.flags = {}; state.ageRange = ""; state.moneySrc = ""; state.tenure = "";
state.top_needs = []; state.notes = ""; state.name = ""; state.email = ""; state.phone = ""; state.dob = "";
state.street = ""; state.city = ""; state.employment = ""; state.citizenship = ""; state.pref = "";
state.contact_time = ""; state.hear_about = ""; state.consent = false;
progClear();
var z = $("#sabck-zip"); if(z) z.value = "";
$$("#sabck-hhgrid button").forEach(function(b){ b.classList.toggle("sel", b.getAttribute("data-hh") === "3"); });
$$(".sabck-checks input").forEach(function(c){ c.checked = false; });
$$(".sabck-options button").forEach(function(b){ b.classList.remove("sel"); });
$$(".sabck-prefchip").forEach(function(b){ b.classList.remove("sel"); });
["sabck-name","sabck-email","sabck-phone","sabck-notes","sabck-street","sabck-city","sabck-state","sabck-zip2"].forEach(function(id){ var el = document.getElementById(id); if(el) el.value = ""; });
var db = $("#sabck-dob"); if(db) db.value = "";
var cs = $("#sabck-consent"); if(cs) cs.checked = false;
var ab = $("#sabck-alerts"); if(ab) ab.checked = false;
addrPrefilled = false;
updatePill();
}
/* ---------- in-progress save (v5: leaving mid-quiz resumes where they stopped) ---------- */
var PROG_KEY = "sabck_prog_v1";
function progSave(){
try{
if(!QUIZ_SCREENS[current]) return;
var raw = {};
Object.keys(state.sitRaw).forEach(function(k){ if(state.sitRaw[k]) raw[k] = true; });
var fl = {};
Object.keys(state.flags).forEach(function(k){ if(state.flags[k]) fl[k] = true; });
window.localStorage.setItem(PROG_KEY, JSON.stringify({
zip: state.zip, hh: state.hh, income: state.income,
incomeUnknown: !!state.incomeUnknown, sitRaw: raw, flags: fl, ageRange: state.ageRange,
moneySrc: state.moneySrc, tenure: state.tenure,
top_needs: state.top_needs.slice(), screen: current, ts: Date.now()
}));
}catch(e){}
}
function progLoad(){
try{
var raw = window.localStorage.getItem(PROG_KEY);
if(!raw) return null;
var d = JSON.parse(raw);
if(!d || typeof d !== "object" || !d.screen || !QUIZ_SCREENS[d.screen]) return null;
return d;
}catch(e){ return null; }
}
function progClear(){
try{ window.localStorage.removeItem(PROG_KEY); }catch(e){}
}
function progApply(d){
state.zip = d.zip || ""; state.hh = d.hh || 3;
state.incomeUnknown = !!d.incomeUnknown;
state.income = d.incomeUnknown ? null : (typeof d.income === "number" ? d.income : null);
state.sitRaw = {}; if(d.sitRaw){ Object.keys(d.sitRaw).forEach(function(k){ if(d.sitRaw[k]) state.sitRaw[k] = true; }); }
state.flags = {}; if(d.flags){ Object.keys(d.flags).forEach(function(k){ if(d.flags[k]) state.flags[k] = true; }); }
state.ageRange = d.ageRange || "";
state.moneySrc = d.moneySrc || "";
state.tenure = d.tenure || "";
state.top_needs = (d.top_needs || []).slice();
deriveSit();
var z = $("#sabck-zip"); if(z) z.value = state.zip;
$$("#sabck-hhgrid button").forEach(function(b){ b.classList.toggle("sel", parseInt(b.getAttribute("data-hh"),10) === state.hh); });
$$("#sabck-sits input").forEach(function(c){ c.checked = !!state.sitRaw[c.value]; });
$$("#sabck-needs input").forEach(function(c){ c.checked = state.top_needs.indexOf(c.value) !== -1; });
$$("#sabck-getting input").forEach(function(c){ c.checked = !!state.flags[c.value]; });
$$("#sabck-tenure-opts button").forEach(function(b){ b.classList.toggle("sel", b.getAttribute("data-tenure") === state.tenure); });
var ub = $("#sabck-utilbills"); if(ub) ub.checked = !!state.flags.utilbills;
$$("#sabck-money-opts button").forEach(function(b){ b.classList.toggle("sel", b.getAttribute("data-src") === state.moneySrc); });
updatePill();
}
/* Welcome-back screen: built here so the markup snippet stays untouched.
Reuses the existing screen/card/button classes, so it looks native. */
var wbScreen = document.createElement("div");
wbScreen.className = "sabck-screen";
wbScreen.setAttribute("data-screen", "welcome");
wbScreen.innerHTML = '
Welcome back
'
+ ''
+ ''
+ ''
+ ''
+ ''
+ '
We remember your answers on this phone only, so you don\'t have to answer again. Nothing extra is sent to us.
';
var wbAnchor = root.querySelector(".sabck-screen");
if(wbAnchor && wbAnchor.parentNode) wbAnchor.parentNode.insertBefore(wbScreen, wbAnchor);
function wbRender(){
var d = wbLoad(); if(!d) return;
/* restore the saved answers so the matcher re-runs against today's data */
state.zip = d.zip || "";
state.hh = d.hh || 3;
state.incomeUnknown = !!d.incomeUnknown;
state.income = d.incomeUnknown ? null : (typeof d.income === "number" ? d.income : null);
state.sit = {};
if(d.sit){ Object.keys(d.sit).forEach(function(k){ if(d.sit[k]) state.sit[k] = true; }); }
state.top_needs = (d.top_needs || []).slice();
/* v6: split the saved map back into engine situations, Q4 chips and flags,
then re-derive — keeping any kids/unemployed flags older saves earned */
var keepKids = !!state.sit.kids, keepUnemp = !!state.sit.unemployed;
state.flags = {};
FLAG_KEYS.forEach(function(k){ if(state.sit[k]) state.flags[k] = true; });
state.sitRaw = { veteran: !!state.sit.veteran, disabled: !!state.sit.disabled,
senior: !!state.sit.senior, youngchild: !!state.sit.youngchild, pregnant: false };
if(state.sit.senior) state.ageRange = "65plus";
state.moneySrc = state.flags.srcnone ? "srcnone" : (state.flags.srcjob ? "srcjob" : (state.flags.srcmix ? "srcmix" : (state.flags.srcbenefits ? "srcbenefits" : "")));
state.tenure = state.flags.renting ? "renting" : (state.flags.ownhome ? "ownhome" : (state.flags.withfamily ? "withfamily" : ""));
deriveSit();
if(keepKids) state.sit.kids = true;
if(keepUnemp) state.sit.unemployed = true;
var res = match();
var savedIds = d.matched || [];
var newOnes = res.filter(function(r){ return savedIds.indexOf(r.p.id) === -1; });
var hotOnes = res.filter(function(r){ return r.p.hot; });
var when = "";
try{ when = new Date(d.ts).toLocaleDateString("en-US", {month:"long", day:"numeric", year:"numeric"}); }catch(e){}
var sub = $("#sabck-wb-sub");
if(sub) sub.textContent = "You last checked on " + when + ". Here are your matches today, based on the answers you gave then.";
var news = $("#sabck-wb-news");
if(news){
var html = "";
if(!newOnes.length && !hotOnes.length){
html = '
What\'s new for you
'
+ '
Nothing has changed since your last check — your matches are below.
';
} else {
html = '
What\'s new for you
';
if(newOnes.length){
html += '
New since your last check
';
newOnes.forEach(function(r){ html += '
' + r.p.name + '
'; });
}
if(hotOnes.length){
html += '
Time-sensitive for you
';
hotOnes.forEach(function(r){ html += '
' + r.p.name + '
'; });
}
html += '
';
}
news.innerHTML = html;
}
var list = $("#sabck-wb-list"); if(!list) return;
list.innerHTML = "";
res.forEach(function(r){
var card = document.createElement("div"); card.className = "sabck-card";
var badge = r.p.hot
? '' + SVG_CLOCK + 'Time-sensitive'
: '✓ May qualify';
var valLine = r.val ? '