1/* ============================================================================ 2 BioProfile Analysis - Full Questionnaire definition (v1). 3 Source of truth for the runnable questionnaire. Derived from 4 ../../analysis/questionnaire-item-bank.md. Loaded as JS (not JSON) so it runs 5 from file:// with no server, matching the design-system convention. 6 7 LICENCE POSTURE (important): this runnable build ships LICENCE-CLEAN wording. 8 - WHO-5 items are the free/open official wording (verbatim). 9 - Every other item is CUSTOM wording modelled on the validated instrument 10 (labelled `source: "derive:<X>"`), so nothing copyright-gated ships before 11 its licence is cleared. On clearing PSS-10 / FAS / FINDRISC / MEDAS the 12 wording + scoring swap in without changing the engine contract. See 13 ../../analysis/science-backing.md section 6 and the Emre briefing. 14 15 Each scored option carries `p` = imbalance points (0..4), already in the 16 imbalance direction (reverse items are pre-inverted here, so the engine never 17 reverse-scores). Positive-behaviour items (aging) carry `h` = health points. 18 WHO-5 options carry `w` = wellbeing points (0..5). Red-flag options carry 19 `flag`. FINDRISC risk items carry `p` used as risk points by the engine. 20 ========================================================================== */ 21globalThis.BP_Q = { 22 version: "1.1.0", 23 title: "BioProfile Analysis", 24 subtitle: "Analyse your health, without diagnosing it.", 25 /* Sections are the wizard steps, in order. `gate` hides a whole section. */ 26 sections: [ 27 { 28 id: "intro", kind: "intro", title: "Before we start", 29 body: "This takes about 10 to 15 minutes. It builds a picture of where your body may be out of balance, to guide you and your coach. It is an analysis, not a diagnosis, and only a doctor can diagnose. Your answers are private (GDPR)." 30 }, 31 32 /* ---- Stage 0 preamble: sets gating context + risk-tool inputs -------- */ 33 { 34 id: "about", kind: "questions", title: "About you", 35 note: "These help us ask the right questions. They do not change your scores.", 36 items: [ 37 { key: "pre.goal", basis: "preamble", input: "single", prompt: "What do you most want to improve right now?", options: [ 38 { value: "more_energy", label: "More energy" }, { value: "better_sleep", label: "Better sleep" }, 39 { value: "lose_weight", label: "Lose weight" }, { value: "sharper_focus", label: "Sharper focus" }, 40 { value: "healthy_aging", label: "Healthy aging / prevention" }, { value: "gut_comfort", label: "Gut comfort" }, 41 { value: "hormonal_balance", label: "Hormonal balance" }, { value: "general_vitality", label: "General vitality" }, 42 /* T553 (Jesse 2026-10-06): the one list of goals (app/data/health-goals.js, migration 191), five added; offered 43 only while their switch is on (the end of this file), so the live scan never offers a goal before its release */ 44 { value: "build_muscle", label: "Build muscle and strength", gate: "new_goals" }, { value: "less_stress", label: "Less stress", gate: "new_goals" }, 45 { value: "strong_resistance", label: "Strong resistance", gate: "new_goals" }, { value: "heart_health", label: "Heart and blood vessels", gate: "new_goals" }, 46 { value: "sport_recovery", label: "Sport and recovery", gate: "new_goals" } ] }, 47 { key: "pre.sex", basis: "preamble", input: "single", prompt: "What was your biological gender at birth?", 48 help: "We ask because reference ranges and some questions differ by biology. Prefer not to say is fine - it just makes a few results a little less precise.", options: [ 49 { value: "f", label: "Female" }, { value: "m", label: "Male" }, { value: "x", label: "Prefer not to say" } ] }, 50 /* Year of birth (Jesse 2026-07-18): asked as a 4-digit year, stored with 51 date-of-birth semantics (dob = YYYY-01-01, precision "year"; full date 52 addable later, migration 003). The ENGINE still consumes age - it is 53 derived from the year at scoring time (server + client preview), and 54 legacy stored ages (values < 200) keep working via the same mapping. */ 55 { key: "pre.birthdate", basis: "preamble", input: "number", numKind: "yob", prompt: "Your year of birth", suffix: "", help: "The year is enough for now; you can add your full birth date later.", 56 min: new Date().getFullYear() - 110, max: new Date().getFullYear() - 16 }, 57 { key: "pre.stage_f", basis: "preamble", input: "single", gate: { sex: "f" }, prompt: "Which best describes your current stage?", options: [ 58 { value: "premenopause", label: "Before menopause (regular cycles)" }, { value: "perimenopause", label: "Perimenopause (cycles changing)" }, 59 { value: "menopause", label: "Menopause" }, { value: "postmenopause", label: "Postmenopause" }, { value: "unsure", label: "Not sure" } ] }, 60 { key: "pre.stage_m", basis: "preamble", input: "single", gate: { sex: "m" }, prompt: "How has your vitality been over the last year?", options: [ 61 { value: "stable", label: "Stable" }, { value: "some_dip", label: "Some dip" }, { value: "marked_dip", label: "A marked dip" } ] }, 62 { key: "pre.glp1", basis: "preamble", input: "single", prompt: "Do you currently use a GLP-1 medication (e.g. semaglutide)?", options: [ 63 { value: "no", label: "No" }, { value: "yes", label: "Yes" } ] }, 64 { key: "pre.height_cm", basis: "preamble", input: "number", prompt: "Your height", suffix: "cm", min: 120, max: 230 }, 65 { key: "pre.weight_kg", basis: "preamble", input: "number", prompt: "Your weight", suffix: "kg", min: 35, max: 300 }, 66 { key: "pre.waist_cm", basis: "preamble", input: "number", skippable: true, prompt: "Your waist at the navel", suffix: "cm", help: "Measure around your middle, at the belly button. Not sure? You can skip this - it only sharpens one result.", min: 50, max: 200 } 67 ] 68 }, 69 70 /* ---- Red-flag safety battery: overrides scoring --------------------- */ 71 { 72 id: "safety", kind: "questions", title: "A last few safety checks", 73 note: "Almost done. These tell us when to point you to a doctor rather than give you a plan. If anything here is a yes, that is what matters, not a score.", 74 items: [ 75 { key: "rf.chest_pain", basis: "redflag", input: "single", prompt: "Do you get chest pain or pressure when you exert yourself?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "chest_pain_exertion" } ] }, 76 { key: "rf.headache", basis: "redflag", input: "single", prompt: "Have you had a sudden, severe, worst-ever headache?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "thunderclap_headache" } ] }, 77 { key: "rf.neuro", basis: "redflag", input: "single", prompt: "Any sudden face drooping, arm weakness, or trouble speaking?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "neuro_deficit" } ] }, 78 { key: "rf.weight_loss", basis: "redflag", input: "single", prompt: "Have you lost weight recently without trying?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "unexplained_weight_loss" } ] }, 79 { key: "rf.blood_stool", basis: "redflag", input: "single", prompt: "Have you noticed blood in your stool or unex
79plained rectal bleeding?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "rectal_bleeding" } ] }, 80 { key: "rf.other_bleeding", basis: "redflag", input: "single", prompt: "Any other unexplained bleeding (in urine, coughing blood, or bleeding after menopause)?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "unexplained_bleeding" } ] }, 81 { key: "rf.lump", basis: "redflag", input: "single", prompt: "Any new unexplained lump, or a change in a breast?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "new_lump" } ] }, 82 { key: "rf.swallow", basis: "redflag", input: "single", prompt: "Any new or ongoing difficulty swallowing?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "dysphagia" } ] }, 83 { key: "rf.faint", basis: "redflag", input: "single", prompt: "Have you fainted or blacked out unexpectedly?", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "syncope" } ] }, 84 { key: "rf.mood", basis: "redflag", input: "single", prompt: "In the last 2 weeks, have you had thoughts that you would be better off dead, or of hurting yourself?", 85 help: "If yes, we will show you support right away.", options: [ { value: "no", label: "No" }, { value: "yes", label: "Yes", flag: "suicidal_ideation" } ] } 86 ] 87 }, 88 89 /* ---- WHO-5 wellbeing (verbatim, free/open) -------------------------- */ 90 { 91 id: "wellbeing", kind: "questions", title: "Your wellbeing", profile: "who5", 92 note: "Over the last two weeks...", 93 items: who5Item("who5.cheerful", "I have felt cheerful and in good spirits") .concat( 94 who5Item("who5.calm", "I have felt calm and relaxed"), 95 who5Item("who5.active", "I have felt active and vigorous"), 96 who5Item("who5.rested", "I woke up feeling fresh and rested"), 97 who5Item("who5.interest", "My daily life has been filled with things that interest me")) 98 }, 99 100 /* ---- Gated mood step (only if WHO-5 <= 50%) ------------------------- */ 101 { 102 id: "mood", kind: "questions", title: "A closer look", gate: { whenWho5AtMost: 50 }, 103 note: "Over the last two weeks, how often have you been bothered by...", 104 items: [ 105 mood4("mood.phq2_low", "Feeling down, depressed, or hopeless"), 106 mood4("mood.phq2_anhedonia", "Little interest or pleasure in doing things"), 107 mood4("mood.gad2_nervous", "Feeling nervous, anxious, or on edge"), 108 mood4("mood.gad2_worry", "Not being able to stop or control worrying") 109 ] 110 }, 111 112 /* ---- A1 Sleep & Recovery (derive:ISI) ------------------------------ */ 113 { 114 id: "sleep", kind: "questions", title: "Sleep & recovery", profile: "sleep", note: "Over the last two weeks...", 115 items: [ 116 s4("sleep.onset_latency", "How long does it usually take you to fall asleep?", ["Under 15 min","15 to 30 min","30 to 60 min","Over an hour","I rarely get to sleep"], [0,1,2,3,4]), 117 s4("sleep.night_waking", "How often do you wake in the night and struggle to get back to sleep?", ["Rarely","Occasionally","Some nights","Most nights","Every night"], [0,1,2,3,4]), 118 s4("sleep.rested_waking", "How often do you wake feeling rested?", ["Every day","Most days","Some days","Rarely","Never"], [0,1,2,3,4]), 119 s4("sleep.satisfaction", "Overall, how satisfied are you with your sleep?", ["Very satisfied","Satisfied","Neutral","Dissatisfied","Very dissatisfied"], [0,1,2,3,4]), 120 s4("sleep.daytime_interference", "How much does poor sleep interfere with your day?", ["Not at all","A little","Somewhat","Much","Very much"], [0,1,2,3,4]) 121 ] 122 }, 123 124 /* ---- A3 Energy & Vitality (derive:FAS) ----------------------------- */ 125 { 126 id: "energy", kind: "questions", title: "Energy & vitality", profile: "energy", 127 items: [ 128 s4("energy.daily_stability", "How steady is your energy across the day?", ["Steady","Mostly steady","Up and down","A clear afternoon dip","Drained most days"], [0,1,2,3,4]), 129 s4("energy.midday_dip", "How often do you hit an energy crash during the day?", ["Rarely","Weekly","A few times a week","Most days","Daily"], [0,1,2,3,4]), 130 s4("energy.stimulant_reliance", "How much do you rely on caffeine or stimulants to get through?", ["None","A little","Moderate","A lot","Cannot function without"], [0,1,2,3,4]), 131 s4("energy.physical_tire", "How easily do you tire physically doing everyday things?", ["Not easily","Slightly","Moderately","Easily","Very easily"], [0,1,2,3,4]), 132 s4("energy.motivation", "How often do you feel too worn out to do things you want to?", ["Rarely","Occasionally","Sometimes","Often","Almost always"], [0,1,2,3,4]) 133 ] 134 }, 135 136 /* ---- A2 Stress Resilience (derive:PSS, licence-clean variant) ------- */ 137 { 138 id: "stress", kind: "questions", title: "Stress & resilience", profile: "stress", note: "In the last month...", 139 items: [ 140 s4("stress.control", "How often have you felt unable to control the important things in your life?", ["Never","Almost never","Sometimes","Fairly often","Very often"], [0,1,2,3,4]), 141 s4("stress.overwhelm", "How often have you felt difficulties were piling up too high to overcome?", ["Never","Almost never","Sometimes","Fairly often","Very often"], [0,1,2,3,4]), 142 s4("stress.coping", "How often have you felt confident about handling your problems?", ["Very often","Fairly often","Sometimes","Almost never","Never"], [0,1,2,3,4]), 143 s4("stress.on_top", "How often have you felt that things were going your way?", ["Very often","Fairly often","Sometimes","Almost never","Never"], [0,1,2,3,4]), 144 s4("stress.switch_off", "How hard is it to switch off and relax at the end of the day?", ["Easy","Fairly easy","Somewhat hard","Hard","Very hard"], [0,1,2,3,4]) 145 ] 146 }, 147 148 /* ---- A4 Gut Health (derive) ---------------------------------------- */ 149 { 150 id: "gut", kind: "questions", title: "Digestion & gut", profile: "gut", note: "Over the last two weeks...", 151 items: [ 152 s4("gut.stool_form", "Which best matches your usual stool?", ["Hard lumps","Lumpy and firm","Cracked but formed","Smooth and soft","Soft blobs","Mushy","Watery"], [3,2,0,0,1,2,3]), 153 s4("gut.pain_frequency", "How often do you get abdominal pain or discomfort?", ["Rarely","About 1 day a week","2 to 3 days a week","Most days","Daily"], [0,1,2,3,4]), 154 s4("gut.bloating", "How troubled are you by bloating or distension?", ["Not at all","Mild","Moderate","Quite a bit","Severe"], [0,1,2,3,4]), 155 s4("gut.regularity", "How regular are your bowel movements?", ["Very regular","Mostly regular","Irregular","Often constipated or loose","Very irregular"], [0,1,2,3,4]), 156 s4("gut.reflux_indigestion", "How often do you get reflux, heartburn, or indigestion?", ["Rarely","Monthly","Weekly","Several times a week","Daily"], [0,1,2,3,4]), 157 s4("gut.food_relation", "How often do symptoms follow eating specific foods?", ["Never","Rarely","Sometimes","Often","Almost always"], [0,1,2,3,4]) 158 ] 159 }, 160 161 /* ---- C Brain Performance (derive:CFQ, PERCEIVED) ------------------- */ 162 { 163 id: "brain", kind: "questions", title: "Focus & mind", profile: "brain", note: "This is about how your thinking feels to you (perceived), over the last two weeks.", 164 items: [ 165 s4("brain.clarity_fog", "How often do you feel mentally fogg
165y or unclear?", ["Rarely","Occasionally","Sometimes","Often","Most days"], [0,1,2,3,4]), 166 s4("brain.focus", "How often do you lose focus or get easily distracted?", ["Rarely","Occasionally","Sometimes","Often","Almost constantly"], [0,1,2,3,4]), 167 s4("brain.memory_lapses", "How often do you forget names, words, or why you entered a room?", ["Rarely","Occasionally","Sometimes","Often","Daily"], [0,1,2,3,4]), 168 s4("brain.mental_fatigue", "How often does thinking feel effortful or tiring?", ["Rarely","Occasionally","Sometimes","Often","Most days"], [0,1,2,3,4]) 169 ] 170 }, 171 172 /* ---- B Hormonal Balance (cohort-gated) ----------------------------- */ 173 { 174 id: "hormonal_f", kind: "questions", title: "Hormonal balance", profile: "hormonal", 175 gate: { sex: "f", stageIn: ["perimenopause","menopause","postmenopause","unsure"] }, 176 note: "How troubled have you been by...", 177 items: [ 178 s4("hormonal.hot_flushes", "Hot flushes or night sweats?", ["None","Mild","Moderate","Marked","Severe"], [0,1,2,3,4]), 179 s4("hormonal.vaginal_dryness", "Vaginal dryness or discomfort?", ["None","Mild","Moderate","Marked","Severe"], [0,1,2,3,4]), 180 s4("hormonal.mood_swings_f", "Irritability or mood swings?", ["None","Mild","Moderate","Marked","Severe"], [0,1,2,3,4]), 181 s4("hormonal.sleep_hormonal", "Sleep disturbed by night sweats or hormonal changes?", ["None","Mild","Moderate","Marked","Severe"], [0,1,2,3,4]) 182 ] 183 }, 184 { 185 id: "hormonal_m", kind: "questions", title: "Vitality", profile: "hormonal", gate: { sex: "m" }, 186 note: "Over the last year...", 187 items: [ 188 s4("hormonal.libido_m", "How would you describe your sex drive?", ["Normal","Slightly lower","Moderately lower","Much lower","Absent"], [0,1,2,3,4]), 189 s4("hormonal.vitality_m", "How is your drive, strength, and get-up-and-go?", ["Unchanged","Slightly lower","Moderately lower","Much lower","Markedly lower"], [0,1,2,3,4]), 190 s4("hormonal.mood_m", "How often do you feel low, flat, or irritable?", ["Rarely","Occasionally","Sometimes","Often","Most days"], [0,1,2,3,4]) 191 ] 192 }, 193 194 /* ---- D1 Metabolic Health (risk: FINDRISC-style) -------------------- */ 195 { 196 id: "metabolic", kind: "questions", title: "Metabolic risk", profile: "metabolic", 197 note: "This is a risk picture, not a symptom score. A blood test confirms it.", 198 items: [ 199 { key: "metabolic.activity_findrisc", profile: "metabolic", basis: "risk", input: "single", prompt: "Do you do at least 30 minutes of physical activity most days?", options: [ { value: "yes", label: "Yes", p: 0 }, { value: "no", label: "No", p: 2 } ] }, 200 { key: "metabolic.fruitveg", profile: "metabolic", basis: "risk", input: "single", prompt: "How often do you eat vegetables or fruit?", options: [ { value: "daily", label: "Every day", p: 0 }, { value: "not_daily", label: "Not every day", p: 1 } ] }, 201 { key: "metabolic.bp_meds", profile: "metabolic", basis: "risk", input: "single", prompt: "Have you ever taken blood-pressure medication regularly?", options: [ { value: "no", label: "No", p: 0 }, { value: "yes", label: "Yes", p: 2 } ] }, 202 { key: "metabolic.high_glucose_history", profile: "metabolic", basis: "risk", input: "single", prompt: "Have you ever been told you had high blood sugar (a test, or in pregnancy)?", options: [ { value: "no", label: "No", p: 0 }, { value: "yes", label: "Yes", p: 5 } ] }, 203 { key: "metabolic.family_diabetes", profile: "metabolic", basis: "risk", input: "single", prompt: "Family history of diabetes?", options: [ { value: "none", label: "None", p: 0 }, { value: "distant", label: "Grandparent, aunt, uncle, cousin", p: 3 }, { value: "immediate", label: "Parent, sibling, or child", p: 5 } ] }, 204 s4("metabolic.sweet_cravings", "How often do you get strong sugar or carb cravings?", ["Rarely","Weekly","A few times a week","Most days","Daily"], [0,1,2,3,4], "colour") 205 ] 206 }, 207 208 /* ---- D2 Cardiovascular Resilience (risk/behaviour) ----------------- */ 209 { 210 id: "cardio", kind: "questions", title: "Movement & heart", profile: "cardio", 211 note: "A risk and fitness picture. A validated heart-risk score needs a measured blood pressure.", 212 items: [ 213 s4("cardio.activity_ipaq", "In a typical week, how much moderate-to-vigorous activity do you get?", ["150+ minutes","90 to 150 min","30 to 90 min","Under 30 min","None"], [0,1,2,3,4]), 214 { key: "cardio.smoking", profile: "cardio", basis: "risk", input: "single", prompt: "Do you smoke?", options: [ { value: "never", label: "Never", p: 0 }, { value: "ex_smoker", label: "Ex-smoker", p: 1 }, { value: "occasionally", label: "Occasionally", p: 2 }, { value: "daily", label: "Daily", p: 3 } ] }, 215 { key: "cardio.family_cvd", profile: "cardio", basis: "risk", input: "single", prompt: "Family history of heart attack or stroke before age 60?", options: [ { value: "no", label: "No", p: 0 }, { value: "yes", label: "Yes", p: 2 } ] }, 216 { key: "cardio.conditions", profile: "cardio", basis: "risk", input: "single", prompt: "Any diagnosed high blood pressure, high cholesterol, diabetes, or atrial fibrillation?", options: [ { value: "none", label: "None", p: 0 }, { value: "one", label: "One", p: 2 }, { value: "two_plus", label: "Two or more", p: 3 } ] }, 217 s4("cardio.exertion_capacity", "Can you climb two flights of stairs without stopping?", ["Easily","With some effort","I need to stop","I cannot"], [0,1,2,3]) 218 ] 219 }, 220 221 /* ---- E Inflammation & Recovery (proxy - NOT a measure) ------------- */ 222 { 223 id: "inflam", kind: "questions", title: "Recovery", profile: "inflam", 224 note: "This is a recovery and diet proxy, not a measure of inflammation. A blood test (hs-CRP) is the real measure.", 225 items: [ 226 s4("inflam.recovery_speed", "After exertion or illness, how quickly do you recover?", ["Quickly","N
226ormally","Slowly","Very slowly"], [0,1,2,3]), 227 s4("inflam.aches_stiffness", "How often do you have unexplained aches or stiffness?", ["Rarely","Occasionally","Often","Most days"], [0,1,2,3]), 228 s4("inflam.diet_tendency", "How often do you eat ultra-processed foods, sugary drinks, or fried food?", ["Rarely","Weekly","Most days","Daily"], [0,1,2,3]) 229 ] 230 }, 231 232 /* ---- D3 Healthy Aging (behaviour, POSITIVE, derive:MEDAS/HPLP-II) -- */ 233 { 234 id: "aging", kind: "questions", title: "Daily habits", profile: "aging", 235 note: "These habits are scored positively: good habits add health.", 236 items: [ 237 { key: "aging.med_olive_veg", profile: "aging", basis: "behaviour", input: "single", prompt: "Do you use olive oil as your main fat and eat 2+ veg servings daily?", options: [ { value: "yes_both", label: "Yes, both", h: 2 }, { value: "one", label: "One of the two", h: 1 }, { value: "neither", label: "Neither", h: 0 } ] }, 238 { key: "aging.med_fish_legumes", profile: "aging", basis: "behaviour", input: "single", prompt: "Do you eat fish and legumes several times a week?", options: [ { value: "yes", label: "Yes", h: 2 }, { value: "sometimes", label: "Sometimes", h: 1 }, { value: "rarely", label: "Rarely", h: 0 } ] }, 239 { key: "aging.med_processed", profile: "aging", basis: "behaviour", input: "single", prompt: "How often do you eat processed meat or commercial sweets?", options: [ { value: "rarely", label: "Rarely", h: 2 }, { value: "weekly", label: "Weekly", h: 1 }, { value: "most_days", label: "Most days", h: 0 } ] }, 240 { key: "aging.prevention_mindset", profile: "aging", basis: "behaviour", input: "single", prompt: "Do you take active steps for prevention (check-ups, screening, health goals)?", options: [ { value: "regularly", label: "Regularly", h: 2 }, { value: "sometimes", label: "Sometimes", h: 1 }, { value: "rarely", label: "Rarely", h: 0 } ] }, 241 { key: "aging.health_responsibility", profile: "aging", basis: "behaviour", input: "single", prompt: "Do you monitor your own health and act on it?", options: [ { value: "actively", label: "Actively", h: 2 }, { value: "somewhat", label: "Somewhat", h: 1 }, { value: "not_really", label: "Not really", h: 0 } ] } 242 ] 243 }, 244 245 /* ---- The intent (T325c, Jesse D1, 2026-09-17) ------------------------ 246 How a person wants to get to their result, and what they will spend 247 once. Stored as codes, scored nowhere, and read by the routing matrix 248 and the profile. T368d (2026-09-29) places them: while the questions' 249 switch is on for the run (`BP_INTENT_QUESTIONS`, pinned to the run at its 250 first answer), buildSteps() asks this section before the e-mail step, 251 after the last health area in the short scan and after its one area in a 252 section scan, and never again after it. The top band was split on 253 Jesse's word of 2026-09-29 (migration 104): 1000_12000 and 12000_plus 254 replace 1000_plus. */ 255 { 256 id: "intent", kind: "questions", title: "Your way to the result", 257 items: [ 258 { key: "intent.guidance", basis: "preference", input: "single", 259 prompt: "If you had to pick one way to get to the result you want, which would it be?", 260 help: "We use your answers to show you help that fits, and to send you matching advice emails if you say yes to them in the next step.", 261 options: [ 262 { value: "lab_testing", label: "Lab testing (blood work): understand what is happening inside my body" }, 263 { value: "personal_plan", label: "A personal plan I carry out on my own" }, 264 { value: "plan_with_coaching", label: "A personal plan plus coaching from an expert (be guided)" }, 265 { value: "supplement_advice", label: "Personal supplement advice: I just want to take the right supplements" }, 266 { value: "all_in", label: "All in: full blood work plus guidance and coaching from a true expert I choose" } ] }, 267 { key: "intent.budget", basis: "preference", input: "single", 268 prompt: "How much are you willing to spend, once, to get there?", 269 help: "We use your answers to show you help that fits, and to send you matching advice emails if you say yes to them in the next step.", 270 options: [ 271 { value: "under_50", label: "Below EUR 50" }, 272 { value: "under_100", label: "EUR 50 to 100" }, 273 { value: "under_500", label: "EUR 100 to 500" }, 274 { value: "under_1000", label: "EUR 500 to 1,000" }, 275 { value: "1000_12000", label: "EUR 1,000 to 12,000" }, 276 { value: "12000_plus", label: "EUR 12,000 or more: whatever it takes to make a massive change" } ] } 277 ] 278 }, 279 280 /* ---- Optional objective inputs (deferred wedge) --------------------- */ 281 { 282 id: "extras", kind: "extras", title: "Optional: add your data", 283 body: "You can add blood results or wearable data to sharpen your picture. This is optional and can be done later. It is the only way some profiles (metabolic, cardiovascular, inflammation, hormonal) reach high confidence." 284 }, 285 286 { id: "finish", kind: "finish", title: "See your BioProfile" } 287 ] 288}; 289 290/* ---- Progressive single-session model (mini -> opt-in -> full) ---------- 291 The mini-test is NOT a separate analysis. It is the opening block of ONE 292 analysis session: the questions below (plus preamble, safety, WHO-5) are asked 293 first and give a cross-profile read; the opt-in gate sits right after them; the 294 person then continues the SAME session to complete the full analysis. The 295 engine already scores any partial set of answers (confidence rises with 296 coverage), so the "mini result" is just this session scored at the gate, and 297 the "full result" is the same session scored at completion. One session, one 298 date, two result snapshots. See ../../analysis/questionnaire-item-bank.md. */ 299/* Mini set at THREE questions per main area (Jesse 2026-07-18: "3 questions per 300 area instead of 2... worth making the foundation stronger"). Every third 301 question is taken from the EXISTING item bank; nothing invented. */ 302globalThis.BP_Q.miniKeys = [ 303 "energy.daily_stability", "energy.midday_dip", "energy.stimulant_reliance",
304 "sleep.onset_latency", "sleep.night_waking", "sleep.rested_waking", 305 "stress.control", "stress.overwhelm", "stress.coping", 306 "gut.stool_form", "gut.pain_frequency", "gut.bloating", 307 "brain.clarity_fog", "brain.focus", "brain.memory_lapses", 308 "hormonal.hot_flushes", "hormonal.mood_swings_f", "hormonal.sleep_hormonal", 309 "hormonal.libido_m", "hormonal.vitality_m", "hormonal.mood_m", 310 "metabolic.high_glucose_history", "metabolic.activity_findrisc", "metabolic.family_diabetes", 311 "cardio.activity_ipaq", "cardio.smoking", "cardio.exertion_capacity", 312 "inflam.recovery_speed", "inflam.aches_stiffness", "inflam.diet_tendency", 313 "aging.med_olive_veg", "aging.prevention_mindset", "aging.med_fish_legumes" 314]; 315/* Whole sections that belong to the pre-gate (mini) phase regardless of item. */ 316globalThis.BP_Q.miniSections = ["intro", "about", "safety", "wellbeing", "mood"]; 317/* T368d (T325b's ruling): while the intent questions are asked, these items of 318 the About you step move behind the e-mail step, as the first step after it. 319 Nothing scores on them. */ 320globalThis.BP_Q.postGateKeys = ["pre.glp1"]; 321 322/* ---- Section scans (Jesse 2026-07-28) ------------------------------------ 323 The mini test becomes ONE section of the questionnaire IN FULL, chosen by the 324 `?scan=` URL parameter. Same page, same funnel, same opt-in gate, same landing 325 - only the questions change. The safety battery is skipped on a scan (his 326 explicit allowance) and asked after the gate instead. With no parameter, or an 327 unknown one, the standard mini test runs exactly as before. 328 329 This registry is the ONE source of truth: the browser flow builds the step 330 list from it and the server canonicalises the submitted value against it 331 (api/lead.js), so an advertorial link can never write a junk value onto a lead. 332 `sections` = questionnaire section ids (gating still applies, so the two 333 hormonal cohorts resolve to whichever one fits the person). `area`/`icon`/ 334 `color` drive the front-page chip; the display strings live in the i18n packs 335 under `scan.name.<slug>` and never in here. */ 336globalThis.BP_Q.scans = { 337 "energy": { sections: ["energy"], area: "energy", icon: "energy", color: "lime" }, 338 "sleep": { sections: ["sleep"], area: "sleep", icon: "sleep", color: "forest" }, 339 "stress": { sections: ["stress"], area: "stress", icon: "stress", color: "slate" }, 340 "gut-health": { sections: ["gut"], area: "gut", icon: "gut", color: "emerald" }, 341 "brain-health": { sections: ["brain"], area: "brain", icon: "brain", color: "teal" }, 342 "hormonal-balance": { sections: ["hormonal_f", "hormonal_m"], area: "hormonal", icon: "hormonal_f", color: "slate" }, 343 "metabolic-health": { sections: ["metabolic"], area: "metabolic", icon: "metabolic", color: "olive" }, 344 "heart-health": { sections: ["cardio"], area: "cardio", icon: "cardio", color: "teal" }, 345 "recovery": { sections: ["inflam"], area: "inflam", icon: "inflam", color: "emerald" }, 346 "healthy-aging": { sections: ["aging"], area: "aging", icon: "aging", color: "forest" }, 347 "wellbeing": { sections: ["wellbeing", "mood"], area: "wellbeing", icon: "wellbeing", color: "teal" } 348}; 349 350/* Forgiving aliases so an advertorial link that says the obvious thing still 351 lands on the right scan (plain section ids, common synonyms, Dutch words). 352 Anything not in either map falls back to the standard mini test. */ 353globalThis.BP_Q.scanAliases = { 354 "brain": "brain-health", "focus": "brain-health", "brain-fog": "brain-health", "brein": "brain-health", 355 "gut": "gut-health", "digestion": "gut-health", "darm": "gut-health", "darmen": "gut-health", "darmgezondheid": "gut-health", 356 "metabolic": "metabolic-health", "metabolism": "metabolic-health", "stofwisseling": "metabolic-health", 357 "cardio": "heart-health", "heart": "heart-health", "hart": "heart-health", 358 "hormonal": "hormonal-balance", "hormones": "hormonal-balance", "hormonen": "hormonal-balance", "hormoonbalans": "hormonal-balance", 359 "inflam": "recovery", "inflammation": "recovery", "herstel": "recovery", 360 "aging": "healthy-aging", "ageing": "healthy-aging", "longevity": "healthy-aging", "leefstijl": "healthy-aging", 361 "energie": "energy", "vitaliteit": "energy",
362 "slaap": "sleep", "sleep-recovery": "sleep", 363 "mood": "wellbeing", "welzijn": "wellbeing", "mental-wellbeing": "wellbeing" 364}; 365 366/* Normalise + resolve a raw parameter value to a canonical slug, or null. 367 Null is the documented fallback: the standard mini test, never a broken page. */ 368globalThis.BP_Q.resolveScan = function (raw) { 369 if (raw == null) return null; 370 var s = String(raw).slice(0, 64).toLowerCase().trim() 371 .replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/g, "").replace(/-+/g, "-").replace(/^-|-$/g, ""); 372 if (!s) return null; 373 if (globalThis.BP_Q.scans[s]) return s; 374 var a = globalThis.BP_Q.scanAliases[s]; 375 return a && globalThis.BP_Q.scans[a] ? a : null; 376}; 377 378/* ---- tiny item factories (keep the definition above compact) ---------- */ 379function s4(key, prompt, labels, points, basis) { 380 var profileGuess = key.split(".")[0]; 381 return { key: key, profile: profileGuess, basis: basis || (profileGuess === "brain" ? "perceived" : "symptom"), 382 input: labels.length > 4 ? "single" : "scale", 383 prompt: prompt, options: labels.map(function (l, i) { return { value: slug(l, i), label: l, p: points[i] }; }) }; 384} 385function who5Item(key, prompt) { 386 var labels = ["At no time","Some of the time","Less than half the time","More than half the time","Most of the time","All of the time"]; 387 return [{ key: key, profile: "who5", basis: "wellbeing", input: "single", prompt: prompt, 388 options: labels.map(function (l, i) { return { value: slug(l, i), label: l, w: i }; }) }]; 389} 390function mood4(key, prompt) { 391 var labels = ["Not at all","Several days","More than half the days","Nearly every day"]; 392 return { key: key, profile: "mood", basis: "mood", input: "scale", prompt: prompt, 393 options: labels.map(function (l, i) { return { value: slug(l, i), label: l, m: i }; }) }; 394} 395function slug(label, i) { return label.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || ("o" + i); } 396 397/* T553: an answer behind a switch is offered, and accepted by the server, only while its switch is on: in the browser 398 BP_CONFIG.newGoals "1" (app/config.js), on the server the environment's BP_NEW_GOALS "1". Off by default, so the five 399 new goals reach the live scan only by the hub's release, after their ActiveCampaign tags exist (Astra's code read 2). */ 400(function () { 401 var on = false; 402 try { 403 if (typeof process !== "undefined" && process.env && process.env.BP_NEW_GOALS === "1") on = true; 404 if (typeof window !== "undefined" && window.BP_CONFIG && window.BP_CONFIG.newGoals === "1") on = true; 405 } catch (e) { on = false; } 406 if (on) return; 407 globalThis.BP_Q.sections.forEach(function (sec) { 408 (sec.items || []).forEach(function (it) { 409 if (it.options) it.options = it.options.filter(function (o) { return o.gate !== "new_goals"; }); 410 }); 411 }); 412})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.