// placespace.co/get-started — the paid-traffic landing page for the $99 app offer.
//
// SEPARATE FROM /for-business ON PURPOSE: that page pitches the whole platform
// to anyone who wanders in. This one has a single job — take an ad click for
// "$99 restaurant app" and turn it into a qualified lead — so it carries one
// offer, one price, and one form. Loud on purpose: food brands that convert
// are high-contrast and high-energy, not quiet and tasteful.
//
// ⚠️ PRICING — every number the public sees is in PRICING below, in one place.
//    `setup` (99, one-time) is confirmed. EVERYTHING ELSE IS A PLACEHOLDER and
//    must be confirmed before this page runs behind paid traffic.

const { useState: useStateF, useMemo: useMemoF } = React;

const PRICING = {
  setup: 99,            // ✅ confirmed: $99 one-time setup
  // Monthly depends on the publishing route — see ROUTES below.
  // inside $9.99 · standalone $49.99, both confirmed.
  monthlyNote: 'cancel anytime',
  upsells: [            // ⚠️ PLACEHOLDER prices — confirm each
    // 'ordering' and 'loyalty' were removed as add-ons — the single plan
    // includes both, so listing them here priced at $0 only made the package
    // look like it had holes in it. They are stated on the base card instead.
    // Hardware, so it is priced per table and the style is theirs to pick.
    // ⚠️ Unit prices are PLACEHOLDERS — set them against real supplier cost.
    { id: 'codes', name: 'Branded table codes & window stickers', per: 'once', hardware: true,
      blurb: 'Your code and branding on every table, plus a window sticker for the door. Stickers are free; the custom signs carry your logo and a photo of your food.',
      // Priced from real supplier listings (scraped 2026-08-19), doubled on a
      // PER-UNIT basis. Two of these are multi-packs, so doubling the listing
      // price would have been ~3x markup on the 3-pack rather than 2x.
      //   plaque  B0FC2BNJKP  $7.99 x1  -> $7.99/unit -> $16
      //   holder  B0C1NXG7BB  $6.99 x3  -> $2.33/unit -> $5
      //   metal   B0CN3BW6B3  $9.99 x2  -> $5.00/unit -> $10
      //   scanpay B0GVG5Y7JY  $7.99 x1  -> $7.99/unit -> $16
      styles: [
        { id: 'decal', name: 'Table stickers', unit: 0, img: null,
          note: 'Peel-and-stick vinyl for every table, plus a window sticker.' },
        { id: 'holder', name: 'Acrylic sign holder', unit: 5, img: 'holder',
          note: 'Clear 4×6 vertical stand. The everyday table tent.' },
        { id: 'metal', name: 'Clear sign, metal stand', unit: 10, img: 'metal',
          note: '4×6 acrylic on a weighted metal foot. Sturdier for busy tables.' },
        { id: 'plaque', name: 'Custom acrylic plaque', unit: 16, img: 'plaque',
          note: 'Your logo AND a photo of your food printed into the acrylic. One per table.' },
        { id: 'scanpay', name: 'Custom scan-to-pay sign', unit: 16, img: 'scanpay',
          note: 'Your photo, your menu code and your payment handles on one sign. One per table.' },
      ] },
    { id: 'revisions', name: 'Extra revision pack (3 more)', price: 149, per: 'once',
      blurb: 'Two rounds are already included. This adds three more for menus that change often.' },
    { id: 'priority', name: 'Priority launch (4–7 days)', price: 149, per: 'once',
      blurb: 'Jump the queue. Your app is live in four to seven days.' },
  ],
};

const F = {
  orange: '#FF5A1F', deep: '#B02E00', amber: '#FFC53D',
  ink: '#16130E', body: '#5A5348', line: '#EFEBE3',
  cream: '#FFF7F0', white: '#FFFFFF',
};

const DEMO_MENU_URL = 'https://placespace.co/menu/chili-bird-demoChiliBird01';
const CHECKOUT_URL = 'https://us-central1-placespace-d5241.cloudfunctions.net/createLaunchCheckout';

/**
 * VERTICALS — the same funnel, framed for one trade at a time.
 *
 * A restaurants-only campaign should land on restaurant words, not "business".
 * Drive it with ?v=restaurant (salon, retail, gym, bar...). No param means the
 * generic all-businesses page, so nothing breaks for existing links.
 *
 * `type` must match an <option> in the business-type select, or the record
 * lands with the wrong category and the dashboard shows the wrong tabs.
 */
const VERTICALS = {
  restaurant: {
    eyebrow: 'FOR RESTAURANTS & CAFÉS',
    noun: 'restaurant',
    h1: 'Your own restaurant app.',
    offerBar: 'Your own restaurant app',
    type: 'Café / Restaurant',
    namePlaceholder: 'Chili Bird',
    emailPlaceholder: 'you@restaurant.com',
    menuWord: 'menu',
  },
  bar: {
    eyebrow: 'FOR BARS & NIGHTLIFE',
    noun: 'bar',
    h1: 'Your own bar app.',
    offerBar: 'Your own bar app',
    type: 'Bar / Nightlife',
    namePlaceholder: 'Silk Road Bar',
    emailPlaceholder: 'you@yourbar.com',
    menuWord: 'drinks list',
  },
  salon: {
    eyebrow: 'FOR SALONS & SPAS',
    noun: 'salon',
    h1: 'Your own salon app.',
    offerBar: 'Your own salon app',
    type: 'Salon / Spa',
    namePlaceholder: 'Shear Bliss',
    emailPlaceholder: 'you@yoursalon.com',
    menuWord: 'service list',
  },
  retail: {
    eyebrow: 'FOR RETAIL STORES',
    noun: 'store',
    h1: 'Your own store app.',
    offerBar: 'Your own store app',
    type: 'Retail store',
    namePlaceholder: 'Palafox Supply',
    emailPlaceholder: 'you@yourstore.com',
    menuWord: 'catalog',
  },
  gym: {
    eyebrow: 'FOR GYMS & STUDIOS',
    noun: 'gym',
    h1: 'Your own gym app.',
    offerBar: 'Your own gym app',
    type: 'Fitness / Wellness',
    namePlaceholder: 'Iron & Oak',
    emailPlaceholder: 'you@yourgym.com',
    menuWord: 'class list',
  },
  business: {
    eyebrow: 'FOR LOCAL BUSINESSES',
    noun: 'business',
    h1: 'Your own business app.',
    offerBar: 'Your own business app',
    type: 'Café / Restaurant',
    namePlaceholder: 'Chili Bird',
    emailPlaceholder: 'you@yourbusiness.com',
    menuWord: 'menu',
  },
};
const VERTICAL = (() => {
  try {
    const v = new URLSearchParams(window.location.search).get('v');
    return VERTICALS[v] || VERTICALS.business;
  } catch (_) { return VERTICALS.business; }
})();

