SalesHub2 — Full Source

src/pages/SalesHub2.jsx

import React, { useState, useEffect, useRef } from "react";
import { base44 } from "@/api/base44Client";
import { getCid, fireHubOpened, markSectionViewed, maybeFireInformed, fireDoor, track as ghlTrack } from "@/lib/ghlTrack";
import SalesHub2Nav from "@/components/saleshub2/SalesHub2Nav";
import ConversationSection from "@/components/saleshub2/ConversationSection";
import HeroSection from "@/components/saleshub2/HeroSection";
import MechanismSection from "@/components/saleshub2/MechanismSection";
import LeadExamplesSection from "@/components/saleshub2/LeadExamplesSection";
import DontPromiseSection from "@/components/saleshub2/DontPromiseSection";
import ComparisonSection from "@/components/saleshub2/ComparisonSection";
import ROISection from "@/components/saleshub2/ROISection";
import PricingSection from "@/components/saleshub2/PricingSection";
import GuaranteeSection from "@/components/saleshub2/GuaranteeSection";
import FounderSection from "@/components/saleshub2/FounderSection";
import ReferralSection from "@/components/saleshub2/ReferralSection";
import QABot from "@/components/saleshub2/QABot";
import BookCallModal from "@/components/saleshub2/BookCallModal";
import BuyTrialModal from "@/components/saleshub2/BuyTrialModal";
import ExitBar from "@/components/saleshub2/ExitBar";

const DEFAULT_OFFER = { plan: "pro", term: "monthly", crmEnabled: false };

const SECTIONS = [
  { id: "conversation", label: "How You Got Here" },
  { id: "hero", label: "Overview" },
  { id: "mechanism", label: "How It Works" },
  { id: "leads", label: "What a Lead Is" },
  { id: "dont-promise", label: "What We Don't Promise" },
  { id: "comparison", label: "Why Us" },
  { id: "roi", label: "The Math" },
  { id: "pricing", label: "Pricing" },
  { id: "guarantee", label: "Guarantee" },
  { id: "founder", label: "Who's Behind It" },
  { id: "referral", label: "Referral" },
];

export default function SalesHub2() {
  const [activeSection, setActiveSection] = useState("conversation");
  const [showBook, setShowBook] = useState(false);
  const [showBuy, setShowBuy] = useState(() => new URLSearchParams(window.location.search).get("checkout") === "complete");
  const [selectedOffer, setSelectedOffer] = useState(() => {
    try {
      return { ...DEFAULT_OFFER, ...JSON.parse(sessionStorage.getItem("l4d_selected_offer") || "{}") };
    } catch {
      return DEFAULT_OFFER;
    }
  });
  const sectionRefs = useRef({});
  const sessionRef = useRef(null);
  const contactId = new URLSearchParams(window.location.search).get("contact_id") || "";

  // Session tracking object — accumulates all behavior for the booked call
  const session = sessionRef.current || (sessionRef.current = {
    contact_id: contactId,
    started_at: new Date().toISOString(),
    sections_viewed: {},
    threads_expanded: [],
    bot_questions: [],
    roi_inputs: {},
    exits: {},
  });

  const track = (eventName, data = {}) => {
    try {
      base44.analytics.track({ eventName, properties: { contact_id: contactId, ...data } });
    } catch {}
  };

  const onSectionView = (id) => {
    setActiveSection(id);
    if (!session.sections_viewed[id]) {
      session.sections_viewed[id] = { first_view: new Date().toISOString(), count: 0 };
      track("sh2_section_viewed", { section: id });
    }
    session.sections_viewed[id].count = (session.sections_viewed[id].count || 0) + 1;
  };

  const onThreadExpand = (threadId) => {
    if (!session.threads_expanded.includes(threadId)) {
      session.threads_expanded.push(threadId);
      track("sh2_thread_expanded", { thread: threadId });
    }
  };

  const onBotQuestion = (text) => {
    session.bot_questions.push({ text, ts: new Date().toISOString() });
    track("sh2_bot_question", { question: text });
  };

  const onRoiChange = (inputs) => {
    session.roi_inputs = inputs;
    track("sh2_roi_input", inputs);
  };

  const onExit = (type, stage) => {
    session.exits[type] = session.exits[type] || [];
    session.exits[type].push({ stage, ts: new Date().toISOString() });
    track("sh2_exit", { type, stage });
  };

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) onSectionView(entry.target.id);
        });
      },
      { threshold: 0.25 }
    );
    SECTIONS.forEach(({ id }) => {
      const el = sectionRefs.current[id];
      if (el) observer.observe(el);
    });
    return () => observer.disconnect();
  }, []);

  // GHL contact tracking — cid capture, hub_opened, cumulative section views, informed
  useEffect(() => {
    const cid = getCid();
    if (!cid) return;
    fireHubOpened();

    const TRACK = [
      { id: "mechanism", name: "mechanism" },
      { id: "pricing", name: "pricing" },
      { id: "leads", name: "lead_examples" },
    ];
    const visible = {};
    const accum = {};

    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          const sec = TRACK.find((t) => t.id === entry.target.id);
          if (sec) {
            visible[sec.name] =
              entry.isIntersecting &&
              entry.intersectionRect.height >= 0.4 * window.innerHeight;
          }
        });
      },
      { threshold: [0, 0.1, 0.25, 0.5, 0.75, 1] }
    );
    TRACK.forEach((t) => {
      const el = sectionRefs.current[t.id];
      if (el) observer.observe(el);
    });

    const interval = setInterval(() => {
      TRACK.forEach((t) => {
        try {
          if (localStorage.getItem(`l4d_seen_${t.name}`)) return;
        } catch {}
        if (visible[t.name]) {
          accum[t.name] = (accum[t.name] || 0) + 1000;
          if (accum[t.name] >= 10000) {
            markSectionViewed(t.name);
            ghlTrack("section_viewed", { section: t.name });
            maybeFireInformed();
          }
        }
      });
    }, 1000);

    return () => {
      observer.disconnect();
      clearInterval(interval);
    };
  }, []);

  const scrollTo = (id) => sectionRefs.current[id]?.scrollIntoView({ behavior: "smooth" });

  const openBook = () => { setShowBook(true); onExit("book", "started"); fireDoor("demo"); };
  const openBuy = (offer) => {
    const nextOffer = offer?.plan ? { ...DEFAULT_OFFER, ...offer } : selectedOffer;
    setSelectedOffer(nextOffer);
    sessionStorage.setItem("l4d_selected_offer", JSON.stringify(nextOffer));
    setShowBuy(true);
    onExit("buy", "started");
  };

  return (
    <div className="min-h-screen bg-[#0b1220] text-white font-sans antialiased">
      <SalesHub2Nav sections={SECTIONS} active={activeSection} onNav={scrollTo} onBook={openBook} onBuy={openBuy} />

      <div className="max-w-3xl mx-auto px-4 pb-28 lg:pb-32">
        {SECTIONS.map(({ id }) => (
          <div key={id} id={id} ref={(el) => (sectionRefs.current[id] = el)} className="min-h-[70vh] py-16 lg:py-20 scroll-mt-4">
            {id === "conversation" && <ConversationSection />}
            {id === "hero" && <HeroSection onBook={openBook} />}
            {id === "mechanism" && <MechanismSection />}
            {id === "leads" && <LeadExamplesSection onThreadExpand={onThreadExpand} />}
            {id === "dont-promise" && <DontPromiseSection />}
            {id === "comparison" && <ComparisonSection />}
            {id === "roi" && <ROISection onChange={onRoiChange} />}
            {id === "pricing" && <PricingSection onBook={openBook} onBuy={openBuy} />}
            {id === "guarantee" && <GuaranteeSection onBuy={openBuy} />}
            {id === "founder" && <FounderSection />}
            {id === "referral" && <ReferralSection onBook={openBook} />}
          </div>
        ))}
      </div>

      <ExitBar onBook={openBook} onBuy={openBuy} />
      <QABot onBook={openBook} onBuy={openBuy} onNav={scrollTo} onBotQuestion={onBotQuestion} />

      {showBook && (
        <BookCallModal
          onClose={() => setShowBook(false)}
          onExit={(stage) => onExit("book", stage)}
          onBuy={openBuy}
          session={session}
        />
      )}
      {showBuy && (
        <BuyTrialModal
          onClose={() => setShowBuy(false)}
          onExit={(stage) => onExit("buy", stage)}
          contactId={contactId}
          initialOffer={selectedOffer}
          onOfferChange={(offer) => {
            setSelectedOffer(offer);
            sessionStorage.setItem("l4d_selected_offer", JSON.stringify(offer));
          }}
        />
      )}
    </div>
  );
}

src/components/saleshub2/ConversationSection.jsx

import React from "react";
import { MessageSquare } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";
import SectionVideo from "@/components/saleshub2/SectionVideo";

const HOW_AUDIO = "https://media.base44.com/files/public/68cda4fea6b13489887bfb79/42ef824d5_1Roger-HowYouGotHere-095.mp3";
const HOW_VIDEO = "https://media.base44.com/videos/public/68cda4fea6b13489887bfb79/a3e144392_0-howyougothere.mov";

const B2B_THREAD = [
  { from: "ai", text: "Hey — I work with a lead-gen agency taking on wholesalers this quarter. You take seller leads?" },
  { from: "lead", text: "Yeah depending on market and price. What's the story?" },
  { from: "ai", text: "AI cold-SMS, motivated seller data, qualified conversations delivered to your CRM. Exclusive, not resold. Want to see what a lead actually looks like before anything else?" },
  { from: "lead", text: "Sure send it" },
];

export default function ConversationSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">0 · How You Got Here</div>
      <h1 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">
        You got here from a text message.
      </h1>
      <p className="text-white/55 text-base mb-8 max-w-xl leading-relaxed">
        Not a form, not an ad. Our AI found you, started a conversation, and sent you this link.
      </p>

      <div className="max-w-lg mb-8">
        <SectionVideo src={HOW_VIDEO} label="How you got here" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>
      <div className="max-w-lg mb-8">
        <SectionAudio src={HOW_AUDIO} label="How you got here" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-5 sm:p-6 max-w-lg mb-6">
        <div className="flex items-center justify-between mb-4">
          <div className="text-white/30 text-xs">A real conversation from this campaign — not yours, but the same flow.</div>
          <MessageSquare className="w-4 h-4 text-white/20" />
        </div>
        <div className="flex flex-col gap-2.5">
          {B2B_THREAD.map((m, i) => (
            <div key={i} className={`flex ${m.from === "ai" ? "justify-start" : "justify-end"}`}>
              <div className={`max-w-[82%] px-3.5 py-2 rounded-xl text-sm leading-relaxed ${
                m.from === "ai"
                  ? "bg-blue-600/15 text-white/85 rounded-tl-sm border border-blue-500/15"
                  : "bg-white/[0.06] text-white/70 rounded-tr-sm"
              }`}>
                {m.from === "ai" && <div className="text-blue-400 text-[10px] font-medium mb-1">AI System</div>}
                {m.text}
              </div>
            </div>
          ))}
        </div>
      </div>

      <div className="rounded-xl bg-blue-600/[0.06] border border-blue-500/15 p-5 max-w-lg">
        <p className="text-white/70 text-sm leading-relaxed">
          Same system, pointed at homeowners instead of investors. If it got a busy operator to click a link, consider what it does with someone three months behind on payments.
        </p>
      </div>
    </div>
  );
}

src/components/saleshub2/HeroSection.jsx

import React from "react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const OVERVIEW_AUDIO = "https://media.base44.com/files/public/68cda4fea6b13489887bfb79/288f1dd65_2Roger-Overview-095.mp3";

