Scott Harshman commited on
Commit
b3d0687
Β·
verified Β·
1 Parent(s): 50f3e93

v2.3: subhead, trust line, font fix, transparent bg, stand-alone medical route

Browse files
Files changed (1) hide show
  1. app.py +234 -13
app.py CHANGED
@@ -1113,6 +1113,81 @@ def send_to_hubspot_soft_exit(first_name, email, interest, soft_exit_reason):
1113
  return str(exc)
1114
 
1115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1116
  def send_soft_exit_guide_email(email, first_name, guide_name, guide_url):
1117
  """Send a brief email with a link to the matched free guide.
1118
  Uses the existing _sendgrid_send helper. Non-blocking.
@@ -1234,7 +1309,12 @@ st.markdown(
1234
 
1235
  st.markdown("""
1236
  <style>
1237
- .stApp { background-color: #f0f6fa; }
 
 
 
 
 
1238
  html, body, [class*="css"] { font-family: 'Barlow', 'Segoe UI', sans-serif; }
1239
  .stMarkdown p, .stMarkdown span,
1240
  div[data-testid="stMarkdownContainer"] p,
@@ -1693,11 +1773,15 @@ st.markdown(
1693
  .quiz-card .quiz-question {
1694
  color: #ffffff !important;
1695
  font-family: 'Barlow Condensed', sans-serif !important;
1696
- font-weight: 800 !important;
1697
  font-size: 1.55rem !important;
1698
- line-height: 1.25 !important;
1699
  margin: 0 0 8px !important;
1700
- letter-spacing: 0.2px;
 
 
 
 
1701
  }
1702
  .quiz-card .quiz-helper {
1703
  color: #8ecae6 !important;
@@ -2087,8 +2171,8 @@ st.markdown(
2087
  '<div class="hero">'
2088
  f'<img class="hero-logo" src="{LOGO_B64}" alt="Ignite Performance and Health" />'
2089
  '<div class="hero-title">Ignition Sequence&#8482;</div>'
2090
- '<div class="hero-sub">Personalized Wellness Assessment</div>'
2091
- '<div class="hero-tagline">Physician-led &nbsp;&bull;&nbsp; Chesterfield, MO &nbsp;&bull;&nbsp; Accepting new patients</div>'
2092
  '</div>',
2093
  unsafe_allow_html=True,
2094
  )
@@ -2160,10 +2244,16 @@ if not st.session_state.get("_fired_quiz_start"):
2160
  # ── Progress indicator ───────────────────────────────────────────────────────
2161
  TOTAL_STEPS_MAIN = 5
2162
  TOTAL_STEPS_SOFT_EXIT = 2
 
2163
 
2164
  current_step = st.session_state["quiz_step"]
2165
  current_path = st.session_state["quiz_path"]
2166
- total_steps = TOTAL_STEPS_MAIN if current_path == "main" else TOTAL_STEPS_SOFT_EXIT
 
 
 
 
 
2167
 
2168
  # Don't show progress on processing/done steps. Card wrapper opens here and includes
2169
  # whatever the step renders next (question + helper) so they share one navy panel.
@@ -2200,6 +2290,16 @@ def _trigger_soft_exit(reason, data_updates=None):
2200
  _fire_quiz_event("quiz_soft_exit_triggered", {"reason": reason})
2201
  st.rerun()
2202
 
 
 
 
 
 
 
 
 
 
 
2203
  # ═════════════════════════════════════════════════════════════════════════════
2204
  # MAIN PATH
2205
  # ═════════════════════════════════════════════════════════════════════════════
@@ -2263,21 +2363,26 @@ if current_path == "main":
2263
  if st.button("← Back", key="back_step_3"):
2264
  _go_back(2)
2265
 
2266
- # ─── Step 4: Commitment qualifier ────────────────────────────────────────
2267
  elif current_step == 4:
2268
  _render_quiz_card_open(current_step, total_steps,
2269
- "Our integrated programs run $800–$2,500/month for serious change. Is that within reach?",
2270
- "Six-month minimum. Real lifestyle work alongside medical support. We work with people who are committed.")
 
 
2271
 
2272
  commitment_options = [
2273
- ("ready", "Yes, I am ready to invest in this", "main"),
2274
- ("learning", "Maybe β€” I want to learn more first", "main"),
2275
- ("not_now", "Not right now", "soft_exit"),
 
2276
  ]
2277
  for key, label, path in commitment_options:
2278
  if st.button(label, key=f"commit_{key}", use_container_width=True):
2279
  if path == "soft_exit":
2280
  _trigger_soft_exit("not_ready", {"commitment_level": label})
 
 
2281
  else:
2282
  _advance_main(5, {"commitment_level": label})
2283
 
@@ -2571,6 +2676,122 @@ elif current_path == "soft_exit":
2571
  st.session_state["quiz_path"] = "main"
2572
  st.session_state["quiz_data"] = {}
2573
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2574
  # ── End of multi-step quiz ──────────────────────────────────────────────────
2575
 
2576
 
 
1113
  return str(exc)
1114
 
1115
 
1116
+ def send_to_hubspot_standalone(first_name, email, service_key, service_label):
1117
+ """v2.3: stand-alone medical services lead β€” same form, distinct tagging.
1118
+ Routes through HubSpot workflows that key on recommended_program and pageUri.
1119
+ """
1120
+ email = (email or "").strip().lower()
1121
+ if not email:
1122
+ return "No email; skipping HubSpot stand-alone submission"
1123
+
1124
+ # Map service key to existing primary_goal_category buckets HubSpot already knows
1125
+ standalone_category_map = {
1126
+ "glp1": "Weight Loss",
1127
+ "trt": "Hormones",
1128
+ "hrt": "Hormones",
1129
+ "peptides": "Hormones",
1130
+ "other": "Other",
1131
+ }
1132
+ category = standalone_category_map.get(service_key, "Other")
1133
+
1134
+ fields = [
1135
+ {"objectTypeId": "0-1", "name": "firstname", "value": first_name or ""},
1136
+ {"objectTypeId": "0-1", "name": "email", "value": email},
1137
+ {"objectTypeId": "0-1", "name": "lead_source", "value": LEAD_SOURCE_VALUE},
1138
+ {"objectTypeId": "0-1", "name": "recommended_program", "value": f"Stand-alone medical services β€” {service_label}"},
1139
+ {"objectTypeId": "0-1", "name": "quiz_submission_date", "value": datetime.now().strftime("%Y-%m-%d")},
1140
+ {"objectTypeId": "0-1", "name": "primary_goal_category", "value": category},
1141
+ ]
1142
+
1143
+ payload = {
1144
+ "fields": fields,
1145
+ "context": {
1146
+ "pageUri": f"https://ignitepah.com/wellness-quiz/?path=standalone&service={service_key}",
1147
+ "pageName": f"Wellness Quiz Stand-Alone Medical ({service_label})",
1148
+ },
1149
+ }
1150
+
1151
+ try:
1152
+ response = requests.post(HUBSPOT_FORMS_URL, json=payload, headers={"Content-Type": "application/json"}, timeout=10)
1153
+ if response.status_code in (200, 201):
1154
+ log.info("HubSpot stand-alone submission succeeded for %s (service=%s)", email, service_key)
1155
+ return True
1156
+ msg = f"HubSpot stand-alone returned status {response.status_code}: {response.text[:300]}"
1157
+ log.warning("HubSpot stand-alone submission failed for %s: %s", email, msg)
1158
+ return msg
1159
+ except requests.exceptions.RequestException as exc:
1160
+ log.warning("HubSpot stand-alone exception for %s: %s", email, exc)
1161
+ return str(exc)
1162
+
1163
+
1164
+ def send_standalone_pricing_email(email, first_name, service_label):
1165
+ """v2.3: send a pricing-and-next-steps email for stand-alone medical inquiries."""
1166
+ email = (email or "").strip()
1167
+ if not email:
1168
+ return "No email; skipping stand-alone pricing email"
1169
+
1170
+ body = (
1171
+ f"Hi {first_name or 'there'},\n\n"
1172
+ f"Thanks for your interest in {service_label} at Ignite Performance & Health.\n\n"
1173
+ f"Stand-alone medical services are evaluated and prescribed individually based on bloodwork, "
1174
+ f"medical history, and physician review. Pricing depends on the protocol your physician determines is right for you.\n\n"
1175
+ f"Our team will follow up within 24 to 48 hours with next steps and a pricing breakdown for {service_label}. "
1176
+ f"If you would like to skip the wait and book a brief call directly:\n"
1177
+ f"{BRAND['scheduling_url']}\n\n"
1178
+ f"All medical services are physician-supervised. We will not prescribe anything before reviewing your "
1179
+ f"intake and confirming clinical fit.\n\n"
1180
+ f"β€” The Ignite team\n"
1181
+ f"14830 Clayton Rd, Chesterfield, MO 63017\n"
1182
+ f"(314) 887-0858 | info@ignitepah.com"
1183
+ )
1184
+ return _sendgrid_send(
1185
+ to_addr=email,
1186
+ subject=f"Your inquiry: {service_label} at Ignite",
1187
+ body_text=body,
1188
+ )
1189
+
1190
+
1191
  def send_soft_exit_guide_email(email, first_name, guide_name, guide_url):
1192
  """Send a brief email with a link to the matched free guide.
1193
  Uses the existing _sendgrid_send helper. Non-blocking.
 
1309
 
1310
  st.markdown("""
1311
  <style>
1312
+ /* Transparent so the WordPress page bg shows through (v2.3 β€” fixes nav-logo overlap) */
1313
+ .stApp,
1314
+ [data-testid="stAppViewContainer"],
1315
+ [data-testid="stHeader"],
1316
+ [data-testid="stMain"],
1317
+ .main .block-container { background: transparent !important; }
1318
  html, body, [class*="css"] { font-family: 'Barlow', 'Segoe UI', sans-serif; }
1319
  .stMarkdown p, .stMarkdown span,
1320
  div[data-testid="stMarkdownContainer"] p,
 
1773
  .quiz-card .quiz-question {
1774
  color: #ffffff !important;
1775
  font-family: 'Barlow Condensed', sans-serif !important;
1776
+ font-weight: 700 !important;
1777
  font-size: 1.55rem !important;
1778
+ line-height: 1.3 !important;
1779
  margin: 0 0 8px !important;
1780
+ letter-spacing: 0.4px;
1781
+ text-shadow: none !important;
1782
+ -webkit-font-smoothing: antialiased;
1783
+ -moz-osx-font-smoothing: grayscale;
1784
+ text-rendering: optimizeLegibility;
1785
  }
1786
  .quiz-card .quiz-helper {
1787
  color: #8ecae6 !important;
 
2171
  '<div class="hero">'
2172
  f'<img class="hero-logo" src="{LOGO_B64}" alt="Ignite Performance and Health" />'
2173
  '<div class="hero-title">Ignition Sequence&#8482;</div>'
2174
+ '<div class="hero-sub">Your Personalized Health Plan</div>'
2175
+ '<div class="hero-tagline">Physician-led &nbsp;&bull;&nbsp; Integrated System &nbsp;&bull;&nbsp; One Care Team</div>'
2176
  '</div>',
2177
  unsafe_allow_html=True,
2178
  )
 
2244
  # ── Progress indicator ───────────────────────────────────────────────────────
2245
  TOTAL_STEPS_MAIN = 5
2246
  TOTAL_STEPS_SOFT_EXIT = 2
2247
+ TOTAL_STEPS_STANDALONE = 1 # v2.3: stand-alone medical-services route
2248
 
2249
  current_step = st.session_state["quiz_step"]
2250
  current_path = st.session_state["quiz_path"]
2251
+ if current_path == "main":
2252
+ total_steps = TOTAL_STEPS_MAIN
2253
+ elif current_path == "standalone":
2254
+ total_steps = TOTAL_STEPS_STANDALONE
2255
+ else:
2256
+ total_steps = TOTAL_STEPS_SOFT_EXIT
2257
 
2258
  # Don't show progress on processing/done steps. Card wrapper opens here and includes
2259
  # whatever the step renders next (question + helper) so they share one navy panel.
 
2290
  _fire_quiz_event("quiz_soft_exit_triggered", {"reason": reason})
2291
  st.rerun()
2292
 
2293
+ def _trigger_standalone(data_updates=None):
2294
+ """v2.3: route prospect to stand-alone medical services lead-capture path.
2295
+ Lighter ask than main path (no phone, service-type dropdown, pricing-info CTA)."""
2296
+ if data_updates:
2297
+ st.session_state["quiz_data"].update(data_updates)
2298
+ st.session_state["quiz_path"] = "standalone"
2299
+ st.session_state["quiz_step"] = 1
2300
+ _fire_quiz_event("quiz_standalone_triggered", {"path": "standalone"})
2301
+ st.rerun()
2302
+
2303
  # ═════════════════════════════════════════════════════════════════════════════
2304
  # MAIN PATH
2305
  # ═════════════════════════════════════════════════════════════════════════════
 
2363
  if st.button("← Back", key="back_step_3"):
2364
  _go_back(2)
2365
 
2366
+ # ─── Step 4: Commitment + service-path qualifier (v2.3) ─────────────────
2367
  elif current_step == 4:
2368
  _render_quiz_card_open(current_step, total_steps,
2369
+ "Our integrated Ignition Sequence&#8482; programs run $800–$2,500/month. Is that within reach?",
2370
+ "That is our comprehensive program β€” physician oversight, training, and nutrition under one care team. "
2371
+ "Stand-alone medical services (peptides, hormones, GLP-1) are priced separately. "
2372
+ "We work with people who aren't just interested, they are committed.")
2373
 
2374
  commitment_options = [
2375
+ ("ready", "Yes β€” the integrated program is for me", "main"),
2376
+ ("standalone", "I'm looking for stand-alone medical services", "standalone"),
2377
+ ("learning", "Maybe β€” I want to learn more first", "main"),
2378
+ ("not_now", "Not right now", "soft_exit"),
2379
  ]
2380
  for key, label, path in commitment_options:
2381
  if st.button(label, key=f"commit_{key}", use_container_width=True):
2382
  if path == "soft_exit":
2383
  _trigger_soft_exit("not_ready", {"commitment_level": label})
2384
+ elif path == "standalone":
2385
+ _trigger_standalone({"commitment_level": label})
2386
  else:
2387
  _advance_main(5, {"commitment_level": label})
2388
 
 
2676
  st.session_state["quiz_path"] = "main"
2677
  st.session_state["quiz_data"] = {}
2678
 
2679
+ # ═════════════════════════════════════════════════════════════════════════════
2680
+ # STAND-ALONE MEDICAL SERVICES PATH (v2.3)
2681
+ # ═════════════════════════════════════════════════════════════════════════════
2682
+ # Stops medical-only prospects from leaking into the soft-exit funnel. Lighter
2683
+ # ask than main path: no phone required, single-screen capture with service
2684
+ # dropdown. Routes to a pricing-info email rather than a recommendation PDF.
2685
+ elif current_path == "standalone":
2686
+
2687
+ # ─── Stand-alone Step 1: Service + lighter contact (single screen) ──────
2688
+ if current_step == 1:
2689
+ _render_quiz_card_open(current_step, total_steps,
2690
+ "What service are you looking at?",
2691
+ "Pricing depends on the service. We will send you details and next steps within 24 to 48 hours.")
2692
+
2693
+ with st.form("standalone_contact_form"):
2694
+ service_options = [
2695
+ ("glp1", "GLP-1 weight loss (semaglutide / tirzepatide)"),
2696
+ ("trt", "Testosterone replacement (TRT)"),
2697
+ ("hrt", "Hormone therapy for women (HRT / menopause)"),
2698
+ ("peptides", "Peptides (BPC-157, ipamorelin, etc.)"),
2699
+ ("other", "Other / not sure yet"),
2700
+ ]
2701
+ service_labels = [label for _, label in service_options]
2702
+ service_choice = st.selectbox(
2703
+ "Service of interest",
2704
+ options=service_labels,
2705
+ index=0,
2706
+ )
2707
+ service_key = next((k for k, l in service_options if l == service_choice), "other")
2708
+
2709
+ first_name = st.text_input("First name", value=st.session_state["quiz_data"].get("first_name", ""))
2710
+ email = st.text_input("Email address", value=st.session_state["quiz_data"].get("email", ""))
2711
+
2712
+ consent_contact = st.checkbox(
2713
+ "I consent to be contacted by Ignite Performance and Health regarding pricing and next steps.",
2714
+ key="standalone_consent",
2715
+ )
2716
+
2717
+ st.markdown(
2718
+ '<p style="color:#666;font-size:0.78rem;line-height:1.5;margin-top:8px;">'
2719
+ 'Stand-alone medical services are physician-supervised. Nothing is prescribed before '
2720
+ 'a clinical review confirms fit.'
2721
+ '</p>',
2722
+ unsafe_allow_html=True,
2723
+ )
2724
+
2725
+ submitted = st.form_submit_button("Send Me Pricing β†’", use_container_width=True)
2726
+
2727
+ if submitted:
2728
+ errors = []
2729
+ if not first_name: errors.append("First name")
2730
+ if not email: errors.append("Email")
2731
+ if not consent_contact: errors.append("Consent checkbox")
2732
+
2733
+ if errors:
2734
+ st.error(f"Please complete: {', '.join(errors)}")
2735
+ else:
2736
+ st.session_state["quiz_data"]["first_name"] = first_name
2737
+ st.session_state["quiz_data"]["email"] = email
2738
+ st.session_state["quiz_data"]["service_key"] = service_key
2739
+ st.session_state["quiz_data"]["service_label"] = service_choice
2740
+ st.session_state["quiz_step"] = 2 # process
2741
+ _fire_quiz_event("quiz_complete", {"path": "standalone", "service": service_key})
2742
+ st.rerun()
2743
+
2744
+ if st.button("← Back", key="back_standalone_step_1"):
2745
+ st.session_state["quiz_path"] = "main"
2746
+ st.session_state["quiz_step"] = 4
2747
+ st.rerun()
2748
+
2749
+ # ─── Stand-alone Step 2: Process and show thank-you ─────────────────────
2750
+ elif current_step == 2:
2751
+ first_name = st.session_state["quiz_data"].get("first_name", "")
2752
+ email = st.session_state["quiz_data"].get("email", "")
2753
+ service_key = st.session_state["quiz_data"].get("service_key", "other")
2754
+ service_label = st.session_state["quiz_data"].get("service_label", "Stand-alone medical services")
2755
+
2756
+ # HubSpot β€” stand-alone tag via recommended_program
2757
+ send_to_hubspot_standalone(
2758
+ first_name=first_name,
2759
+ email=email,
2760
+ service_key=service_key,
2761
+ service_label=service_label,
2762
+ )
2763
+
2764
+ # SendGrid β€” pricing-and-next-steps email
2765
+ send_standalone_pricing_email(
2766
+ email=email,
2767
+ first_name=first_name,
2768
+ service_label=service_label,
2769
+ )
2770
+
2771
+ # Render thank-you
2772
+ st.markdown(
2773
+ '<div class="hero">'
2774
+ f'<img class="hero-logo" src="{LOGO_B64}" alt="Ignite Performance and Health" />'
2775
+ '<div class="hero-title">Thanks β€” pricing details are on the way.</div>'
2776
+ '<div class="hero-sub">Check your email in the next few minutes.</div>'
2777
+ '</div>',
2778
+ unsafe_allow_html=True,
2779
+ )
2780
+
2781
+ st.markdown(
2782
+ f'<div class="cta-box">'
2783
+ f'<p>We sent next steps for <strong>{service_label}</strong> to <strong>{email}</strong>.</p>'
2784
+ f'<p>Our team will follow up within 24 to 48 hours with a pricing breakdown and clinical review process.</p>'
2785
+ f'<a class="cta-link" href="{BRAND["scheduling_url"]}" target="_blank">Skip the wait β€” book a brief call</a>'
2786
+ f'</div>',
2787
+ unsafe_allow_html=True,
2788
+ )
2789
+
2790
+ # Reset state so refresh starts over
2791
+ st.session_state["quiz_step"] = 1
2792
+ st.session_state["quiz_path"] = "main"
2793
+ st.session_state["quiz_data"] = {}
2794
+
2795
  # ── End of multi-step quiz ──────────────────────────────────────────────────
2796
 
2797