// LIVE — this page now takes real money through Stripe Checkout.
// Confirmed: $99 setup · free / $9.99 / $49.99 monthly tiers · free stickers ·
// stand hardware priced per table from supplier cost.
const PAYMENT_LIVE = true;

/** Bundled into the standalone plan — its $49.99/mo already covers these, so
 *  they must not be billed twice. Inside the PlaceSpace app they stay paid
 *  add-ons. Keep in sync with STANDALONE_INCLUDED in functions/index.js. */
// (ordering + loyalty are part of the single plan now, not add-ons, so there
// is no longer a set of upsells that the plan silently covers.)

/** Annual billing: pay for 10 months, get 12. Standard SaaS shape and easy to
 *  say out loud — "two months free" beats "17% off". Keep in sync with
 *  ANNUAL_MONTHS in functions/index.js. */
const ANNUAL_MONTHS = 10;

/**
 * How the restaurant gets published. This is a compliance decision, not just
 * a pricing one.
 *
 * Apple App Review Guideline 4.2.6: "Apps created from a commercialized
 * template or app generation service will be rejected unless they are
 * submitted directly by the provider of the app's content. These services
 * should not submit apps on behalf of their clients... Another acceptable
 * option for template providers is to create a single binary to host all
 * client content in an aggregated or 'picker' model, for example as a
 * restaurant finder app with separate customized entries or pages for each
 * client restaurant."
 *
 * So we cannot publish a client's standalone app under OUR developer account
 * — that is the exact practice 4.2.6 prohibits, and it risks the account.
 * Both routes below are compliant:
 *   own    — separate branded binary, submitted on the RESTAURANT's account
 *   inside — the aggregated model Apple names, which PlaceSpace already is
 */
// ONE tier. The free menu-link rung and the aggregated "inside our app" rung
// were removed deliberately: the paid ad sells a $99 app, and offering a free
// route alongside it gave people a reason not to buy the thing being
// advertised. Price is now the $99 launch package (PRICING.setup) with no
// extra up-front charge, plus the monthly. Upsells carry the rest.
const ROUTES = {
  own: {
    label: 'Your own standalone app',
    price: 0,          // no second up-front charge — the $99 launch package is the price
    monthly: 49.99,
    badge: 'Your name on the App Store',
    points: [
      'A separate app under your restaurant\'s name on both stores',
      'We build it and do the submission work for you',
      'Published on YOUR developer account — Apple requires the restaurant to submit',
      'Apple charges $99/year and Google $25 once; year one is covered here',
      'Ordering, card payments and loyalty rewards are all included — no add-on fees',
    ],
  },
};