export default function HeroSection({ onBook }) {
  return (
    <div className="flex flex-col justify-center min-h-[60vh]">
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">1 · Overview</div>
      <h1 className="text-3xl sm:text-4xl lg:text-5xl font-bold text-white mb-5 leading-tight">
        We generate motivated seller leads for wholesalers and investors.
      </h1>
      <p className="text-white/55 text-lg mb-5 max-w-xl leading-relaxed">
        60-second overview, or explore on your own. Everything is open — pricing, lead examples, the math. The booking step is the only thing that asks you anything.
      </p>

      <div className="flex flex-wrap items-center gap-x-4 gap-y-1 text-white/40 text-xs mb-8">
        <span><span className="text-white/70 font-semibold">$10M+</span> transactions</span>
        <span className="text-white/20">·</span>
        <span><span className="text-white/70 font-semibold">5,000+</span> seller leads worked</span>
        <span className="text-white/20">·</span>
        <span><span className="text-white/70 font-semibold">100+</span> contracts signed</span>
        <span className="text-white/20">·</span>
        <span><span className="text-white/70 font-semibold">6 yrs</span> lead generation</span>
      </div>

      <div className="max-w-lg mb-8">
        <SectionAudio src={OVERVIEW_AUDIO} label="60-second overview" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>
    </div>
  );
}

src/components/saleshub2/MechanismSection.jsx

import React from "react";
import { Database, MessageSquare, Filter, Inbox } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const STEPS = [
  {
    icon: Database,
    title: "Build the list",
    desc: "Your market, your criteria. Motivation filters: pre-foreclosure, tax delinquent, probate, divorce, tired landlord, absentee, liens, high equity, 65+. Off-market only. Exclusive — never resold.",
  },
  {
    icon: MessageSquare,
    title: "Start conversations",
    desc: "AI SMS, 24/7, thousands of messages. Real back-and-forth; the bot has the conversation, not a blast.",
  },
  {
    icon: Filter,
    title: "Qualify",
    desc: "Four pillars: why selling, how fast, what price, what condition. Three of four required to reach you.",
  },
  {
    icon: Inbox,
    title: "Delivery",
    desc: "Lands in your CRM with the full conversation attached. Same number the bot used, so you continue the thread. Appointments auto-confirmed.",
  },
];

export default function MechanismSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">2 · How It Works</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">The system, plainly.</h2>
      <p className="text-white/55 text-base mb-8 max-w-lg">
        We replaced the cold-call grind with AI. You get qualified sellers who've already answered the questions — ready for your close, not your opener.
      </p>

      <div className="mb-8">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/5526ad40e_3Roger-HowitWorks-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="flex flex-col gap-3">
        {STEPS.map(({ icon: Icon, title, desc }, i) => (
          <div key={title} className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-5 flex gap-4">
            <div className="shrink-0">
              <div className="w-10 h-10 rounded-xl bg-blue-600/15 flex items-center justify-center text-blue-400">
                <Icon className="w-5 h-5" />
              </div>
            </div>
            <div className="flex-1">
              <div className="flex items-center gap-2 mb-1.5">
                <span className="text-white/30 text-xs font-medium">Step {i + 1}</span>
                <h3 className="text-base font-semibold text-white">{title}</h3>
              </div>
              <p className="text-white/55 text-sm leading-relaxed">{desc}</p>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

src/components/saleshub2/LeadExamplesSection.jsx

import React, { useState } from "react";
import { GROUPS } from "@/lib/saleshub2Groups";
import SectionAudio from "@/components/saleshub2/SectionAudio";

// Tag types — green = qualification pillar, amber = agent-handling, blue = booking
const TAGS = {
  M: { label: "Motivation", cls: "bg-green-600/15 text-green-400 border-green-500/20" },
  T: { label: "Timeline", cls: "bg-green-600/15 text-green-400 border-green-500/20" },
  P: { label: "Price", cls: "bg-green-600/15 text-green-400 border-green-500/20" },
  C: { label: "Condition", cls: "bg-green-600/15 text-green-400 border-green-500/20" },
  A: { label: "Agent", cls: "bg-amber-600/15 text-amber-400 border-amber-500/20" },
  B: { label: "Booked", cls: "bg-blue-600/15 text-blue-400 border-blue-500/20" },
};

function Tags({ tags }) {
  if (!tags?.length) return null;
  return (
    <div className="flex flex-wrap gap-1 mt-1.5">
      {tags.map((t) => {
        const info = TAGS[t];
        if (!info) return null;
        return (
          <span key={t} className={`px-1.5 py-0.5 rounded text-[10px] font-medium border ${info.cls}`}>
            {info.label}
          </span>
        );
      })}
    </div>
  );
}

function Msg({ m }) {
  return (
    <div className={`flex ${m.from === "ai" ? "justify-start" : "justify-end"}`}>
      <div className={`max-w-[80%] px-3 py-2 rounded-xl text-sm leading-relaxed ${
        m.from === "ai"
          ? "bg-blue-600/15 text-white/85 rounded-tl-sm border border-blue-500/15"
          : "bg-white/[0.06] text-white/75 rounded-tr-sm"
      }`}>
        {m.from === "ai" && <div className="text-blue-400 text-[10px] font-medium mb-0.5">AI</div>}
        {m.text}
        <Tags tags={m.tags} />
      </div>
    </div>
  );
}

function Thread({ thread, onExpand }) {
  // "collapsed" | "key" | "full" — all threads start collapsed so the page scans before anyone expands
  const [view, setView] = useState("collapsed");
  const [hasExpanded, setHasExpanded] = useState(false);

  const messages = view === "collapsed" ? [] : view === "key" ? thread.key : thread.full;

  const expand = (next) => {
    // fire onExpand only on the very first expansion (collapsed -> key/full)
    if (!hasExpanded && next !== "collapsed") {
      onExpand?.(thread.id);
      setHasExpanded(true);
    }
    setView(next);
  };

  return (
    <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-4 mb-3">
      <div className="flex items-center justify-between gap-3 mb-3">
        <div>
          <div className="text-white font-medium text-sm">{thread.name}</div>
          <div className="text-white/35 text-xs">{thread.tagline}</div>
        </div>
        <div className="flex gap-2 shrink-0">
          {view === "collapsed" && (
            <button onClick={() => expand("key")} className="px-2.5 py-1 rounded-lg bg-white/[0.05] hover:bg-white/[0.08] text-white/60 text-xs font-medium transition-colors whitespace-nowrap">
              Read conversation
            </button>
          )}
          {view === "key" && (
            <button onClick={() => expand("full")} className="px-2.5 py-1 rounded-lg bg-white/[0.05] hover:bg-white/[0.08] text-white/60 text-xs font-medium transition-colors whitespace-nowrap">
              Show full thread
            </button>
          )}
          {view === "full" && (
            <>
              <button onClick={() => expand("key")} className="px-2.5 py-1 rounded-lg bg-white/[0.05] hover:bg-white/[0.08] text-white/60 text-xs font-medium transition-colors whitespace-nowrap">
                Key moments
              </button>
              <button onClick={() => expand("collapsed")} className="px-2.5 py-1 rounded-lg bg-white/[0.05] hover:bg-white/[0.08] text-white/60 text-xs font-medium transition-colors whitespace-nowrap">
                Collapse
              </button>
            </>
          )}
        </div>
      </div>
      {messages.length > 0 && (
        <div className="flex flex-col gap-2">
          {messages.map((m, i) => <Msg key={i} m={m} />)}
        </div>
      )}
    </div>
  );
}

export default function LeadExamplesSection({ onThreadExpand }) {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">3 · What a Lead Actually Is</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">Real seller conversations.</h2>
      <p className="text-white/55 text-base mb-2 max-w-lg">
        Not a name and a number — a seller who answered the questions. Read them and judge for yourself.
      </p>

      <div className="mb-6">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/4d243d3ea_4Roger-Whataleadactuallyis-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      {/* Legend */}
      <div className="flex flex-wrap gap-2 mb-8">
        {Object.values(TAGS).map((info) => (
          <span key={info.label} className={`px-2 py-0.5 rounded text-[11px] font-medium border ${info.cls}`}>
            {info.label}
          </span>
        ))}
      </div>

      <div className="flex flex-col gap-8">
        {GROUPS.map((group) => (
          <div key={group.key}>
            <div className="rounded-xl bg-blue-600/[0.04] border border-blue-500/10 p-4 mb-4">
              <h3 className="text-white font-semibold text-base mb-1">{group.title}</h3>
              <p className="text-white/50 text-sm leading-relaxed mb-2">{group.desc}</p>
              <p className="text-white/30 text-xs">{group.people}</p>
            </div>
            {group.threads.map((t) => (
              <Thread key={t.id} thread={t} onExpand={onThreadExpand} />
            ))}
          </div>
        ))}
      </div>

      <div className="mt-6 rounded-xl bg-white/[0.03] border border-white/[0.06] p-5">
        <p className="text-white/50 text-sm italic leading-relaxed">
          "Every batch is a mix of all three. Anyone showing you only the first group is showing you a highlight reel."
        </p>
      </div>
    </div>
  );
}

src/components/saleshub2/DontPromiseSection.jsx

import React from "react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

export default function DontPromiseSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">4 · What We Don't Promise</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-6 leading-tight">What we don't promise.</h2>

      <div className="mb-6">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/34404886a_5Roger-Whatwedon_tpromise-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="grid sm:grid-cols-3 gap-3 mb-5">
        <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-5">
          <div className="text-white font-semibold text-sm mb-1">Not every lead is ready to sign.</div>
          <div className="text-white/50 text-sm">Some are curious. Some are three months out. Some go nowhere.</div>
        </div>
        <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-5">
          <div className="text-white font-semibold text-sm mb-1">We don't pull real motivation over text.</div>
          <div className="text-white/50 text-sm">We get the stated reason. The real one comes out on your call.</div>
        </div>
        <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-5">
          <div className="text-white font-semibold text-sm mb-1">You close. We don't.</div>
          <div className="text-white/50 text-sm">No comps, no underwriting, no dispo.</div>
        </div>
      </div>

      <p className="text-white font-semibold text-lg leading-relaxed">
        If anyone tells you 30 out of 30 will be deals, they're selling you something we can't sell.
      </p>
    </div>
  );
}

src/components/saleshub2/ComparisonSection.jsx

import React from "react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const WHY_US_AUDIO = "https://media.base44.com/files/public/68cda4fea6b13489887bfb79/8c620d678_6Roger-Whyus-095.mp3";

const ROWS = [
  { them: "A lead card with a score", here: "The full thread, unedited" },
  { them: "Their qualification, taken on trust", here: "You read it and judge yourself" },
  { them: "Sold to you and three others", here: "Exclusive, never resold" },
  { them: "Handed off cold, new number", here: "Same number the whole way through" },
];

export default function ComparisonSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">5 · Why Us</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">You see the conversation, not our summary.</h2>
      <p className="text-white/55 text-base mb-8 max-w-lg">
        Now that price isn't a differentiator, this is what separates us.
      </p>

      <div className="max-w-lg mb-8">
        <SectionAudio src={WHY_US_AUDIO} label="You see the conversation" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="rounded-2xl bg-white/[0.03] border border-white/[0.06] overflow-hidden">
        {ROWS.map((row, i) => (
          <div key={i} className={`grid grid-cols-2 divide-x divide-white/[0.06] ${i > 0 ? "border-t border-white/[0.06]" : ""}`}>
            <div className="p-4 flex items-center gap-2">
              <span className="text-red-400/60 text-sm">✕</span>
              <span className="text-white/40 text-sm">{row.them}</span>
            </div>
            <div className="p-4 flex items-center gap-2">
              <span className="text-green-400 text-sm">✓</span>
              <span className="text-white/85 text-sm font-medium">{row.here}</span>
            </div>
          </div>
        ))}
      </div>

      <div className="mt-6 rounded-xl bg-blue-600/[0.05] border border-blue-500/15 p-5">
        <p className="text-white/80 text-sm leading-relaxed">
          Every lead gen company tells you their leads are qualified. We show you and let you decide.
        </p>
      </div>
    </div>
  );
}

src/components/saleshub2/ROISection.jsx

import React, { useState } from "react";
import { TrendingUp } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

export default function ROISection({ onChange }) {
  const [leads, setLeads] = useState(46);
  const [closeRate, setCloseRate] = useState(3.3); // 1-in-30
  const [assignment, setAssignment] = useState(20000);

  const costPerLead = 2495 / 46; // ~$54 at the canonical Pro monthly price
  const costPerDeal = costPerLead / (closeRate / 100);
  const deals = ((leads * (closeRate / 100))).toFixed(1);
  const revenue = Math.round(Number(deals) * assignment);
  const monthlySpend = Math.round(leads * costPerLead); // derived from Pro pricing

  const update = (field, val) => {
    const inputs = { leads, closeRate, assignment, [field]: val };
    onChange(inputs);
  };

  const Slider = ({ label, value, onChange, min, max, step, format, field }) => (
    <div>
      <div className="flex justify-between mb-2">
        <label className="text-white/55 text-sm">{label}</label>
        <span className="text-white font-semibold text-sm">{format(value)}</span>
      </div>
      <input type="range" min={min} max={max} step={step} value={value}
        onChange={(e) => { onChange(Number(e.target.value)); update(field, Number(e.target.value)); }}
        className="roi-slider" />
    </div>
  );

  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">6 · The Math</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">Make the price obviously worth it.</h2>
      <p className="text-white/55 text-base mb-8 max-w-lg">
        Plug in your numbers. The assumptions are all visible — the people who challenge this math are the ones worth converting.
      </p>

      <div className="mb-8">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/43045972f_7Roger-TheMath-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-4 mb-4">
        <div className="text-white/40 text-xs mb-2">Built-in assumptions</div>
        <div className="grid grid-cols-2 gap-3 text-sm">
          <div className="text-white/60">Default close rate: <span className="text-white/90 font-medium">1-in-30 (3.3%)</span></div>
          <div className="text-white/60">Cost per lead: <span className="text-white/90 font-medium">~${Math.round(costPerLead)}</span></div>
          <div className="text-white/60">Cost per deal: <span className="text-white/90 font-medium">~${Math.round(costPerDeal).toLocaleString()}</span></div>
          <div className="text-white/60">Typical assignment: <span className="text-white/90 font-medium">$15–25K</span></div>
        </div>
      </div>

      <div className="grid sm:grid-cols-2 gap-6">
        {/* Inputs */}
        <div className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-6 flex flex-col gap-5">
          <Slider label="Leads per month" value={leads} field="leads" onChange={setLeads} min={10} max={200} step={1} format={(v) => v} />
          <Slider label="Close rate (%)" value={closeRate} field="closeRate" onChange={setCloseRate} min={1} max={20} step={0.5} format={(v) => `${v}% (1-in-${Math.round(100/v)})`} />
          <Slider label="Avg assignment ($)" value={assignment} field="assignment" onChange={setAssignment} min={2000} max={50000} step={1000} format={(v) => `$${v.toLocaleString()}`} />
        </div>

        {/* Output */}
        <div className="rounded-2xl bg-blue-600/[0.06] border border-blue-500/20 p-6 flex flex-col justify-between">
          <div>
            <div className="flex items-center gap-2 mb-6">
              <TrendingUp className="w-5 h-5 text-blue-400" />
              <span className="text-blue-400 font-semibold text-sm">Your projected return</span>
            </div>
            <div className="space-y-4">
              <div className="flex justify-between items-center">
                <span className="text-white/50 text-sm">Leads delivered</span>
                <span className="text-white font-semibold">{leads}/mo</span>
              </div>
              <div className="flex justify-between items-center">
                <span className="text-white/50 text-sm">Projected deals</span>
                <span className="text-white font-semibold">~{deals}/mo</span>
              </div>
              <div className="flex justify-between items-center">
                <span className="text-white/50 text-sm">Revenue per deal</span>
                <span className="text-white font-semibold">${assignment.toLocaleString()}</span>
              </div>
              <div className="flex justify-between items-center pt-3 border-t border-blue-500/20">
                <span className="text-white/50 text-sm">Monthly revenue</span>
                <span className="text-white font-bold text-lg">${revenue.toLocaleString()}</span>
              </div>
            </div>
          </div>
          <div className="mt-6 pt-4 border-t border-blue-500/20">
            <div className="flex justify-between items-end">
              <div>
                <div className="text-white/40 text-xs mb-1">Monthly spend</div>
                <div className="text-white/70 text-sm">${monthlySpend.toLocaleString()}</div>
              </div>
              <div className="text-right">
                <div className="text-white/40 text-xs mb-1">Return</div>
                <div className="text-3xl font-bold text-green-400">{(revenue / monthlySpend).toFixed(1)}x</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

src/components/saleshub2/ClientResultsSection.jsx

import React from "react";

export default function ClientResultsSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">7 · Client Results</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">Trust is earned, not fabricated.</h2>

      <div className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-6 sm:p-8 text-center">
        <div className="w-14 h-14 rounded-full bg-blue-600/10 flex items-center justify-center mx-auto mb-4">
          <span className="text-2xl">📊</span>
        </div>
        <p className="text-white/70 text-base font-medium mb-2">Client results coming as campaigns mature.</p>
        <p className="text-white/45 text-sm max-w-md mx-auto leading-relaxed">
          We don't ship fabricated testimonials. Real video testimonials will be added here as we collect them — each mapped to a named objection.
        </p>
        <p className="text-white/35 text-xs mt-4 max-w-sm mx-auto">
          In the meantime, Section 3 carries the proof load — 30 real seller conversations are stronger evidence than a quote.
        </p>
      </div>
    </div>
  );
}

src/components/saleshub2/PricingSection.jsx

import React, { useState } from "react";
import { Check, ArrowRight } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const TIERS = [
  { name: "Growth", monthly: 1995, bimonthly: 3995, quarterly: 5995, leadsMonthly: 36, leadsBi: 74, leadsQ: 115, crm: 99 },
  { name: "Pro", monthly: 2495, bimonthly: 4995, quarterly: 7495, leadsMonthly: 46, leadsBi: 96, leadsQ: 147, crm: 0 },
  { name: "Plus", monthly: 2997, bimonthly: 5997, quarterly: 8995, leadsMonthly: 58, leadsBi: 120, leadsQ: 180, crm: 0 },
];

const TERMS = [
  { key: "monthly", label: "Monthly" },
  { key: "bimonthly", label: "Bi-monthly" },
  { key: "quarterly", label: "Quarterly" },
];

// Verified live Stripe Payment Links for the direct-purchase path. The optional
// $497 trial remains a separate Base44 Payment Element flow below the plan cards.
//
// All nine links were replaced on 2026-09-09. The CRM Fee is now an explicit
// line item on every checkout: $99/month on Growth monthly, where it is a
// required part of the offer, and a $0 waived line on the other eight, so the
// buyer can see the fee is included rather than missing. Stripe cannot add line
// items to an existing Payment Link, so each one needed a new URL; the previous
// links are deactivated.
export const PAYMENT_LINKS_REFERENCE = {
  monthly: {
    Growth: "https://buy.stripe.com/6oU00j5aZ0VS2sn3Uc4ko2Y",
    Pro: "https://buy.stripe.com/6oUeVd5aZ9soaYT0I04ko30",
    Plus: "https://buy.stripe.com/8x2dR96f3eMId710I04ko31",
  },
  bimonthly: {
    Growth: "https://buy.stripe.com/00w28r9rf0VS7MH1M44ko32",
    Pro: "https://buy.stripe.com/bJe8wP32R9soeb51M44ko33",
    Plus: "https://buy.stripe.com/8x2eVdavjdIE3wr62k4ko34",
  },
  quarterly: {
    Growth: "https://buy.stripe.com/aFa8wP0UJeMI1oj2Q84ko35",
    Pro: "https://buy.stripe.com/14A14n7j7fQM8QL62k4ko36",
    Plus: "https://buy.stripe.com/5kQeVd8nbfQM8QLcqI4ko37",
  },
};

const BONUS = {
  bimonthly: { Growth: "+5", Pro: "+8", Plus: "+10" },
  quarterly: { Growth: "+10", Pro: "+14", Plus: "+18" },
};

const VALUE_GAIN = { Growth: "—", Pro: "+32% more value", Plus: "+65% more value" };

const ADDED_VALUE = {
  bimonthly: { Growth: "$750", Pro: "$900", Plus: "$1,000" },
  quarterly: { Growth: "$1,000", Pro: "$1,250", Plus: "$1,450" },
};

const TERM_BAR = {
  bimonthly: "Setup fee waived · Bonus leads on your first term · Better rate per lead, locked",
  quarterly: "Setup fee waived · Up to 18 bonus leads · Best rate per lead, locked · 147 leads is a real test, not a sample",
};

function paymentLinkUrl(baseUrl) {
  const params = new URLSearchParams(window.location.search);
  const clientReferenceId = params.get("cid") || params.get("contact_id");
  if (!clientReferenceId) return baseUrl;
  const url = new URL(baseUrl);
  url.searchParams.set("client_reference_id", clientReferenceId.slice(0, 200));
  return url.toString();
}