function LandingFunnel({ width = 1280 }) {
  const isMobile = width < 720;
  const pad = isMobile ? 22 : 64;
  // Store submission starts ticked — an app that is never published is not a
  // product. It can still be removed by anyone who already has the accounts.
  const [picked, setPicked] = useStateF({});
  // Single tier now, so this is fixed rather than chosen. Kept as state so the
  // downstream ROUTES[route] lookups and the checkout payload stay unchanged.
  const [route] = useStateF('own');
  const [codeStyle, setCodeStyle] = useStateF('holder');
  const [checkoutOpen, setCheckoutOpen] = useStateF(false);
  const [billing, setBilling] = useStateF('monthly');
  const [tables, setTables] = useStateF(10);
  const WINDOW_DECAL = 0; // window sticker is free — see below
  // Contact comes FIRST. An ad click is a stranger; capturing name/email/
  // website/phone before the package builder means an abandoned funnel still
  // leaves a lead behind, instead of a bounce.
  // Prefilled from ?name=&email=&biz=&type=&city=&phone= so a signup started
  // on /for-business doesn't ask for the same details a second time.
  const _q = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
  const [lead, setLead] = useStateF({
    contactName: _q.get('name') || '',
    email: _q.get('email') || '',
    website: _q.get('website') || '',
    phone: _q.get('phone') || '',
    businessName: _q.get('biz') || '',
    businessType: _q.get('type') || VERTICAL.type,
    city: _q.get('city') || '',
    // Straight from Google Places via the for-business search.
    placeId: _q.get('placeId') || '',
    address: _q.get('address') || '',
    state: _q.get('state') || '',
  });
  const [leadDone, setLeadDone] = useStateF(false);
  const [leadBusy, setLeadBusy] = useStateF(false);
  const [leadErr, setLeadErr] = useStateF('');
  const setL = (k) => (e) => setLead({ ...lead, [k]: e.target.value });

  const submitLead = async (e) => {
    e.preventDefault();
    if (!lead.contactName || !lead.email) { setLeadErr('Name and email are required'); return; }
    setLeadBusy(true); setLeadErr('');
    try {
      // Fire into the same inbox the main form uses, so a half-finished funnel
      // is still followable.
      await fetch('https://us-central1-placespace-d5241.cloudfunctions.net/submitBusinessSignupRequest', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          businessName: lead.businessName || lead.contactName,
          businessType: 'Café / Restaurant',
          firstName: lead.contactName, lastName: '',
          email: lead.email, phone: lead.phone,
          notes: `Funnel step 1 (contact captured)\nWebsite: ${lead.website || '—'}`,
        }),
      });
    } catch (_) { /* never block the funnel on the lead ping */ }
    setLeadBusy(false); setLeadDone(true);
    // Tell Meta a lead landed, so the ad account has a real conversion to
    // optimise against. Never block the funnel if the pixel failed to load.
    try { if (window.psTrackLead) window.psTrackLead(); } catch (_) {}
    // The form becomes the package builder in place, so keep the section in
    // view rather than sending them down the page to a second block.
    const el = document.getElementById('start');
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
  };

  const toggle = (id) => setPicked((p) => ({ ...p, [id]: !p[id] }));
  const chosen = PRICING.upsells.filter((u) => picked[u.id]);
  const styleObj = PRICING.upsells.find((u) => u.hardware)?.styles.find((x) => x.id === codeStyle);
  const codesTotal = picked.codes && styleObj ? styleObj.unit * tables + WINDOW_DECAL : 0;
  // Declared BEFORE priceOf: dueNow calls priceOf during render, so a later
  // const put this in the temporal dead zone and crashed the page as soon as
  // a one-time add-on was ticked.
  const priceOf = (u) => (u.hardware ? codesTotal : u.price);
  const setupFee = ROUTES[route].setup != null ? ROUTES[route].setup : PRICING.setup;
  const oneTimes = setupFee + ROUTES[route].price + chosen.filter((u) => u.per === 'once').reduce((s, u) => s + priceOf(u), 0);
  // Both routes carry a monthly; the standalone one costs more because there
  // is a separate binary to host, update and keep in the stores.
  const baseMonthly = ROUTES[route].monthly;
  const perMonth = baseMonthly
    + chosen.filter((u) => u.per === 'mo').reduce((s, u) => s + u.price, 0);
  const annual = Math.round(perMonth * ANNUAL_MONTHS * 100) / 100;
  const annualSaving = Math.round(perMonth * (12 - ANNUAL_MONTHS) * 100) / 100;
  const isAnnual = billing === 'annual';
  // Nothing to charge = do not send anyone to Stripe. A $0 Checkout Session
  // is invalid, and asking for a card to hand over something free is worse.
  const isFree = oneTimes === 0 && perMonth === 0;
  // Annual bills the whole year on the first invoice, so it lands in due-today.
  const dueNow = Math.round((oneTimes + (isAnnual ? annual : 0)) * 100) / 100;
  const money = (n) => (Number.isInteger(n) ? `$${n}` : `$${n.toFixed(2)}`);

  /** Selections ride along in the lead's notes so sales sees intent. */
  const notesPrefill = useMemoF(() => {
    const lines = [`Plan: $${setupFee} setup + $${baseMonthly.toFixed(2)}/mo`, `Publishing: ${ROUTES[route].label} ($${ROUTES[route].price})`];
    if (picked.codes && styleObj) lines.push(`Hardware: ${styleObj.name} x${tables} tables + window decal ($${codesTotal})`);
    if (chosen.length) lines.push('Add-ons: ' + chosen.map((u) => `${u.name} ($${u.price}${u.per === 'mo' ? '/mo' : ' once'})`).join(', '));
    lines.push(`Due today: $${dueNow} · Monthly: $${perMonth}`);
    lines.push(`Billing: ${isAnnual ? `annual $${annual.toFixed(2)}/yr (saves $${annualSaving.toFixed(2)})` : `monthly $${perMonth.toFixed(2)}/mo after a free first month`}`);
    if (lead.website) lines.push(`Website: ${lead.website}`);
    return lines.join('\n');
  }, [picked, lead]);

  const fld = { width: '100%', padding: '13px 14px', fontSize: 15, borderRadius: 11,
    border: '1px solid #DED8CE', fontFamily: BRAND.font, boxSizing: 'border-box', outline: 'none' };
  const lbl = { fontSize: 12.5, fontWeight: 700, color: F.ink, display: 'block', marginBottom: 6 };
  const qtyBtn = { width: 30, height: 30, borderRadius: 999, border: `1px solid ${F.line}`,
    background: F.white, color: F.ink, fontSize: 16, fontWeight: 800, cursor: 'pointer',
    lineHeight: 1, padding: 0, fontFamily: BRAND.font };

  const H = ({ children, size }) => (
    <h2 style={{
      fontSize: size || (isMobile ? 32 : 46), lineHeight: 1.03, letterSpacing: -1.4,
      fontWeight: 800, color: F.ink, margin: '0 0 14px', textWrap: 'balance',
    }}>{children}</h2>
  );

  return (
    <div style={{ width: '100%', background: F.white, fontFamily: BRAND.font, color: F.ink }}>

      {/* Offer bar — the price never leaves the screen. */}
      <div style={{
        background: F.deep, color: F.white, padding: '10px 16px',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
        fontSize: isMobile ? 12.5 : 14, fontWeight: 700, textAlign: 'center',
      }}>
        <span style={{ background: F.amber, color: F.deep, padding: '3px 9px', borderRadius: 999, fontSize: 11, fontWeight: 800 }}>LIMITED</span>
        {VERTICAL.offerBar} for <strong>${PRICING.setup}</strong> to launch
      </div>

      {/* HERO */}
      <div style={{
        background: `linear-gradient(160deg, ${F.orange} 0%, ${F.deep} 120%)`,
        color: F.white, padding: `${isMobile ? 40 : 66}px ${pad}px ${isMobile ? 44 : 72}px`,
      }}>
        <div style={{
          maxWidth: 1180, margin: '0 auto', display: 'grid',
          gridTemplateColumns: isMobile ? '1fr' : '1.05fr 0.95fr',
          gap: isMobile ? 34 : 54, alignItems: 'center',
        }}>
          <div>
            <div style={{
              display: 'inline-block', background: 'rgba(255,255,255,0.16)',
              border: '1px solid rgba(255,255,255,0.3)', borderRadius: 999,
              padding: '6px 14px', fontSize: 12, fontWeight: 800, letterSpacing: 0.6, marginBottom: 18,
            }}>{VERTICAL.eyebrow}</div>
            <h1 style={{
              fontSize: isMobile ? 40 : 66, lineHeight: 0.98, letterSpacing: -2.2,
              fontWeight: 800, margin: '0 0 18px', textWrap: 'balance',
            }}>
              {VERTICAL.h1}<br />
              <span style={{ color: F.amber }}>${PRICING.setup} to launch.</span>
            </h1>
            <p style={{ fontSize: isMobile ? 17 : 20, lineHeight: 1.5, margin: '0 0 26px', color: 'rgba(255,255,255,0.94)', maxWidth: 520 }}>
              We design, build and publish a mobile app under <em>your</em> name — menus, ordering,
              and a scan-to-open menu your guests use without installing anything.
              ${PRICING.setup} one-time to launch, then ${ROUTES.own.monthly.toFixed(2)}/mo — first month free.
            </p>
            <a href="#start" style={{
              display: 'inline-flex', alignItems: 'center', gap: 10, background: F.white,
              color: F.deep, fontSize: 17, fontWeight: 800, padding: '16px 30px',
              borderRadius: 14, textDecoration: 'none',
              boxShadow: '0 14px 30px -12px rgba(0,0,0,0.45)',
            }}>Claim the ${PRICING.setup} launch →</a>
            <div style={{ marginTop: 16, fontSize: 13, color: 'rgba(255,255,255,0.85)' }}>
              First month free · {PRICING.monthlyNote} · Live in about 2 weeks
            </div>
          </div>

          {/* Scan panel — the demo IS the pitch. */}
          <div style={{
            background: F.white, borderRadius: 22, padding: isMobile ? 22 : 28,
            boxShadow: '0 30px 60px -22px rgba(0,0,0,0.45)', color: F.ink, textAlign: 'center',
          }}>
            <div style={{ fontSize: 13, fontWeight: 800, letterSpacing: 1, color: F.deep, marginBottom: 4 }}>SEE IT IN 3 SECONDS</div>
            <div style={{ fontSize: isMobile ? 19 : 21, fontWeight: 800, letterSpacing: -0.5, marginBottom: 14 }}>
              Point your camera here
            </div>
            <img src="/landings/assets/demo-qr.svg" alt="QR code that opens a live demo menu"
                 style={{ width: isMobile ? 190 : 214, height: isMobile ? 190 : 214, display: 'block', margin: '0 auto' }} />
            <div style={{ fontSize: 13, color: F.body, lineHeight: 1.55, marginTop: 14 }}>
              <strong style={{ color: F.ink }}>On your phone?</strong> Press and hold the code, then tap the link.<br />
              <strong style={{ color: F.ink }}>On a computer?</strong> Point your phone camera at the screen.
            </div>
            <div style={{
              marginTop: 14, paddingTop: 14, borderTop: `1px solid ${F.line}`,
              fontSize: 12.5, color: F.body,
            }}>
              A real menu for <strong style={{ color: F.ink }}>Chili Bird</strong>, our demo restaurant.
              It opens instantly — nothing to install.
            </div>
            <a href={DEMO_MENU_URL} style={{ display: 'inline-block', marginTop: 10, fontSize: 12.5, fontWeight: 700, color: F.orange }}>
              or just open it here →
            </a>
          </div>
        </div>
      </div>

      {/* STEP 1 — who they are. Everything downstream is gated on this. */}
      <div id="start" style={{
        padding: `${isMobile ? 42 : 64}px ${pad}px`,
        maxWidth: leadDone ? 1180 : 700, margin: '0 auto',
        transition: 'max-width .25s ease',
      }}>
        <div style={{ textAlign: 'center', marginBottom: 8 }}>
          <div style={{ fontSize: 12, fontWeight: 800, letterSpacing: 1.4, color: F.orange, marginBottom: 10 }}>
            {leadDone ? 'STEP 2 OF 2' : 'STEP 1 OF 2'}
          </div>
          <H size={isMobile ? 28 : 38}>
            {leadDone ? `Build your package, ${lead.contactName.split(' ')[0] || 'chef'}.` : 'Where should we send your app plan?'}
          </H>
          <p style={{ fontSize: 16, color: F.body, margin: '0 0 22px', lineHeight: 1.55 }}>
            {leadDone
              ? 'Add only what you actually want on top of the launch package. Nothing is charged here.'
              : 'Takes about twenty seconds. No card, no call required.'}
          </p>
        </div>

        {leadDone ? (
<div style={{ maxWidth: 1180, margin: '0 auto' }}>
          {/* The step label and heading are already rendered by the shared
              header above — repeating them here printed "STEP 2 OF 2 / Build
              your package" twice on the page. */}
          <p style={{ fontSize: 16.5, color: F.body, margin: '0 0 28px', maxWidth: 560, lineHeight: 1.55 }}>
            The ${PRICING.setup} launch stands on its own. Add only what you actually want.
          </p>
          <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1.35fr 0.65fr', gap: isMobile ? 20 : 28, alignItems: 'start' }}>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              <div style={{ fontSize: 13, fontWeight: 800, color: F.ink, marginBottom: 2 }}>
                What you get
              </div>
              {Object.entries(ROUTES).map(([key, r]) => (
                <div key={key} style={{
                  background: F.white, border: `2px solid ${F.orange}`,
                  borderRadius: 14, padding: '15px 16px',
                }}>
                  <div style={{ display: 'flex', gap: 13, alignItems: 'flex-start' }}>
                    <div style={{
                      width: 22, height: 22, borderRadius: 6, flexShrink: 0, marginTop: 1,
                      background: F.orange, color: F.white, display: 'flex',
                      alignItems: 'center', justifyContent: 'center',
                      fontSize: 13, fontWeight: 800, boxSizing: 'border-box',
                    }}>✓</div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 15.5, fontWeight: 800 }}>{r.label}</div>
                      <div style={{ fontSize: 12, fontWeight: 700, color: F.deep, marginTop: 2 }}>
                        {r.badge} · ${r.monthly.toFixed(2)}/mo after the first month
                      </div>
                      <div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 5 }}>
                        {r.points.map((pt, i) => (
                          <div key={i} style={{ display: 'flex', gap: 8, fontSize: 13, color: F.body, lineHeight: 1.45 }}>
                            <span style={{ color: F.orange, fontWeight: 800 }}>•</span>{pt}
                          </div>
                        ))}
                      </div>
                    </div>
                    <div style={{ textAlign: 'right', flexShrink: 0, fontSize: 15, fontWeight: 800 }}>
                      ${PRICING.setup}
                    </div>
                  </div>
                </div>
              ))}
              <div style={{ fontSize: 11.5, color: F.body, lineHeight: 1.5, marginTop: 2, marginBottom: 6 }}>
                Apple requires a restaurant's own standalone app to be submitted on its own
                developer account, so we can't publish it under ours — we do the work, you hold the account.
              </div>
            </div>

            <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
              {/* Base, always on. */}
              <div style={{
                display: 'flex', gap: 13, alignItems: 'flex-start', background: F.white,
                border: `2px solid ${F.orange}`, borderRadius: 14, padding: '15px 16px',
              }}>
                <div style={{
                  width: 22, height: 22, borderRadius: 6, background: F.orange, color: F.white,
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 13, fontWeight: 800, flexShrink: 0, marginTop: 1,
                }}>✓</div>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 15.5, fontWeight: 800 }}>Your app, launch package</div>
                  <div style={{ fontSize: 13.5, color: F.body, marginTop: 3 }}>
                    Built, branded, published. Menu, scan-to-open, social space.
                  </div>
                  {/* Ordering and loyalty used to sit in the add-on list priced
                      at $0, which read as optional extras you still had to tick.
                      They are part of the plan, so they are stated here. */}
                  <div style={{ fontSize: 13.5, color: F.body, marginTop: 3 }}>
                    Online ordering with card payments and loyalty rewards are included.
                  </div>
                </div>
                <div style={{ textAlign: 'right', flexShrink: 0 }}>
                  <div style={{ fontSize: 16, fontWeight: 800 }}>${PRICING.setup}</div>
                  <div style={{ fontSize: 11.5, color: F.body }}>then ${baseMonthly.toFixed(2)}/mo</div>
                </div>
              </div>

              {PRICING.upsells.map((u) => {
                const on = !!picked[u.id];
                return (
                  <div key={u.id} onClick={() => toggle(u.id)} style={{
                    display: 'flex', gap: 13, alignItems: 'flex-start', background: F.white,
                    border: `2px solid ${on ? F.orange : F.line}`, borderRadius: 14,
                    padding: '15px 16px', cursor: 'pointer', transition: 'border-color .15s',
                  }}>
                    <div style={{
                      width: 22, height: 22, borderRadius: 6, flexShrink: 0, marginTop: 1,
                      background: on ? F.orange : F.white, color: F.white,
                      border: on ? 'none' : `2px solid #D9D3C8`,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                      fontSize: 13, fontWeight: 800,
                    }}>{on ? '✓' : ''}</div>
                    <div style={{ flex: 1 }}>
                      <div style={{ fontSize: 15.5, fontWeight: 800 }}>{u.name}</div>
                      <div style={{ fontSize: 13.5, color: F.body, marginTop: 3 }}>{u.blurb}</div>
                    </div>
                    <div style={{ textAlign: 'right', flexShrink: 0, fontSize: 15, fontWeight: 800 }}>
                      +{money(priceOf(u))}<span style={{ fontSize: 11.5, color: F.body, fontWeight: 700 }}>{u.per === 'mo' ? '/mo' : ''}</span>
                    </div>
                  </div>
                );
              })}

              {/* Hardware is a physical order, so the style and the table count
                  are theirs to set rather than a single flat SKU. */}
              {picked.codes && (
                <div onClick={(e) => e.stopPropagation()} style={{
                  background: F.white, border: `2px solid ${F.orange}`, borderTop: 'none',
                  borderRadius: '0 0 14px 14px', padding: '4px 16px 16px', marginTop: -12,
                }}>
                  <div style={{ fontSize: 12.5, fontWeight: 800, color: F.ink, margin: '12px 0 8px' }}>
                    Pick your style
                  </div>
                  <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr 1fr' : 'repeat(5, 1fr)', gap: 8 }}>
                    {PRICING.upsells.find((x) => x.hardware).styles.map((st) => {
                      const on = codeStyle === st.id;
                      return (
                        <div key={st.id} onClick={() => setCodeStyle(st.id)} style={{
                          border: `2px solid ${on ? F.orange : F.line}`, borderRadius: 12,
                          padding: '11px 12px', cursor: 'pointer', background: on ? '#FFF7F0' : F.white,
                        }}>
                          {st.img && (
                            <img src={`/landings/assets/stands/${st.img}.jpg`} alt={st.name}
                                 style={{ width: '100%', aspectRatio: '1', objectFit: 'contain',
                                          borderRadius: 8, background: '#fff', marginBottom: 8 }} />
                          )}
                          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 6 }}>
                            <span style={{ fontSize: 13.5, fontWeight: 800 }}>{st.name}</span>
                            <span style={{ fontSize: 13, fontWeight: 800, color: F.deep }}>
                              {st.unit === 0 ? 'Free' : `$${st.unit}`}
                            </span>
                          </div>
                          <div style={{ fontSize: 11.5, color: F.body, marginTop: 4, lineHeight: 1.4 }}>{st.note}</div>
                          <div style={{ fontSize: 11, color: F.body, marginTop: 4 }}>
                            {st.unit === 0 ? 'included' : 'per table'}
                          </div>
                        </div>
                      );
                    })}
                  </div>

                  <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginTop: 14, flexWrap: 'wrap' }}>
                    <span style={{ fontSize: 13, fontWeight: 800 }}>How many tables?</span>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                      <button type="button" onClick={() => setTables(Math.max(1, tables - 1))} style={qtyBtn}>−</button>
                      <span style={{ minWidth: 28, textAlign: 'center', fontSize: 15, fontWeight: 800 }}>{tables}</span>
                      <button type="button" onClick={() => setTables(Math.min(200, tables + 1))} style={qtyBtn}>+</button>
                    </div>
                    <span style={{ fontSize: 12.5, color: F.body }}>
                      {styleObj.unit === 0
                        ? <>Stickers for {tables} tables + window sticker — <strong style={{ color: F.ink }}>free</strong></>
                        : <>{tables} × ${styleObj.unit} + free window sticker = <strong style={{ color: F.ink }}> ${codesTotal}</strong></>}
                    </span>
                  </div>
                </div>
              )}
            </div>

            {/* Running total — sticky on desktop so it follows the choices. */}

            <div style={{
              background: F.ink, color: F.white, borderRadius: 18, padding: 22,
              position: isMobile ? 'static' : 'sticky', top: 20,
            }}>
              <div style={{ fontSize: 12, fontWeight: 800, letterSpacing: 1, color: F.amber, marginBottom: 12 }}>YOUR PACKAGE</div>

              {/* Billing cadence. Annual is the cheaper path, so it says what
                  it saves rather than making them work it out. */}
              {perMonth > 0 && (
              <div style={{ display: 'flex', gap: 6, background: 'rgba(255,255,255,0.10)', borderRadius: 999, padding: 4, marginBottom: 14 }}>
                {[['monthly', 'Monthly'], ['annual', 'Annual']].map(([key, label]) => {
                  const on = billing === key;
                  return (
                    <button key={key} onClick={() => setBilling(key)} style={{
                      flex: 1, border: 'none', borderRadius: 999, cursor: 'pointer',
                      padding: '8px 0', fontFamily: BRAND.font, fontSize: 12.5, fontWeight: 800,
                      background: on ? F.amber : 'transparent',
                      color: on ? F.ink : 'rgba(255,255,255,0.8)',
                    }}>{label}</button>
                  );
                })}
              </div>
              )}
              {perMonth > 0 && (
                <div style={{
                  background: 'rgba(255,197,61,0.16)', border: '1px solid rgba(255,197,61,0.35)',
                  borderRadius: 10, padding: '8px 10px', marginBottom: 12,
                  fontSize: 12, fontWeight: 700, color: F.amber, textAlign: 'center',
                }}>
                  {isAnnual
                    ? `Two months free — saves ${money(annualSaving)} a year`
                    : 'First month free — nothing charged until next month'}
                </div>
              )}
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14, marginBottom: 7 }}>
                <span style={{ color: 'rgba(255,255,255,0.75)' }}>{setupFee === 0 ? 'Setup' : 'Launch package'}</span>
                <span>{setupFee === 0 ? 'Free' : `$${setupFee}`}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13.5, marginBottom: 6, color: 'rgba(255,255,255,0.75)' }}>
                <span>{ROUTES[route].label}</span>
                <span>{ROUTES[route].price === 0 ? 'incl.' : `+$${ROUTES[route].price}`}</span>
              </div>
              {chosen.filter((u) => u.per === 'once').map((u) => (
                <div key={u.id} style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13.5, marginBottom: 6, color: 'rgba(255,255,255,0.75)' }}>
                  <span>{u.hardware ? `${styleObj.name} × ${tables}` : u.name}</span><span>+${priceOf(u)}</span>
                </div>
              ))}
              {isAnnual && perMonth > 0 && (
                <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13.5, marginBottom: 6, color: 'rgba(255,255,255,0.75)' }}>
                  <span>First year ({ANNUAL_MONTHS} months)</span><span>+{money(annual)}</span>
                </div>
              )}
              <div style={{ borderTop: '1px solid rgba(255,255,255,0.18)', margin: '12px 0', paddingTop: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                <span style={{ fontSize: 15, fontWeight: 800 }}>Due today</span>
                <span style={{ fontSize: 26, fontWeight: 800, color: F.amber }}>{isFree ? 'Free' : money(dueNow)}</span>
              </div>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', fontSize: 14 }}>
                <span style={{ color: 'rgba(255,255,255,0.75)' }}>{isAnnual ? 'Then yearly' : 'From month two'}</span>
                <span style={{ fontWeight: 800 }}>{perMonth === 0 ? 'Nothing' : isAnnual ? `${money(annual)}/yr` : `${money(perMonth)}/mo`}</span>
              </div>
              <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.6)', marginTop: 6, lineHeight: 1.45 }}>
                {!isAnnual && perMonth > 0
                  ? 'Your first month of hosting is free. The monthly starts 30 days after launch and you can cancel before it does.'
                  : 'Includes hosting your standalone app and keeping it current in both stores.'}
              </div>
              {chosen.filter((u) => u.per === 'mo').map((u) => (
                <div key={u.id} style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12.5, marginTop: 5, color: 'rgba(255,255,255,0.6)' }}>
                  <span>incl. {u.name}</span>
                  <span>+${u.price}/mo</span>
                </div>
              ))}
              <button
                onClick={() => setCheckoutOpen(true)}
                style={{
                  display: 'block', width: '100%', textAlign: 'center', marginTop: 18,
                  background: F.orange, color: F.white, fontWeight: 800, fontSize: 15,
                  padding: '14px 0', borderRadius: 12, border: 'none', cursor: 'pointer',
                  fontFamily: BRAND.font,
                }}
              >{isFree ? 'Get my free menu →' : 'Continue →'}</button>
              <div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.6)', textAlign: 'center', marginTop: 10, lineHeight: 1.5 }}>
                Nothing is charged on this page.<br />
                Apple and Google charge their own developer fees — covered above for year one.
              </div>
            </div>
          </div>
        </div>

        ) : (
          <form onSubmit={submitLead} style={{
            background: F.white, border: `1px solid ${F.line}`, borderRadius: 20,
            padding: isMobile ? 20 : 26, display: 'flex', flexDirection: 'column', gap: 13,
            boxShadow: '0 20px 44px -22px rgba(20,16,10,0.22)',
          }}>
            <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 13 }}>
              <div>
                <label style={lbl}>Your name *</label>
                <input style={fld} value={lead.contactName} onChange={setL('contactName')} placeholder="Jordan Diaz" />
              </div>
              <div>
                <label style={lbl}>Business name</label>
                <input style={fld} value={lead.businessName} onChange={setL('businessName')} placeholder={VERTICAL.namePlaceholder} />
              </div>
            </div>
            <div>
              <label style={lbl}>Business type</label>
              <select style={fld} value={lead.businessType} onChange={setL('businessType')}>
                <option>Café / Restaurant</option>
                <option>Bar / Nightlife</option>
                <option>Retail store</option>
                <option>Fitness / Wellness</option>
                <option>Salon / Spa</option>
                <option>Entertainment venue</option>
                <option>Professional services</option>
                <option>Hotel / Hospitality</option>
                <option>Other</option>
              </select>
            </div>
            <div>
              <label style={lbl}>Email *</label>
              <input style={fld} type="email" value={lead.email} onChange={setL('email')} placeholder={VERTICAL.emailPlaceholder} />
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 13 }}>
              <div>
                <label style={lbl}>Website</label>
                <input style={fld} value={lead.website} onChange={setL('website')} placeholder="yourbusiness.com" />
              </div>
              <div>
                <label style={lbl}>Phone</label>
                <input style={fld} value={lead.phone} onChange={setL('phone')} placeholder="(850) 555-0142" />
              </div>
            </div>
            {leadErr && <div style={{ color: F.deep, fontSize: 13.5, fontWeight: 700 }}>{leadErr}</div>}
            <button type="submit" disabled={leadBusy} style={{
              width: '100%', border: 'none', borderRadius: 13, padding: '16px 0',
              background: leadBusy ? '#D9D3C8' : F.orange, color: F.white,
              fontSize: 16.5, fontWeight: 800, cursor: leadBusy ? 'default' : 'pointer', fontFamily: BRAND.font,
            }}>{leadBusy ? 'One moment…' : 'Continue →'}</button>
            <div style={{ fontSize: 12, color: F.body, textAlign: 'center' }}>
              We only use this to send your plan. No spam.
            </div>
          </form>
        )}
      </div>

      {/* WHAT YOU GET */}
      <div style={{ padding: `${isMobile ? 48 : 72}px ${pad}px`, maxWidth: 1180, margin: '0 auto' }}>
        <H>Everything below is included in the ${PRICING.setup}.</H>
        <p style={{ fontSize: 16.5, color: F.body, margin: '0 0 32px', maxWidth: 560, lineHeight: 1.55 }}>
          Not a template you fill in yourself. We build it, publish it, and hand you the keys.
        </p>
        <div style={{
          display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'repeat(3, 1fr)', gap: isMobile ? 14 : 20,
        }}>
          {[
            ['Your app, your name', 'Published to the App Store and Google Play under your own name and colours.'],
            ['Scan-to-open menu', 'Guests point a camera at your code and your menu opens — no download, no app store.'],
            ['Full digital menu', 'Photos, prices, categories, sold-out toggles. Change it yourself in seconds.'],
            ['Your own social space', 'A room only your guests get: who’s in tonight, your regulars, your announcements.'],
            ['Updates without review', 'Menu and design changes reach every phone the same day.'],
            ['Setup done for you', 'We load your menu, shoot the layout, and launch it. You approve.'],
            ['Two rounds of revisions', 'We build it, you review, we adjust — twice, included. Further rounds are available if you need them.'],
          ].map(([t, b], i) => (
            <div key={i} style={{ background: F.cream, border: `1px solid ${F.line}`, borderRadius: 16, padding: 20 }}>
              <div style={{ fontSize: 16.5, fontWeight: 800, marginBottom: 7 }}>{t}</div>
              <div style={{ fontSize: 14, color: F.body, lineHeight: 1.55 }}>{b}</div>
            </div>
          ))}
        </div>
      </div>

      {/* BUILD YOUR PACKAGE — upsells as real, priced choices. */}
      {/* Checkout — opened straight from Continue, so the package and the
          payment are one motion rather than another section to scroll past. */}
      {checkoutOpen && (
        <div style={{
          position: 'fixed', inset: 0, zIndex: 100, display: 'flex',
          alignItems: 'center', justifyContent: 'center', padding: isMobile ? 14 : 24,
        }}>
          <div onClick={() => setCheckoutOpen(false)} style={{ position: 'absolute', inset: 0, background: 'rgba(20,16,10,0.55)' }} />
          <div style={{
            position: 'relative', background: F.white, borderRadius: 20,
            width: '100%', maxWidth: 560, maxHeight: '92vh', overflowY: 'auto',
            padding: isMobile ? 20 : 28, boxShadow: '0 40px 80px -30px rgba(0,0,0,0.6)',
          }}>
            <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 12, marginBottom: 16 }}>
              <div>
                <div style={{ fontSize: isMobile ? 22 : 26, fontWeight: 800, letterSpacing: -0.8 }}>
                  {isFree ? 'Start your free menu' : PAYMENT_LIVE ? 'Checkout' : 'Confirm your details'}
                </div>
                <div style={{ fontSize: 14, color: F.body, marginTop: 4, lineHeight: 1.5 }}>
                  {PAYMENT_LIVE
                    ? `${money(dueNow)} today${perMonth ? (isAnnual ? `, then ${money(annual)}/yr` : `. First month free, then ${money(perMonth)}/mo`) : ''}. Card handled by Stripe.`
                    : `${money(dueNow)} today${perMonth ? `, then ${money(perMonth)}/mo` : ' — no monthly'}. We'll confirm and send payment.`}
                </div>
              </div>
              <button onClick={() => setCheckoutOpen(false)} style={{
                border: 'none', background: 'none', fontSize: 22, cursor: 'pointer',
                color: F.body, lineHeight: 1, padding: 4,
              }}>×</button>
            </div>
            {PAYMENT_LIVE && !isFree
              ? <LaunchCheckout isMobile={isMobile} addons={chosen.filter((u) => !u.hardware).map((u) => u.id).concat(picked.codes ? [`codes_${codeStyle}`] : []).concat(route === 'own' ? ['stores'] : [])} tables={tables} dueNow={dueNow} perMonth={perMonth} money={money} lead={lead} />
              : <BizForm isMobile={isMobile}
                  ctaLabel={isFree ? 'Get my free menu' : `Confirm · ${money(dueNow)}`}
                  prefillNotes={notesPrefill} />}
          </div>
        </div>
      )}

      {/* FAQ */}
      <div style={{ background: F.cream, padding: `${isMobile ? 44 : 66}px ${pad}px` }}>
        <div style={{ maxWidth: 820, margin: '0 auto' }}>
          <H size={isMobile ? 26 : 34}>Straight answers.</H>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 16, marginTop: 22 }}>
            {[
              [`What exactly is the $${PRICING.setup}?`, `A one-time launch fee. It covers designing your app, loading your menu, and publishing it to both app stores. After that it is $${ROUTES.own.monthly.toFixed(2)}/mo to host your app and keep it current in both stores — and the first month is free, so nothing recurring is charged for 30 days. Pay annually instead and you get two months free. Cancel anytime.`],
              ['Do my guests have to download anything?', 'No. Scanning your code opens your menu instantly — that is the App Clip you just tried. Downloading the full app is optional and gets them your social space and rewards.'],
              ['How long does it take?', 'About two weeks from the day we get your menu. Priority launch cuts that to four to seven days.'],
              ['How many changes can I make?', 'Two full rounds of revisions are included before launch — you review what we built and we adjust it. After launch you edit your own menu, prices and photos yourself, whenever you like, at no cost. Extra pre-launch rounds can be added if you expect to iterate more.'],
              ['What if I already have a website?', 'Keep it. This is the app and the in-venue experience — it links to your site rather than replacing it.'],
              ['Who owns the app?', 'You do. It carries your name, your branding, and your customers. If you leave, your menu data comes with you.'],
            ].map(([q, a], i) => (
              <div key={i} style={{ background: F.white, border: `1px solid ${F.line}`, borderRadius: 14, padding: 18 }}>
                <div style={{ fontSize: 15.5, fontWeight: 800, marginBottom: 6 }}>{q}</div>
                <div style={{ fontSize: 14.5, color: F.body, lineHeight: 1.6 }}>{a}</div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* FINAL CTA */}
      <div style={{ background: F.deep, color: F.white, padding: `${isMobile ? 48 : 72}px ${pad}px`, textAlign: 'center' }}>
        <h2 style={{ fontSize: isMobile ? 30 : 44, fontWeight: 800, letterSpacing: -1.4, margin: '0 0 14px', textWrap: 'balance' }}>
          Your competition is on someone else's app.
        </h2>
        <p style={{ fontSize: 17, color: 'rgba(255,255,255,0.9)', margin: '0 0 26px' }}>
          Be on your own. ${PRICING.setup} to launch.
        </p>
        <a href="#start" style={{
          display: 'inline-block', background: F.amber, color: F.deep, fontSize: 17,
          fontWeight: 800, padding: '16px 34px', borderRadius: 14, textDecoration: 'none',
        }}>Claim the ${PRICING.setup} launch →</a>
      </div>
    </div>
  );
}