export default function PricingSection({ onBuy, standalone = false }) {
  const [term, setTerm] = useState("monthly");

  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">{standalone ? "Choose your plan" : "8 · Pricing"}</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">
        {standalone ? "Buy a plan directly." : "Transparent. No 'book a call to hear the price.'"}
      </h2>
      <p className="text-white/55 text-base mb-6 max-w-2xl">
        Choose a plan and billing term to purchase it now. All nine Growth, Pro, and Plus combinations are available directly.
      </p>

      {!standalone && (
        <div className="mb-6">
          <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/ab4f56e66_8Roger-Pricing-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
        </div>
      )}

      <div className="inline-flex bg-white/[0.04] rounded-xl p-1 gap-1 mb-6">
        {TERMS.map((t) => (
          <button key={t.key} onClick={() => setTerm(t.key)}
            className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${term === t.key ? "bg-blue-600 text-white" : "text-white/45 hover:text-white"}`}>
            {t.label}
          </button>
        ))}
      </div>

      {term !== "monthly" && (
        <div className="rounded-xl bg-green-600/[0.06] border border-green-500/20 p-3 mb-4">
          <p className="text-green-300 text-sm text-center font-medium">{TERM_BAR[term]}</p>
        </div>
      )}

      <div className="grid sm:grid-cols-3 gap-3 mb-6">
        {TIERS.map((tier) => {
          const price = tier[term];
          const leadCount = term === "monthly" ? tier.leadsMonthly : term === "bimonthly" ? tier.leadsBi : tier.leadsQ;
          const bonus = term !== "monthly" ? BONUS[term][tier.name] : null;
          const setup = term === "monthly" ? 497 : 0;
          return (
            <div key={tier.name} className={`rounded-2xl border p-5 flex flex-col ${tier.name === "Pro" ? "border-blue-500/40 bg-blue-600/[0.05]" : "border-white/[0.06] bg-white/[0.03]"}`}>
              {tier.name === "Pro" && (
                <div className="text-blue-400 text-[11px] font-medium mb-1 uppercase tracking-wide">Recommended</div>
              )}
              <div className="text-white font-bold text-lg mb-1">{tier.name}</div>
              <div className="text-white/40 text-xs mb-2">{leadCount} leads/term{bonus && <span className="text-green-400"> ({bonus} bonus)</span>}</div>
              <div className="text-white/40 text-xs mb-4">vs Growth on same term: <span className={tier.name === "Growth" ? "text-white/30" : "text-green-400 font-medium"}>{VALUE_GAIN[tier.name]}</span></div>
              <div className="mb-1">
                <span className="text-2xl font-bold text-white">${price.toLocaleString()}</span>
                <span className="text-white/40 text-xs">{term === "monthly" ? "/mo" : term === "bimonthly" ? "/2-mo" : "/3-mo"}</span>
              </div>
              <div className="mb-3">
                {setup > 0 ? (
                  <div className="text-amber-400/80 text-xs">${setup} one-time setup fee</div>
                ) : (
                  <div className="text-green-400 text-xs">Setup waived</div>
                )}
                {term === "monthly" && tier.crm > 0 ? (
                  <div className="text-white/45 text-xs">+ ${tier.crm}/mo CRM, billed with the plan</div>
                ) : (
                  <div className="text-green-400 text-xs">CRM fee waived</div>
                )}
                {term !== "monthly" && (
                  <div className="text-green-400 text-xs font-medium">Added value: {ADDED_VALUE[term][tier.name]}</div>
                )}
              </div>
              <ul className="flex flex-col gap-2 flex-1 mb-5 mt-2">
                <li className="flex items-start gap-2 text-xs text-white/55">
                  <Check className="w-3.5 h-3.5 text-green-400 shrink-0 mt-0.5" />
                  Full SMS conversation context
                </li>
                <li className="flex items-start gap-2 text-xs text-white/55">
                  <Check className="w-3.5 h-3.5 text-green-400 shrink-0 mt-0.5" />
                  Invalid-lead crediting
                </li>
                <li className="flex items-start gap-2 text-xs text-white/55">
                  <Check className="w-3.5 h-3.5 text-green-400 shrink-0 mt-0.5" />
                  {term === "monthly" && tier.crm > 0
                    ? `CRM: $${tier.crm}/mo — billed with the plan`
                    : "CRM included — fee waived"}
                </li>
                <li className="flex items-start gap-2 text-xs text-white/55">
                  <Check className="w-3.5 h-3.5 text-green-400 shrink-0 mt-0.5" />
                  Exclusive — never resold
                </li>
              </ul>
              <a
                href={paymentLinkUrl(PAYMENT_LINKS_REFERENCE[term][tier.name])}
                className={`w-full py-2.5 rounded-xl text-sm font-semibold transition-colors flex items-center justify-center gap-2 ${
                  tier.name === "Pro" ? "bg-blue-600 hover:bg-blue-500 text-white" : "border border-white/15 hover:bg-white/5 text-white"
                }`}
              >
                Buy {tier.name} · ${price.toLocaleString()} <ArrowRight className="w-3.5 h-3.5" />
              </a>
            </div>
          );
        })}
      </div>

      {term !== "monthly" && (
        <p className="text-white/40 text-xs text-center mb-4">Added value = bonus leads at the standard $55 rate, plus the waived setup fee.</p>
      )}

      <div className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-4 text-center mb-4">
        <p className="text-white/50 text-sm">
          At 1-in-30, a single month is a small sample — {term === "bimonthly" ? "96" : term === "quarterly" ? "147" : "46"} leads is a real test.
        </p>
      </div>

      <div className="rounded-xl bg-blue-600/[0.05] border border-blue-500/15 p-4">
        <div className="text-white/50 text-sm leading-relaxed">
          Direct plan purchases charge the selected plan now. Monthly direct purchases also include the one-time $497 setup fee; bi-monthly and quarterly plans waive it. Every checkout shows the CRM fee as its own line: $99/month on Growth monthly, where it is required, and $0 on every other plan, where it is waived. Tax is calculated automatically and added on top.
        </div>
      </div>
    </div>
  );
}

src/components/saleshub2/GuaranteeSection.jsx

import React from "react";
import { ShieldCheck, RefreshCw, Ban, Clock, Wallet } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const TERMS = [
  { icon: ShieldCheck, title: "Lead count guaranteed", desc: "Shortfall made up the following term. If we still can't deliver, prorated refund." },
  { icon: Ban, title: "Invalid leads don't count", desc: "Fewer than three pillars, already listed, or gaming the bot — report in-app, credit returns to your balance on approval." },
  { icon: Clock, title: "Nothing expires", desc: "Undelivered credits roll over while the account is active. You never lose what you paid for." },
  { icon: RefreshCw, title: "Term to term", desc: "Cancel with 3 days' notice before your cycle. We buy data ahead, which is why the window exists." },
  { icon: Wallet, title: "Your balance still delivers", desc: "For 60 days after cancellation, nothing you paid for is lost. Leads keep coming." },
];

export default function GuaranteeSection({ onBuy }) {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">9 · Guarantee & Terms</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">The "what if it doesn't work" answer.</h2>
      <p className="text-white/55 text-base mb-8 max-w-lg">Your downside is capped by design.</p>

      <div className="mb-8">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/183ff512e_9Roger-Gauranteeandterms-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="flex flex-col gap-3 mb-6">
        {TERMS.map(({ icon: Icon, title, desc }) => (
          <div key={title} className="rounded-xl bg-white/[0.03] border border-white/[0.06] p-5 flex gap-4">
            <div className="shrink-0 w-9 h-9 rounded-lg bg-blue-600/15 flex items-center justify-center text-blue-400">
              <Icon className="w-4 h-4" />
            </div>
            <div>
              <h3 className="text-white font-semibold text-sm mb-1">{title}</h3>
              <p className="text-white/50 text-sm leading-relaxed">{desc}</p>
            </div>
          </div>
        ))}
      </div>

      <div className="rounded-xl bg-white/[0.02] border border-white/[0.06] p-4 text-center">
        <p className="text-white/35 text-xs">Auto-renews unless cancelled.</p>
      </div>
    </div>
  );
}

src/components/saleshub2/FounderSection.jsx

import React from "react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

const TEAM = [
  { n: "1", role: "Founder", line: "Guy Hakim. Investor and wholesaler." },
  { n: "1", role: "CTO", line: "Platform architecture and AI systems." },
  { n: "4", role: "Campaign Managers", line: "Running client accounts day to day." },
  { n: "2", role: "Developers", line: "Building and maintaining the platform." },
];

export default function FounderSection() {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">10 · Who's Behind It</div>

      <div className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-6 sm:p-8">
        <div className="flex items-center gap-4 mb-6">
          <div className="w-14 h-14 rounded-full bg-gradient-to-br from-blue-500 to-blue-700 flex items-center justify-center text-white text-xl font-bold shrink-0">
            GH
          </div>
          <div>
            <h2 className="text-xl font-bold text-white">Guy Hakim</h2>
            <p className="text-white/45 text-sm">Real estate investor & wholesaler</p>
          </div>
        </div>

        <p className="text-white/65 text-sm leading-relaxed mb-5">
          Started in his mother's garage in Israel. Six years in lead generation. $10M+ in transactions, 100+ contracts signed, 5,000+ seller leads worked by hand.
        </p>

        <blockquote className="border-l-2 border-blue-500/40 pl-4 mb-6">
          <p className="text-white/75 text-sm italic leading-relaxed">
            "Call centers burned out every three months. Paid leads cost $300 and got sold to four buyers at once. So we built something that didn't do that, and ran it on our own deals before selling it to anyone."
          </p>
        </blockquote>

        <div className="mb-6">
          <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/6aee9c5d0_10Roger-Who_sBehindit-095.mp3" speeds={[1, 1.1, 1.2, 1.3]} />
        </div>

        <div className="pt-5 border-t border-white/[0.06]">
          <div className="text-white font-semibold text-base mb-4">Nine people behind your campaign</div>
          <div className="grid sm:grid-cols-2 gap-3 mb-5">
            {TEAM.map((m) => (
              <div key={m.role} className="rounded-2xl bg-white/[0.03] border border-white/[0.06] p-5">
                <div className="text-3xl font-bold text-blue-400 mb-1.5">{m.n}</div>
                <div className="text-white font-semibold text-base mb-1">{m.role}</div>
                <div className="text-white/55 text-sm leading-relaxed">{m.line}</div>
              </div>
            ))}
          </div>
          <p className="text-white/45 text-sm">Miami-based. Operating in markets nationwide.</p>
        </div>
      </div>
    </div>
  );
}

src/components/saleshub2/ReferralSection.jsx

import React from "react";
import { Users, ArrowRight } from "lucide-react";
import SectionAudio from "@/components/saleshub2/SectionAudio";

export default function ReferralSection({ onBook }) {
  return (
    <div>
      <div className="text-blue-400 text-xs font-medium uppercase tracking-[0.15em] mb-4">11 · Referral</div>
      <h2 className="text-2xl sm:text-3xl font-bold text-white mb-4 leading-tight">Know other operators?</h2>
      <p className="text-white/55 text-base mb-8 max-w-lg leading-relaxed">
        Refer other wholesalers and investors. When they start, you earn. It's systematized — this is one of our best client sources.
      </p>

      <div className="max-w-lg mb-8">
        <SectionAudio src="https://media.base44.com/files/public/68cda4fea6b13489887bfb79/040847b92_11Roger-Referral-095.mp3" label="Referral program" speeds={[1, 1.1, 1.2, 1.3]} />
      </div>

      <div className="rounded-2xl bg-blue-600/[0.05] border border-blue-500/15 p-6">
        <div className="flex items-center gap-3 mb-4">
          <div className="w-10 h-10 rounded-xl bg-blue-600/15 flex items-center justify-center text-blue-400">
            <Users className="w-5 h-5" />
          </div>
          <div className="text-white font-semibold">How it works</div>
        </div>
        <div className="flex flex-col gap-3 text-sm text-white/60">
          <div className="flex gap-3">
            <span className="text-blue-400 font-semibold">1.</span>
            <span>Send another operator our way — wholesalers, investors, or agents with investor clients.</span>
          </div>
          <div className="flex gap-3">
            <span className="text-blue-400 font-semibold">2.</span>
            <span>If they sign on, you earn. Details covered on your call.</span>
          </div>
          <div className="flex gap-3">
            <span className="text-blue-400 font-semibold">3.</span>
            <span>This is how many of our best clients arrive — referrals from operators who've seen it work.</span>
          </div>
        </div>
      </div>

      <button onClick={onBook} className="mt-6 flex items-center gap-2 px-5 py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-xl text-sm transition-colors">
        Ask about the referral program <ArrowRight className="w-4 h-4" />
      </button>
    </div>
  );
}

src/components/saleshub2/QABot.jsx

import React, { useState, useRef, useEffect } from "react";
import { MessageCircle, X, Send, Loader2 } from "lucide-react";
import { base44 } from "@/api/base44Client";

const SYSTEM_PROMPT = `You are the assistant on the Leads4Deals sales hub. Visitors are real estate wholesalers, investors, and agents who arrived from an AI SMS conversation and are deciding whether to buy seller leads.

Your job is to answer their questions accurately and help them choose among two clear paths: buy a published plan directly or book a call.

## THE CORE RULE

**Answer only from the knowledge base provided.** If the answer isn't in it, say so and offer to book a call. Never fill a gap with a plausible-sounding answer, an industry average, or a general-knowledge inference.

This matters more here than in most products. Visitors have been burned by lead vendors before — several have lost five figures. An answer you invent becomes a promise the business has to honour, and the first real conversation will contradict it.

If you are uncertain whether something is in the knowledge base, treat it as not in the knowledge base.

## HARD BOUNDARIES

Never answer, always route to a call:

- **Compliance, TCPA, DNC, consent, opt-out records, sender of record.** Say: "Compliance questions get a direct answer from Guy rather than from me — let me get you on his calendar." Do not improvise even a partial answer.
- Legal, contractual, or tax questions
- Custom pricing, custom volume, or anything outside the published tiers
- Commercial-only or unusual property types
- Anything about a specific client's account or results
- Any statistic not in the knowledge base — client counts, total volume delivered, deliverability rates, show rates as percentages

Never state "100+ clients" or "50M+ closed." Never name a client or repeat a testimonial.

## TONE

Direct and plain. These are busy operators, not consumers. Short answers. No exclamation marks, no marketing language, no "great question."

Be honest about limits. The strongest thing about this offer is that it doesn't overclaim, and you should sound like that. If someone asks whether every lead is a ready-to-sign distressed seller, the answer is no — say so.

Never oversell. Your ceiling is what the page states. If you promise more than the page does, the first call undoes it.

## HANDLING THE FIVE OBJECTIONS THAT MATTER

These are the ones that stop deals. Answer, then move them to a section or an exit.

"Will the leads actually be qualified?" — Don't argue about definitions. Point them to the real conversation examples and invite them to read and judge for themselves. Offer to jump them there.

"I've been burned before." — Acknowledge it without inheriting it — the companies that disappointed them aren't this one. Ask what specifically they'd need to see. Route to the conversation examples.

"Paying upfront with no proof." — Point them to the real conversation examples on the page, which carry more weight than any claim, and offer to jump them there. Every other lead vendor says its leads are qualified — the difference here is the full unedited thread is visible.

"Too many leads to work." — Volume is adjustable. If they mention a team or an acquisitions manager, offer a call.

"1 in 30 is a lot of leads per deal." — Route to the ROI calculator and invite them to run it at a worse ratio.

## NAVIGATION

You can move visitors to any section. Do it when it answers their question better than text — especially the conversation examples, which carry more weight than anything you can say.

Offer, don't hijack: "Want me to jump you to the real conversation examples?"

## THE TWO EXITS

**Buy a plan directly** — for anyone ready to purchase Growth, Pro or Plus now. Send them to /plans, where all nine plan and term combinations are available.

**Book a call** — for anyone with questions you can't answer, compliance concerns, a team, custom needs, or who simply wants to talk to someone.

Don't push any path. Mention the relevant options and let the visitor choose.

## WHAT NOT TO DO

- Don't invent numbers, percentages, or timeframes
- Don't guess at compliance
- Don't claim results for clients
- Don't compare to named competitors
- Don't create urgency or scarcity
- Don't ask for contact details — the exits handle that
- Don't apologize repeatedly or pad answers with filler

---

# KNOWLEDGE BASE

Everything the assistant is permitted to say. If it isn't here, it routes to a call.

## THE COMPANY

Leads4Deals generates motivated seller leads for real estate wholesalers, investors, and agents working direct-to-seller.

Founded by Guy Hakim, a real estate investor and wholesaler. He started his wholesaling business out of his mother's garage in Israel and has been in lead generation for six years. He has closed over $10M in transactions, signed more than 100 contracts, and personally worked more than 5,000 seller leads.

The company was built out of his own frustration: call centers that burned out every three months, paid leads at $300 apiece sold to four buyers at once, and inconsistent lead flow. He ran his own call centers for three to four years, moved to texting, and built the AI system two years ago.

Team of nine — Guy, a CTO, four campaign managers running client accounts, and two developers. Miami-based, operating in markets nationwide.

## WHAT THE PRODUCT IS

Done-for-you seller lead generation. Four steps:

1. Build the list. Records are pulled for the client's counties and buying criteria, filtered for motivation signals: pre-foreclosure, tax delinquent, probate, divorce, tired landlord, absentee owner, liens, high equity, 65+, long-time owner. Off-market only — nothing already listed with an agent.
2. Start conversations. AI SMS outreach, running 24/7 at scale across thousands of homeowners.
3. Qualify. Every seller who shows interest is asked the same four questions.
4. Deliver. The lead lands in the client's CRM with the complete conversation attached. Sellers are offered the chance to book directly into the client's calendar.

## THE FOUR PILLARS

Every lead answers these before reaching a client:

1. Why are you selling? — the reason
2. How fast do you need to move? — days, weeks, or months
3. What are you asking? — price expectation, and whether price or speed matters more
4. What condition is the property in? — teardown, needs work, or move-in ready

A lead needs at least three of the four to reach the client. Fewer than three and it isn't delivered and doesn't count against the client's balance.

## WHAT THE PRODUCT IS NOT

State these plainly if asked. Never soften them.

- Not every lead is a distressed seller ready to sign. Some are curious, some are three months out, some go nowhere.
- The system does not extract someone's real underlying motivation over text. It captures the stated reason. The real reason comes out on the phone — that's a sales conversation the client has.
- Closing is not included. The client closes.
- Comps, ARV, and underwriting are not included.
- Dispo and buyer-side work are not included.
- Anyone claiming 30 out of 30 leads will be deals is promising something Leads4Deals does not promise.

## LEAD QUALITY — THE HONEST PICTURE

Leads fall into three groups, and every batch contains a mix:

Under pressure. A life event is forcing the sale — illness, death in the family, divorce, eviction in progress, a roof that caved in, medical bills. Highest likelihood of moving fast and being flexible on price.

Real reason, workable number. No crisis, but they've named a price that pencils or a deadline with a date on it. Ready to transact, but the deal needs working.

Worth the follow-up. Real sellers with genuine reasons, but a longer timeline or a price gap that needs closing. These close in 90 days, not this week.

Clients should call all leads the same day. The groups indicate likelihood, not order.

Real anonymized conversation examples for all three groups are on the page — direct people there rather than describing them.

## CLOSE RATE AND ROI

1 in 30. Thirty qualified leads to one signed contract. This is an average across markets and operators. Some do better, some worse, and the ones who do better are those who work their follow-up and are stronger on the phone.

Never quote a different ratio.

The math:
- 30 leads to a contract
- ~$55 per lead
- ~$1,650 per closed deal
- $15,000–$25,000 typical assignment or commission
- 9–15x return on marketing spend

This assumes 1-in-30, ~$55 per lead, and that the client works every lead. Any of those changing moves the number. Direct people to the ROI calculator to run their own figures.

## PRICING

| | Growth | Pro | Plus |
|---|---|---|---|
| Monthly | 36 leads · $1,995 | 46 leads · $2,495 | 58 leads · $2,997 |
| Bi-monthly | 74 leads · $3,995 | 96 leads · $4,995 | 120 leads · $5,997 |
| Quarterly | 115 leads · $5,995 | 147 leads · $7,495 | 180 leads · $8,995 |

All nine Growth, Pro, and Plus offers are directly purchasable. Bi-monthly and quarterly plans bill and auto-renew for the full selected term.

- Setup fee: $497 on monthly plans only. Waived on bi-monthly and quarterly.
- CRM: included with every plan. Billed as a required $99/month fee on Growth monthly only; on every other plan and term the fee is waived and shows as a $0 line on the invoice.
- Bonus leads on first term: +5 / +8 / +10 on bi-monthly · +10 / +14 / +18 on quarterly.

Why longer terms are recommended: at 1-in-30, a single month of 46 leads is about 1.5 expected deals — a small sample. A slow month can look like failure when it's just variance. 96 or 147 leads is a real test.

Customers buy any of the nine published plan/term combinations directly. Direct plan purchases charge the selected plan at checkout; monthly direct purchases also include the one-time $497 setup fee (that $497 is a setup fee on monthly plans, not a trial), while bi-monthly and quarterly plans waive setup.

If someone asks about a trial, paid trial, or 10-lead test: there is no trial on offer. Say plainly that there is no trial — customers buy a plan directly or book a call. Do not mention, describe, or hint at a $497 trial, 14-day test, or auto-converting trial under any circumstance.

## WHAT'S INCLUDED

- CRM — pipeline management, click-to-call, click-to-text (included on every plan; the $99/mo fee applies to Growth monthly only and is waived elsewhere)
- Full conversation history on every lead
- Appointments booked and confirmed automatically
- Seamless communication — the client texts and calls from the same number the AI used, so the seller isn't handed to a stranger
- Exclusive leads — never shared, never resold
- Support via chat group, plus one-on-one calls when needed
- Data export at any time, or integration with the client's own CRM

## GUARANTEE

Lead count guaranteed. If the full count isn't delivered within the term, it's made up the following term. If it still can't be delivered, the client receives a prorated refund for the undelivered leads.

Invalid leads don't count. A lead is invalid if fewer than three pillars were answered, the property is already listed, or the seller was gaming the bot. The client reports it in the app, it's reviewed, and the credit returns to their balance.

Nothing expires. Undelivered leads roll over. The balance stays with the client while the account is active.

## TERMS

- Billed upfront on the cycle date. Auto-renews unless cancelled.
- Term to term — the client is committed only to the term purchased.
- Cancel with 3 days' notice before the cycle date. No further billing, no questions asked.
- The 3-day window exists because data and infrastructure are purchased ahead of the campaign.
- After cancellation, the remaining balance still delivers over the following period. Nothing paid for is lost.
- Data belongs to the client. Export at any time.
- Markets are client-defined by county.

## TIMELINE FROM SIGNUP

- Day 0 — secure checkout, onboarding form, and agreement completed.
- 48 hours — account activation and verification
- 48 hours after that — campaign live, messages going out
- Days 5–7 — first leads in the pipeline
- Ongoing — delivered daily, spread across the term

## WHO IT'S FOR

Wholesalers, fix-and-flip investors, agents working direct-to-seller, and acquisitions teams — anyone who wants off-market seller conversations and can close them.

Not a fit for:
- People looking for finished deals rather than leads
- Anyone who won't work the follow-up
- Commercial-only requirements (quotable, but treat as a custom conversation — route to a call)

## COMMON QUESTIONS

"Will these leads actually be qualified?" — Don't argue definitions. Point to the real conversation examples on the page and invite them to read and judge for themselves. Every other lead gen company says its leads are qualified — the difference here is the full unedited thread is visible.

"I've been burned by lead vendors before." — Acknowledge it without inheriting it. Every company that disappointed them isn't this one. Ask what specifically they'd need to see, then point to the conversation examples.

"Is there a trial?" — There is no trial and no free trial. Customers buy a full plan directly, or book a call if they want to talk it through first. Cancel at least three days before renewal by emailing billing@leads4deals.io.

"Won't I get too many leads to work?" — Volume is adjustable. Clients on smaller plans or with capacity concerns can have the pace slowed. If they have a team, route to a call.

"1 in 30 is a lot of leads per deal." — Point to the ROI calculator and invite them to run it at a worse ratio. At $55 a lead against a $15–25K assignment, the math holds well below 1-in-30.

"I already have a CRM." — CRM comes with every plan; only Growth monthly pays the $99/month fee, and it is waived on every other plan and term. Leads can also be exported to your own CRM at any time. The conversation history lives in the Leads4Deals system, which is why most clients use it.

"Do the appointments actually show?" — Appointments are confirmed automatically before they happen. Show rates vary by market and operator.

"Are the leads shared or resold?" — Never. Every lead is exclusive to the client.

"How much of my time does this take?" — Setup is one conversation to define counties and buying criteria. After that, leads arrive daily and the client works them.

## DO NOT ANSWER — ROUTE TO A CALL

Compliance, TCPA, DNC, consent, opt-out records, sender of record. Say: "Compliance questions get a direct answer from Guy rather than from me — let me get you on his calendar." Never improvise a compliance answer.

Anything requiring numbers not in this document — client counts, total volume, market-specific performance, deliverability rates, show rates as a percentage.

Commercial-only or unusual property types.

Custom pricing, custom volume, or anything outside the published tiers.

Legal, contractual, or tax questions of any kind.

Anything about a specific client's account or results.

## CLAIMS NOT YET VERIFIED — NEVER USE

- "100+ clients"
- "$50M+ in closed deals"
- Any specific client success story or testimonial
- Any named client`;

 export default function QABot({ onBook, onBuy, onNav, onBotQuestion }) {
  const [open, setOpen] = useState(false);
  const [messages, setMessages] = useState([
    { role: "assistant", text: "Ask me anything about leads, pricing, the process, or guarantees. If I can't answer, I'll point you to the right section or get you to Guy." }
  ]);
  const [input, setInput] = useState("");
  const [loading, setLoading] = useState(false);
  const bottomRef = useRef(null);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  const getNudge = (q) => {
    if (/tcpa|dnc|compliance|consent|opt.?out|sender of record|legal|lawsuit|attorney|tax|contractual/i.test(q)) return { section: null, action: "book" };
    if (/qualif.*hand|what.*lead|real.*conversat|burned|shared|resold|exclusive/i.test(q)) return { section: "leads", label: "What a Lead Actually Is" };
    if (/price|cost|how much|tier|plan|setup|crm/i.test(q)) return { section: "pricing", label: "Pricing" };
    if (/how.*work|mechanism|scrape|sms|process/i.test(q)) return { section: "mechanism", label: "How It Works" };
    if (/roi|math|revenue|return|1-in|close rate/i.test(q)) return { section: "roi", label: "The Math" };
    if (/guarantee|refund|invalid|rollover|cancel|term/i.test(q)) return { section: "guarantee", label: "Guarantee" };
    if (/honest|don't promise|limit|downside/i.test(q)) return { section: "dont-promise", label: "What We Don't Promise" };
    if (/compare|vs|other|differentiat|why you/i.test(q)) return { section: "comparison", label: "Why Us" };
    if (/who|guy|founder|team|behind/i.test(q)) return { section: "founder", label: "Who's Behind It" };
    if (/book|call|talk|human|schedule/i.test(q)) return { section: null, action: "book" };
    return null;
  };

  const send = async () => {
    const q = input.trim();
    if (!q || loading) return;
    setInput("");
    setMessages((prev) => [...prev, { role: "user", text: q }]);
    onBotQuestion(q);
    setLoading(true);
    try {
      const history = messages.map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${m.text}`).join("\n");
      const result = await base44.integrations.Core.InvokeLLM({
        prompt: `${SYSTEM_PROMPT}\n\nConversation:\n${history}\n\nUser: ${q}\n\nAssistant:`,
      });
      const reply = typeof result === "string" ? result : result?.text || "I'd suggest booking a call with Guy for a direct answer.";
      const nudge = getNudge(q);
      setMessages((prev) => [...prev, { role: "assistant", text: reply, nudge }]);
    } catch {
      setMessages((prev) => [...prev, { role: "assistant", text: "Something glitched on my end. Want to book a call with Guy directly?" }]);
    }
    setLoading(false);
  };

  const handleNudge = (nudge) => {
    if (nudge.action === "book") onBook();
    else if (nudge.section) { onNav(nudge.section); setOpen(false); }
  };

  const nudgeLabel = (nudge) => {
    if (nudge.action === "book") return "→ Book a call";
    return `→ See: ${nudge.label}`;
  };

  return (
    <>
      <button onClick={() => setOpen(v => !v)}
        className="fixed bottom-20 right-4 z-50 w-12 h-12 rounded-full bg-blue-600 hover:bg-blue-500 flex items-center justify-center shadow-lg shadow-blue-900/40 transition-transform hover:scale-110">
        {open ? <X className="w-5 h-5 text-white" /> : <MessageCircle className="w-5 h-5 text-white" />}
      </button>

      {open && (
        <div className="fixed z-50 left-2 right-2 bottom-20 sm:left-auto sm:right-4 sm:w-96 bg-[#121a2b] border border-white/[0.08] rounded-2xl shadow-2xl flex flex-col overflow-hidden" style={{ maxHeight: "80vh" }}>
          <div className="px-4 py-3 border-b border-white/[0.06] flex items-center justify-between">
            <div>
              <div className="text-white text-sm font-semibold">Q&A Bot</div>
              <div className="text-white/35 text-xs">Ask anything — I'll point you to the right place</div>
            </div>
            <button onClick={() => setOpen(false)}><X className="w-4 h-4 text-white/35 hover:text-white" /></button>
          </div>

          <div className="flex-1 overflow-y-auto p-4 flex flex-col gap-3">
            {messages.map((m, i) => (
              <div key={i} className={`flex ${m.role === "user" ? "justify-end" : "justify-start"}`}>
                <div className={`max-w-[85%] ${m.role === "user" ? "bg-blue-600 text-white" : "bg-white/[0.05] text-white/85"} px-3.5 py-2.5 rounded-xl text-sm leading-relaxed`}>
                  {m.text}
                  {m.nudge && (
                    <button onClick={() => handleNudge(m.nudge)} className="block mt-2 text-blue-400 text-xs hover:underline font-medium">
                      {nudgeLabel(m.nudge)}
                    </button>
                  )}
                </div>
              </div>
            ))}
            {loading && (
              <div className="flex justify-start">
                <div className="bg-white/[0.05] px-3 py-2.5 rounded-xl">
                  <Loader2 className="w-4 h-4 text-white/30 animate-spin" />
                </div>
              </div>
            )}
            <div ref={bottomRef} />
          </div>

          <div className="p-3 border-t border-white/[0.06] flex gap-2">
            <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()}
              placeholder="Ask a question..." className="flex-1 bg-white/[0.04] border border-white/[0.08] rounded-lg px-3 py-2 text-sm text-white placeholder-white/30 outline-none focus:border-blue-500/40" />
            <button onClick={send} disabled={!input.trim() || loading} className="p-2 bg-blue-600 hover:bg-blue-500 disabled:opacity-40 rounded-lg transition-colors">
              <Send className="w-4 h-4 text-white" />
            </button>
          </div>

          <div className="px-3 pb-3 grid grid-cols-2 gap-2">
            <a href="/plans" onClick={() => setOpen(false)} className="py-1.5 bg-blue-600 hover:bg-blue-500 text-white text-center text-xs font-medium rounded-lg transition-colors">Buy a Plan</a>
            <button onClick={() => { onBook(); setOpen(false); }} className="py-1.5 bg-blue-600/15 hover:bg-blue-600/25 text-blue-400 text-xs font-medium rounded-lg transition-colors">Book a Call</button>
          </div>
        </div>
      )}
    </>
  );
}

src/components/saleshub2/BookCallModal.jsx

import React, { useState } from "react";
import { X, ArrowRight, Phone } from "lucide-react";
import { fireDoor } from "@/lib/ghlTrack";
import BookFlow from "./BookFlow";

const ROLES = ["Wholesaler", "Fix & Flip Investor", "Buy & Hold Investor", "Real Estate Agent", "Other"];
const LEADS_NEEDED = ["Under 40", "40–60", "60+", "Not sure yet"];
const REVENUES = ["< $5k/mo", "$5-20k/mo", "$20-50k/mo", "$50k+/mo"];

const SESSION_WEBHOOK = "REPLACE_ME";

export default function BookCallModal({ onClose, onExit, onBuy, session }) {
  const [step, setStep] = useState(1); // 1 = more-info, 2 = choose, 3 = native book flow
  const [form, setForm] = useState({ role: "", leads_needed: "", revenue: "" });
  const [choice, setChoice] = useState(null); // "call" | "trial" | null — neither pre-selected

  // POST the session summary to a webhook on submit. A failure never blocks the flow.
  const submit = async () => {
    const summary = {
      role: form.role,
      leads_needed: form.leads_needed,
      revenue: form.revenue,
      sections: session.sections_viewed,
      threads_expanded: session.threads_expanded,
      bot_questions: session.bot_questions.map((b) => b.text),
      roi_inputs: session.roi_inputs,
    };
    try {
      if (SESSION_WEBHOOK && SESSION_WEBHOOK !== "REPLACE_ME") {
        await fetch(SESSION_WEBHOOK, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(summary),
        });
      }
    } catch {
      // a webhook failure must never block the booking flow
    }
    fireDoor("more_info", {
      role: form.role,
      leads_needed: form.leads_needed,
      monthly_revenue: form.revenue,
    });
    setStep(2);
  };

  const pickCall = () => {
    setChoice("call");
    onExit("chose_call");
    setStep(3);
  };

  const title = step === 3 ? "Book a Call with Guy" : step === 2 ? "Ready to book?" : "Book a Call with Guy";
  const sub = step === 3 ? "Pick a time, then a couple of details." : step === 2 ? "Pick a time that works." : "A few details first.";

  return (
    <div className="fixed inset-0 z-[60] flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm" onClick={onClose}>
      <div className="bg-[#121a2b] border border-white/[0.08] rounded-2xl w-full max-w-lg max-h-[90vh] overflow-y-auto" onClick={(e) => e.stopPropagation()}>
        <div className="flex items-center justify-between p-6 border-b border-white/[0.06]">
          <div>
            <h2 className="text-white font-bold text-lg">{title}</h2>
            <p className="text-white/40 text-sm">{sub}</p>
          </div>
          <button onClick={onClose}><X className="w-5 h-5 text-white/40 hover:text-white" /></button>
        </div>

        <div className="p-6">
          {step === 1 && (
            <div className="flex flex-col gap-5">
              <div>
                <label className="text-white/55 text-sm mb-2 block">Your role</label>
                <div className="flex flex-wrap gap-2">
                  {ROLES.map((r) => (
                    <button key={r} onClick={() => setForm({ ...form, role: r })}
                      className={`px-3 py-1.5 rounded-lg text-sm transition-all border ${form.role === r ? "bg-blue-600 border-blue-600 text-white" : "border-white/15 text-white/50 hover:text-white hover:border-white/30"}`}>
                      {r}
                    </button>
                  ))}
                </div>
              </div>
              <div>
                <label className="text-white/55 text-sm mb-2 block">Leads needed per month</label>
                <div className="flex flex-wrap gap-2">
                  {LEADS_NEEDED.map((v) => (
                    <button key={v} onClick={() => setForm({ ...form, leads_needed: v })}
                      className={`px-3 py-1.5 rounded-lg text-sm transition-all border ${form.leads_needed === v ? "bg-blue-600 border-blue-600 text-white" : "border-white/15 text-white/50 hover:text-white hover:border-white/30"}`}>
                      {v}
                    </button>
                  ))}
                </div>
              </div>
              <div>
                <label className="text-white/55 text-sm mb-2 block">Monthly revenue from real estate</label>
                <div className="flex flex-wrap gap-2">
                  {REVENUES.map((r) => (
                    <button key={r} onClick={() => setForm({ ...form, revenue: r })}
                      className={`px-3 py-1.5 rounded-lg text-sm transition-all border ${form.revenue === r ? "bg-blue-600 border-blue-600 text-white" : "border-white/15 text-white/50 hover:text-white hover:border-white/30"}`}>
                      {r}
                    </button>
                  ))}
                </div>
              </div>
              <button onClick={submit} disabled={!form.role}
                className="w-full py-3 bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white font-semibold rounded-xl transition-colors flex items-center justify-center gap-2">
                Continue <ArrowRight className="w-4 h-4" />
              </button>
            </div>
          )}

          {step === 2 && (
            <div>
              <button onClick={pickCall}
                className="w-full text-left rounded-2xl border p-5 flex flex-col transition-colors border-white/[0.08] bg-white/[0.03] hover:bg-white/[0.06] hover:border-blue-500/40">
                <Phone className="w-5 h-5 text-blue-400 mb-3" />
                <div className="text-white font-semibold text-base mb-1">Book a call</div>
                <div className="text-white/50 text-sm leading-relaxed">Talk it through with Guy. 15 minutes.</div>
              </button>
              <p className="text-white/35 text-xs text-center mt-4">Ready to buy a plan instead? <a href="/plans" className="text-blue-400 hover:underline">View plans</a></p>
            </div>
          )}

          {step === 3 && (
            <div className="mt-1">
              <BookFlow iframeHeight={460} onBooked={() => onExit("book", "booked")} />
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

src/components/saleshub2/BuyTrialModal.jsx

import React, { useEffect, useMemo, useState } from "react";
import { Elements, PaymentElement, useElements, useStripe } from "@stripe/react-stripe-js";
import { loadStripe } from "@stripe/stripe-js";
import { X, ArrowRight, Check, Loader2, ShieldCheck } from "lucide-react";
import { base44 } from "@/api/base44Client";
import { fireDoor, getCid } from "@/lib/ghlTrack";

const OFFER_CATALOG = {
  growth: {
    label: "Growth",
    monthly: { price: 1995, baseLeads: 36, bonusLeads: 0 },
    bimonthly: { price: 3995, baseLeads: 74, bonusLeads: 5 },
    quarterly: { price: 5995, baseLeads: 115, bonusLeads: 10 },
  },
  pro: {
    label: "Pro",
    monthly: { price: 2495, baseLeads: 46, bonusLeads: 0 },
    bimonthly: { price: 4995, baseLeads: 96, bonusLeads: 8 },
    quarterly: { price: 7495, baseLeads: 147, bonusLeads: 14 },
  },
  plus: {
    label: "Plus",
    monthly: { price: 2997, baseLeads: 58, bonusLeads: 0 },
    bimonthly: { price: 5997, baseLeads: 120, bonusLeads: 10 },
    quarterly: { price: 8995, baseLeads: 180, bonusLeads: 18 },
  },
};

const TERM_LABELS = { monthly: "Monthly", bimonthly: "Bi-monthly", quarterly: "Quarterly" };

function normalizeOffer(raw = {}) {
  const plan = OFFER_CATALOG[raw.plan] ? raw.plan : "pro";
  const term = TERM_LABELS[raw.term] ? raw.term : "monthly";
  // Since 2026-09-09 the CRM Fee is not a buyer choice: it is a required
  // $99/month line on Growth monthly and a $0 waived line on every other
  // plan/term. The backend derives the same rule and ignores what is sent, so
  // this is display state only.
  return { plan, term, crmEnabled: plan === "growth" && term === "monthly" };
}

function describeOffer(offer) {
  const plan = OFFER_CATALOG[offer.plan];
  const term = plan[offer.term];
  const firstPlanLeads = offer.term === "monthly"
    ? term.baseLeads - 10
    : term.baseLeads + term.bonusLeads;
  const cadence = offer.term === "monthly" ? "month" : offer.term === "bimonthly" ? "2 months" : "3 months";
  return {
    planLabel: plan.label,
    termLabel: TERM_LABELS[offer.term],
    price: term.price,
    baseLeads: term.baseLeads,
    bonusLeads: term.bonusLeads,
    firstPlanLeads,
    cadence,
    features: [
      "10 qualified leads after the $497 trial payment succeeds",
      "Card saved securely by Stripe for the selected plan on day 15",
      `$${term.price.toLocaleString()} every ${cadence} after the 14-day trial`,
      offer.term === "monthly"
        ? `${firstPlanLeads} additional leads after the first plan payment — ${term.baseLeads} total in cycle one`
        : `${firstPlanLeads} additional leads after the first plan payment, including ${term.bonusLeads} first-term bonus leads`,
      `${term.baseLeads} leads on every later paid renewal`,
    ],
  };
}

// Stripe Tax resolves the customer's jurisdiction from their billing address,
// and the $497 invoice is issued the moment the subscription is created — so
// the address has to be known before checkout starts, not at card confirmation.
const COUNTRIES = [
  { code: "US", label: "United States" },
  { code: "CA", label: "Canada" },
  { code: "GB", label: "United Kingdom" },
  { code: "AU", label: "Australia" },
  { code: "NZ", label: "New Zealand" },
  { code: "IE", label: "Ireland" },
];

const EMPTY_ADDRESS = { line1: "", city: "", state: "", postalCode: "", country: "US" };

function formatUsd(cents) {
  return `$${(Number(cents || 0) / 100).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
}

function createAttemptId() {
  if (globalThis.crypto?.randomUUID) return globalThis.crypto.randomUUID();
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (character) => {
    const random = Math.floor(Math.random() * 16);
    const value = character === "x" ? random : (random & 0x3) | 0x8;
    return value.toString(16);
  });
}

function PaymentForm({ checkoutAttemptId, email, amountDueCents, taxAmountCents, planDescription, onExit, onSuccess }) {
  const stripe = useStripe();
  const elements = useElements();
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState("");

  const submitPayment = async (event) => {
    event.preventDefault();
    if (!stripe || !elements || submitting) return;

    setSubmitting(true);
    setError("");
    onExit("payment_submitted");

    const returnUrl = new URL(window.location.href);
    returnUrl.searchParams.set("checkout", "complete");
    returnUrl.searchParams.set("attempt", checkoutAttemptId);

    const result = await stripe.confirmPayment({
      elements,
      confirmParams: {
        return_url: returnUrl.toString(),
        receipt_email: email,
      },
      redirect: "if_required",
    });

    if (result.error) {
      setError(result.error.message || "Payment could not be completed.");
      onExit("payment_failed");
      setSubmitting(false);
      return;
    }

    if (["succeeded", "processing"].includes(result.paymentIntent?.status)) {
      onExit("payment_succeeded");
      onSuccess(result.paymentIntent.status);
      return;
    }

    setError("Payment is not complete yet. Please try again or use another card.");
    setSubmitting(false);
  };

  return (
    <form onSubmit={submitPayment} className="space-y-4">
      <PaymentElement options={{ layout: "tabs" }} />
      {amountDueCents > 0 && (
        <div className="rounded-lg bg-white/[0.04] px-3 py-2 text-sm text-white/70 flex items-center justify-between">
          <span>Due today</span>
          <span className="text-white font-semibold">
            {formatUsd(amountDueCents)}
            {taxAmountCents > 0 && <span className="text-white/45 font-normal text-xs ml-1.5">incl. {formatUsd(taxAmountCents)} tax</span>}
          </span>
        </div>
      )}
      {error && <div className="rounded-lg border border-red-400/20 bg-red-500/10 px-3 py-2 text-sm text-red-200">{error}</div>}
      <button
        type="submit"
        disabled={!stripe || submitting}
        className="w-full py-3.5 bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white font-semibold rounded-xl transition-colors flex items-center justify-center gap-2 text-base"
      >
        {submitting ? <Loader2 className="w-4 h-4 animate-spin" /> : <ShieldCheck className="w-4 h-4" />}
        {submitting ? "Processing…" : `Pay ${amountDueCents > 0 ? formatUsd(amountDueCents) : "$497"} securely`}
      </button>
      <p className="text-white/40 text-xs text-center leading-relaxed">
        Stripe securely stores your card. {planDescription} starts after 14 days.
      </p>
    </form>
  );
}

function ReturnStatus({ onSuccess }) {
  const stripe = useStripe();
  const [message, setMessage] = useState("Confirming your payment…");

  useEffect(() => {
    const clientSecret = new URLSearchParams(window.location.search).get("payment_intent_client_secret");
    if (!stripe || !clientSecret) return;

    stripe.retrievePaymentIntent(clientSecret).then(({ paymentIntent, error }) => {
      if (error) {
        setMessage(error.message || "We could not confirm the payment status.");
        return;
      }
      if (["succeeded", "processing"].includes(paymentIntent?.status)) {
        onSuccess(paymentIntent.status);
        return;
      }
      setMessage("Payment is not complete. Please reopen checkout and try again.");
    });
  }, [stripe, onSuccess]);

  return (
    <div className="flex items-center justify-center gap-2 rounded-xl bg-white/[0.04] p-5 text-sm text-white/70">
      <Loader2 className="w-4 h-4 animate-spin" /> {message}
    </div>
  );
}

export default function BuyTrialModal({ onClose, onExit, contactId, initialOffer, onOfferChange }) {
  const query = new URLSearchParams(window.location.search);
  const returnClientSecret = query.get("payment_intent_client_secret") || "";
  const storedPublishableKey = sessionStorage.getItem("l4d_stripe_publishable_key") || "";
  const [checkoutAttemptId] = useState(() => query.get("attempt") || sessionStorage.getItem("l4d_checkout_attempt_id") || createAttemptId());
  const [offer, setOffer] = useState(() => normalizeOffer(initialOffer));
  const summary = describeOffer(offer);
  const [customerName, setCustomerName] = useState("");
  const [email, setEmail] = useState(() => sessionStorage.getItem("l4d_checkout_email") || "");
  const [address, setAddress] = useState(EMPTY_ADDRESS);
  const [termsAccepted, setTermsAccepted] = useState(false);
  const [starting, setStarting] = useState(false);
  const [error, setError] = useState("");
  const [paymentStatus, setPaymentStatus] = useState("");
  const [checkoutConfig, setCheckoutConfig] = useState(() => (
    returnClientSecret && storedPublishableKey
      ? { clientSecret: returnClientSecret, publishableKey: storedPublishableKey }
      : null
  ));
  const clientId = checkoutConfig?.offer?.clientId || sessionStorage.getItem("l4d_client_id") || "";
  const onboardingUrl = `https://link.apisystem.tech/widget/survey/yLAaiXZ5vocRdOOwHTB4?client_id=${encodeURIComponent(clientId)}&checkout_attempt_id=${encodeURIComponent(checkoutAttemptId)}`;

  useEffect(() => {
    sessionStorage.setItem("l4d_checkout_attempt_id", checkoutAttemptId);
  }, [checkoutAttemptId]);

  useEffect(() => {
    const previousOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => {
      document.body.style.overflow = previousOverflow;
    };
  }, []);

  useEffect(() => {
    onOfferChange?.(offer);
  }, [offer, onOfferChange]);

  const stripePromise = useMemo(
    () => checkoutConfig?.publishableKey ? loadStripe(checkoutConfig.publishableKey) : null,
    [checkoutConfig?.publishableKey],
  );

  const startCheckout = async (event) => {
    event.preventDefault();
    if (starting) return;

    setStarting(true);
    setError("");
    onExit("checkout_started");
    fireDoor("trial");

    try {
      const response = await base44.functions.invoke("createTrialSubscription", {
        checkoutAttemptId,
        customerName,
        email,
        contactId,
        // The GHL contact id this page already tracks against. It is what joins
        // the purchase back to the qualification answers (role, deal volume,
        // monthly revenue, leads needed) that were written to GHL earlier in the
        // session — none of which the checkout form itself collects.
        ghlContactId: getCid(),
        billingAddress: address,
        plan: offer.plan,
        term: offer.term,
        crmEnabled: offer.crmEnabled,
      });
      const data = response?.data || response;
      if (!data?.clientSecret || !data?.publishableKey) throw new Error("Secure checkout is unavailable.");

      sessionStorage.setItem("l4d_stripe_publishable_key", data.publishableKey);
      sessionStorage.setItem("l4d_checkout_email", email);
      sessionStorage.setItem("l4d_client_id", data.offer?.clientId || "");
      sessionStorage.setItem("l4d_checkout_offer", JSON.stringify(data.offer || {}));
      setCheckoutConfig(data);
      onExit("payment_form_loaded");
    } catch (checkoutError) {
      setError(checkoutError?.message || "Secure checkout could not be started. Please try again.");
      onExit("checkout_failed");
    } finally {
      setStarting(false);
    }
  };

  const markSuccess = (status) => {
    setPaymentStatus(status);
    const cleanUrl = new URL(window.location.href);
    ["checkout", "attempt", "payment_intent", "payment_intent_client_secret", "redirect_status"].forEach((key) => cleanUrl.searchParams.delete(key));
    window.history.replaceState({}, document.title, cleanUrl.toString());
  };

  return (
    <div className="fixed inset-0 z-[60] p-4 bg-black/70 backdrop-blur-sm overflow-y-auto" onClick={onClose}>
      <div className="bg-[#121a2b] border border-white/[0.08] rounded-2xl w-full max-w-md mx-auto my-0" onClick={(event) => event.stopPropagation()}>
        <div className="flex items-center justify-between p-6 border-b border-white/[0.06]">
          <div>
            <h2 className="text-white font-bold text-lg">$497 trial · 10 leads</h2>
            <p className="text-white/40 text-sm">Then {summary.planLabel} · {summary.termLabel} · ${summary.price.toLocaleString()} after 14 days</p>
          </div>
          <button onClick={onClose} aria-label="Close checkout"><X className="w-5 h-5 text-white/40 hover:text-white" /></button>
        </div>

        <div className="p-6">
          {paymentStatus ? (
            <div className="text-center py-4">
              <div className="w-12 h-12 rounded-full bg-green-500/15 text-green-300 flex items-center justify-center mx-auto mb-4">
                <Check className="w-6 h-6" />
              </div>
              <h3 className="text-xl font-bold text-white mb-2">Payment received</h3>
              <p className="text-white/55 text-sm leading-relaxed">
                Your $497 setup payment is {paymentStatus === "processing" ? "processing" : "confirmed"}. The first 10-lead fulfillment is triggered only after Stripe reports the invoice as paid.
              </p>
              <div className="mt-5 grid gap-2">
                <a href={onboardingUrl} target="_blank" rel="noopener noreferrer" className="w-full py-3 rounded-xl bg-blue-600 hover:bg-blue-500 text-white font-medium">Enter counties & buying criteria</a>
                <a href="https://sendlink.co/documents/doc-form/68bb1b8f358a6947a1ffd140?locale=en_US" target="_blank" rel="noopener noreferrer" className="w-full py-3 rounded-xl border border-white/15 text-white font-medium hover:bg-white/5">Sign service agreement</a>
                <button onClick={onClose} className="w-full py-2 text-white/50 text-sm hover:text-white">Do this later</button>
              </div>
              <p className="mt-4 text-white/40 text-xs">Billing support and cancellation requests: <a className="text-blue-300" href="mailto:billing@leads4deals.io">billing@leads4deals.io</a></p>
            </div>
          ) : checkoutConfig && stripePromise ? (
            <Elements
              stripe={stripePromise}
              options={{
                clientSecret: checkoutConfig.clientSecret,
                appearance: {
                  theme: "night",
                  variables: { colorPrimary: "#2563eb", colorBackground: "#121a2b", borderRadius: "10px" },
                },
              }}
            >
              {returnClientSecret
                ? <ReturnStatus onSuccess={markSuccess} />
                : (
                  <PaymentForm
                    checkoutAttemptId={checkoutAttemptId}
                    email={email}
                    amountDueCents={checkoutConfig.amountDueCents}
                    taxAmountCents={checkoutConfig.taxAmountCents}
                    planDescription={`$${summary.price.toLocaleString()} ${summary.planLabel} ${summary.termLabel}`}
                    onExit={onExit}
                    onSuccess={markSuccess}
                  />
                )}
            </Elements>
          ) : (
            <>
              <div className="flex flex-col gap-2.5 mb-6">
                {summary.features.map((feature) => (
                  <div key={feature} className="flex items-start gap-2.5">
                    <Check className="w-4 h-4 text-green-400 shrink-0 mt-0.5" />
                    <span className="text-white/70 text-sm">{feature}</span>
                  </div>
                ))}
              </div>

              <form onSubmit={startCheckout} className="space-y-3">
                <div className="grid grid-cols-2 gap-2">
                  <label className="block">
                    <span className="text-white/55 text-xs">Plan</span>
                    <select
                      value={offer.plan}
                      onChange={(event) => setOffer((current) => normalizeOffer({ ...current, plan: event.target.value }))}
                      className="mt-1 w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white outline-none focus:border-blue-500/60"
                    >
                      {Object.entries(OFFER_CATALOG).map(([key, plan]) => (
                        <option key={key} value={key} className="bg-[#121a2b]">{plan.label}</option>
                      ))}
                    </select>
                  </label>
                  <label className="block">
                    <span className="text-white/55 text-xs">Billing term</span>
                    <select
                      value={offer.term}
                      onChange={(event) => setOffer((current) => normalizeOffer({ ...current, term: event.target.value }))}
                      className="mt-1 w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white outline-none focus:border-blue-500/60"
                    >
                      {Object.entries(TERM_LABELS).map(([key, label]) => (
                        <option key={key} value={key} className="bg-[#121a2b]">{label}</option>
                      ))}
                    </select>
                  </label>
                </div>
                <div className="rounded-xl border border-white/10 bg-white/[0.03] px-3 py-2.5 text-sm text-white/70">
                  {offer.crmEnabled
                    ? "CRM is included and billed at $99/month with the plan, starting on day 15."
                    : "CRM is included on this plan and the $99/month fee is waived."}
                </div>
                <div className="rounded-xl border border-blue-500/15 bg-blue-600/[0.05] px-3 py-2 text-xs text-white/55">
                  Due today: $497 plus tax · Day 15: ${summary.price.toLocaleString()}{offer.crmEnabled ? " + $99 CRM" : " (CRM fee waived)"} · First plan allocation: {summary.firstPlanLeads} new leads
                </div>
                <label className="flex items-start gap-2.5 rounded-xl border border-white/10 bg-white/[0.03] px-3 py-3 text-xs text-white/65 leading-relaxed">
                  <input
                    type="checkbox"
                    checked={termsAccepted}
                    onChange={(event) => setTermsAccepted(event.target.checked)}
                    required
                    className="mt-0.5 h-4 w-4 shrink-0 rounded border-white/20 bg-transparent accent-blue-600"
                  />
                  <span>
                    I agree to pay $497 plus tax today for 10 qualified leads over the 14-day trial and authorize the selected {summary.planLabel} plan to start automatically on day 15. I have read the <a href="/38472" target="_blank" rel="noopener noreferrer" className="text-blue-300 hover:text-blue-200 underline">billing and cancellation terms</a>.
                  </span>
                </label>
                <label className="block">
                  <span className="text-white/55 text-xs">Name</span>
                  <input
                    value={customerName}
                    onChange={(event) => setCustomerName(event.target.value)}
                    required
                    minLength={2}
                    autoComplete="name"
                    className="mt-1 w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white outline-none focus:border-blue-500/60"
                  />
                </label>
                <label className="block">
                  <span className="text-white/55 text-xs">Email for receipt</span>
                  <input
                    type="email"
                    value={email}
                    onChange={(event) => setEmail(event.target.value)}
                    required
                    autoComplete="email"
                    className="mt-1 w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white outline-none focus:border-blue-500/60"
                  />
                </label>

                <div className="pt-1">
                  <span className="text-white/55 text-xs">Billing address</span>
                  <div className="mt-1 space-y-2">
                    <input
                      value={address.line1}
                      onChange={(event) => setAddress((current) => ({ ...current, line1: event.target.value }))}
                      required
                      placeholder="Street address"
                      autoComplete="address-line1"
                      className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white placeholder-white/25 outline-none focus:border-blue-500/60"
                    />
                    <div className="grid grid-cols-2 gap-2">
                      <input
                        value={address.city}
                        onChange={(event) => setAddress((current) => ({ ...current, city: event.target.value }))}
                        required
                        placeholder="City"
                        autoComplete="address-level2"
                        className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white placeholder-white/25 outline-none focus:border-blue-500/60"
                      />
                      <input
                        value={address.state}
                        onChange={(event) => setAddress((current) => ({ ...current, state: event.target.value.toUpperCase().slice(0, 2) }))}
                        required={address.country === "US"}
                        placeholder={address.country === "US" ? "State (e.g. TX)" : "State / region"}
                        autoComplete="address-level1"
                        className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white placeholder-white/25 outline-none focus:border-blue-500/60"
                      />
                    </div>
                    <div className="grid grid-cols-2 gap-2">
                      <input
                        value={address.postalCode}
                        onChange={(event) => setAddress((current) => ({ ...current, postalCode: event.target.value }))}
                        required
                        placeholder={address.country === "US" ? "ZIP code" : "Postal code"}
                        autoComplete="postal-code"
                        className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white placeholder-white/25 outline-none focus:border-blue-500/60"
                      />
                      <select
                        value={address.country}
                        onChange={(event) => setAddress((current) => ({ ...current, country: event.target.value }))}
                        autoComplete="country"
                        className="w-full rounded-xl border border-white/10 bg-white/[0.04] px-3 py-2.5 text-white outline-none focus:border-blue-500/60"
                      >
                        {COUNTRIES.map((country) => (
                          <option key={country.code} value={country.code} className="bg-[#121a2b]">{country.label}</option>
                        ))}
                      </select>
                    </div>
                  </div>
                </div>
                {error && <div className="rounded-lg border border-red-400/20 bg-red-500/10 px-3 py-2 text-sm text-red-200">{error}</div>}
                <button
                  type="submit"
                  disabled={starting}
                  className="w-full py-3.5 bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white font-semibold rounded-xl transition-colors flex items-center justify-center gap-2 text-base"
                >
                  {starting ? <Loader2 className="w-4 h-4 animate-spin" /> : null}
                  {starting ? "Preparing secure checkout…" : <>Continue to payment <ArrowRight className="w-4 h-4" /></>}
                </button>
              </form>
              <p className="text-white/40 text-xs text-center mt-3 leading-relaxed">
                $497 is charged today for the 10-lead trial. The selected ${summary.price.toLocaleString()} {summary.planLabel} plan starts after 14 days.
                Prices are exclusive of tax, which Stripe calculates automatically from your billing address.
              </p>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

src/components/saleshub2/ExitBar.jsx

import { ArrowRight } from "lucide-react";

export default function ExitBar({ onBook, onBuy }) {
  return (
    <div className="fixed bottom-0 left-0 right-0 z-30 bg-[#0b1220]/95 backdrop-blur border-t border-white/[0.06] px-4 py-3 flex items-center justify-center gap-3 lg:pl-60">
      <span className="text-white/30 text-sm hidden sm:block">Ready to move?</span>
      <a href="/plans" className="flex items-center gap-2 px-5 py-2.5 border border-white/15 hover:bg-white/5 text-white/80 text-sm font-medium rounded-xl transition-colors">
        Buy a Plan
      </a>
      <button onClick={onBook} className="flex items-center gap-2 px-5 py-2.5 bg-blue-600 hover:bg-blue-500 text-white text-sm font-semibold rounded-xl transition-colors">
        Book a Call <ArrowRight className="w-3.5 h-3.5" />
      </button>
    </div>
  );
}

src/components/saleshub2/SalesHub2Nav.jsx

import React, { useState } from "react";
import { Menu, X } from "lucide-react";

export default function SalesHub2Nav({ sections, active, onNav, onBook, onBuy }) {
  const [open, setOpen] = useState(false);

  return (
    <>
      {/* Desktop sidebar */}
      <nav className="hidden lg:flex fixed left-0 top-0 h-full w-56 bg-[#0b1220] border-r border-white/[0.06] flex-col pt-8 pb-6 px-4 z-40">
        <div className="mb-8 px-2">
          <div className="text-lg font-bold tracking-tight">
            <span className="text-blue-400">Leads</span><span className="text-red-400">4</span><span className="text-blue-400">Deals</span>
          </div>
          <div className="text-[11px] text-white/30 mt-0.5 uppercase tracking-wider">Sales Hub</div>
        </div>
        <div className="flex flex-col gap-0.5 flex-1 overflow-y-auto">
          {sections.map(({ id, label }) => (
            <button
              key={id}
              onClick={() => onNav(id)}
              className={`text-left px-3 py-2 rounded-lg text-[13px] transition-all ${
                active === id ? "bg-blue-600/15 text-blue-400 font-medium" : "text-white/45 hover:text-white/80 hover:bg-white/[0.03]"
              }`}
            >
              {label}
            </button>
          ))}
        </div>
        <div className="flex flex-col gap-2 mt-4 px-1">
          <a href="/plans" className="w-full py-2.5 rounded-lg border border-white/15 hover:bg-white/5 text-white/80 text-sm font-medium transition-colors text-center">
            Buy a Plan
          </a>
          <button onClick={onBook} className="w-full py-2.5 rounded-lg bg-blue-600 hover:bg-blue-500 text-white text-sm font-semibold transition-colors">
            Book a Call
          </button>
        </div>
      </nav>

      {/* Mobile top bar */}
      <div className="lg:hidden fixed top-0 left-0 right-0 z-40 bg-[#0b1220]/95 backdrop-blur border-b border-white/[0.06] px-4 py-3 flex items-center justify-between">
        <div className="text-base font-bold">
          <span className="text-blue-400">Leads</span><span className="text-red-400">4</span><span className="text-blue-400">Deals</span>
        </div>
        <div className="flex items-center gap-2">
          <a href="/plans" className="px-3 py-1.5 border border-white/15 text-white/80 text-xs font-medium rounded-lg">Plans</a>
          <button onClick={onBook} className="px-3 py-1.5 bg-blue-600 text-white text-xs font-semibold rounded-lg">Book</button>
          <button onClick={() => setOpen(true)} className="p-1.5 text-white/50 hover:text-white">
            <Menu className="w-5 h-5" />
          </button>
        </div>
      </div>

      {/* Mobile drawer */}
      {open && (
        <div className="lg:hidden fixed inset-0 z-50 bg-black/70" onClick={() => setOpen(false)}>
          <div className="absolute left-0 top-0 bottom-0 w-64 bg-[#0b1220] p-5 flex flex-col" onClick={(e) => e.stopPropagation()}>
            <div className="flex items-center justify-between mb-6">
              <div className="text-base font-bold"><span className="text-blue-400">Leads</span><span className="text-red-400">4</span><span className="text-blue-400">Deals</span></div>
              <button onClick={() => setOpen(false)}><X className="w-5 h-5 text-white/40" /></button>
            </div>
            <div className="flex flex-col gap-0.5 flex-1 overflow-y-auto">
              {sections.map(({ id, label }) => (
                <button key={id} onClick={() => { onNav(id); setOpen(false); }}
                  className={`text-left px-3 py-2.5 rounded-lg text-sm ${active === id ? "bg-blue-600/15 text-blue-400 font-medium" : "text-white/50 hover:text-white"}`}>
                  {label}
                </button>
              ))}
            </div>
            <div className="flex flex-col gap-2 mt-4">
              <a href="/plans" onClick={() => setOpen(false)} className="w-full py-2.5 rounded-lg border border-white/15 text-white/80 text-sm font-medium text-center">Buy a Plan</a>
              <button onClick={() => { onBook(); setOpen(false); }} className="w-full py-2.5 rounded-lg bg-blue-600 text-white text-sm font-semibold">Book a Call</button>
            </div>
          </div>
        </div>
      )}

      <div className="hidden lg:block w-56 shrink-0" />
    </>
  );
}