/**
 * Collects who is buying, then hands off to Stripe Checkout. Deliberately does
 * NOT render card fields — Stripe's hosted page takes the card, so no card
 * data touches this page. Prices are not sent; the server looks them up from
 * the add-on ids, so the page cannot dictate what gets charged.
 */
function LaunchCheckout({ isMobile, addons, dueNow, perMonth, money, lead, tables, billing, route }) {
  // Prefilled from step 1 — asking for the same details twice is where funnels
  // lose people.
  const [v, setV] = useStateF({
    businessName: lead?.businessName || '',
    contactName: lead?.contactName || '',
    email: lead?.email || '',
    phone: lead?.phone || '',
    city: lead?.city || '',
    businessType: lead?.businessType || 'Café / Restaurant',
    placeId: lead?.placeId || '',
    address: lead?.address || '',
    state: lead?.state || '',
    website: lead?.website || '',
  });
  const [busy, setBusy] = useStateF(false);
  const [err, setErr] = useStateF('');
  // Their existing menu — a link, a file, or both. This is what we build the
  // digital menu FROM, so they never have to type items in by hand.
  const [menuUrl, setMenuUrl] = useStateF('');
  const [menuFile, setMenuFile] = useStateF(null);
  const set = (k) => (e) => setV({ ...v, [k]: e.target.value });

  const pickMenuFile = (e) => {
    const f = (e.target.files || [])[0];
    if (!f) { setMenuFile(null); return; }
    if (f.size > 8 * 1024 * 1024) { setErr('That file is over 8MB — try a smaller PDF or a photo.'); e.target.value = ''; return; }
    setErr('');
    setMenuFile(f);
  };

  const go = async (e) => {
    e.preventDefault();
    if (!v.businessName || !v.email) { setErr('Business name and email are required'); return; }
    setBusy(true); setErr('');
    try {
      let filePart = null;
      if (menuFile) {
        const dataUrl = await new Promise((resolve, reject) => {
          const r = new FileReader();
          r.onload = () => resolve(String(r.result));
          r.onerror = reject;
          r.readAsDataURL(menuFile);
        });
        filePart = { name: menuFile.name, type: menuFile.type, dataBase64: dataUrl.split(',')[1] };
      }
      const res = await fetch(CHECKOUT_URL, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ ...v, addons, tables, billing, route, menuUrl, menuFile: filePart }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok || !data.url) { setErr(data.error || 'Could not start checkout. Please try again.'); setBusy(false); return; }
      window.location.href = data.url;   // Stripe hosted checkout
    } catch (_) {
      setErr('Network error. Please try again.');
      setBusy(false);
    }
  };

  const field = { width: '100%', padding: '13px 14px', fontSize: 15, borderRadius: 11,
    border: `1px solid #DED8CE`, fontFamily: BRAND.font, boxSizing: 'border-box', outline: 'none' };
  const label = { fontSize: 12.5, fontWeight: 700, color: F.ink, display: 'block', marginBottom: 6 };

  return (
    <form onSubmit={go} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
      <div>
        <label style={label}>Business name</label>
        <input style={field} value={v.businessName} onChange={set('businessName')} placeholder="Chili Bird" />
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
        <div><label style={label}>Your name</label>
          <input style={field} value={v.contactName} onChange={set('contactName')} placeholder="Jordan Diaz" /></div>
        <div><label style={label}>City</label>
          <input style={field} value={v.city} onChange={set('city')} placeholder="Pensacola" /></div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 14 }}>
        <div><label style={label}>Email</label>
          <input style={field} type="email" value={v.email} onChange={set('email')} placeholder="you@yourbusiness.com" /></div>
        <div><label style={label}>Phone</label>
          <input style={field} value={v.phone} onChange={set('phone')} placeholder="(850) 555-0142" /></div>
      </div>

      {/* ── Where your menu lives now ─────────────────────────────────── */}
      <div style={{ background: F.cream, border: `1px solid ${F.line}`, borderRadius: 12, padding: 14 }}>
        <div style={{ fontSize: 13.5, fontWeight: 800, color: F.ink }}>Where's your menu now?</div>
        <div style={{ fontSize: 12.5, color: F.body, marginTop: 3, marginBottom: 11 }}>
          Give us a link or upload it and we'll build your digital menu for you. Optional — you can also add it later.
        </div>
        <input
          style={{ ...field, background: F.white }}
          value={menuUrl}
          onChange={(e) => setMenuUrl(e.target.value)}
          placeholder="yourbusiness.com/menu, a PDF link, or DoorDash"
        />
        <label style={{
          display: 'flex', alignItems: 'center', gap: 10, marginTop: 10, cursor: 'pointer',
          background: F.white, border: `1px dashed #C9C1B4`, borderRadius: 11, padding: '11px 13px',
        }}>
          <input type="file" accept=".pdf,.jpg,.jpeg,.png,.webp,application/pdf,image/*"
            onChange={pickMenuFile} style={{ display: 'none' }} />
          <span style={{ fontSize: 18, lineHeight: 1 }} aria-hidden="true">＋</span>
          <span style={{ fontSize: 13.5, fontWeight: 700, color: menuFile ? F.ink : F.body, minWidth: 0,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {menuFile ? menuFile.name : 'Upload a PDF or a photo of your menu'}
          </span>
          {menuFile && (
            <span
              onClick={(ev) => { ev.preventDefault(); ev.stopPropagation(); setMenuFile(null); }}
              style={{ marginLeft: 'auto', fontSize: 12, fontWeight: 700, color: F.body }}
            >Remove</span>
          )}
        </label>
        <div style={{ fontSize: 11.5, color: F.body, marginTop: 7 }}>PDF, JPG or PNG · up to 8MB</div>
      </div>

      <div style={{ background: F.cream, border: `1px solid ${F.line}`, borderRadius: 12, padding: 14 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 14.5, fontWeight: 800 }}>
          <span>Due today</span><span>{money(dueNow)}</span>
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 13, color: F.body, marginTop: 5 }}>
          <span>Then monthly</span><span>{money(perMonth)}/mo</span>
        </div>
      </div>

      {err && <div style={{ color: '#B02E00', fontSize: 13.5, fontWeight: 700 }}>{err}</div>}

      <button type="submit" disabled={busy} style={{
        width: '100%', border: 'none', borderRadius: 13, padding: '16px 0',
        background: busy ? '#D9D3C8' : F.orange, color: F.white,
        fontSize: 16.5, fontWeight: 800, cursor: busy ? 'default' : 'pointer', fontFamily: BRAND.font,
      }}>
        {busy ? 'Taking you to Stripe…' : `Continue to secure checkout · ${money(dueNow)}`}
      </button>
      <div style={{ fontSize: 12, color: F.body, textAlign: 'center' }}>
        Payment is handled by Stripe. Cancel the monthly plan anytime.
      </div>
    </form>
  );
}

window.LandingFunnel = LandingFunnel;
