{"id":7436,"date":"2026-07-16T15:19:03","date_gmt":"2026-07-16T15:19:03","guid":{"rendered":"https:\/\/mutualafricapay.com\/marketplace\/?page_id=7436"},"modified":"2026-07-17T07:26:09","modified_gmt":"2026-07-17T07:26:09","slug":"flow","status":"publish","type":"page","link":"https:\/\/mutualafricapay.com\/marketplace\/onboarding\/flow\/","title":{"rendered":"flow"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"7436\" class=\"elementor elementor-7436\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e969818 e-con e-atomic-element e-flexbox-base e-e969818-714af10 \" data-id=\"e969818\" data-element_type=\"e-flexbox\" data-e-type=\"e-flexbox\" data-interaction-id=\"e969818\">\n    \t\t<div class=\"elementor-element elementor-element-638af32 elementor-widget elementor-widget-html\" data-id=\"638af32\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!--\r\n  Mutual Africa Marketplace \u2014 public onboarding + listing creation\r\n  ------------------------------------------------------------\r\n  Built for: Elementor \"HTML\" widget. No <html>\/<head>\/<body> \u2014\r\n  paste this whole block into the widget. All CSS\/JS is scoped\r\n  under .mafp-onboard \/ mafpOnboard so nothing leaks into the theme.\r\n\r\n  This closely follows marketplace-create-listing.html (the internal\r\n  app file you shared) \u2014 same listing types, field names, validation,\r\n  storage bucket paths, fee amount, and PayPal client ID. The one\r\n  addition is Step 0 (Account), since public visitors won't already\r\n  have a session the way logged-in app users do.\r\n\r\n  CONFIG \u2014 fill these in before publishing:\r\n    SUPABASE_URL \/ SUPABASE_ANON_KEY   \u2014 same values as config.js in the app\r\n    (PayPal client ID below is the one already live in your app file \u2014\r\n    reused as-is since it's already public\/production.)\r\n\r\n  SUBSTITUTED, NOT COPIED:\r\n    Your file loads action-feedback.js\/css for toast messages \u2014 I don't\r\n    have those, so I built a small self-contained status banner instead\r\n    (search \"mafpToast\"). Swap in the real one if you'd rather match\r\n    pixel-for-pixel.\r\n\r\n  CORS: Storage\/Auth\/REST calls go straight to Supabase from the\r\n  browser. If this domain (mutualafricapay.com) isn't already an\r\n  allowed origin on your Supabase project, add it or these calls\r\n  will fail silently with CORS errors.\r\n\r\n  LINKS TO CONFIRM:\r\n    - Back button  -> assumed https:\/\/mutualafricapay.com\/marketplace\/\r\n    - Disclaimer   -> assumed https:\/\/mutualafricapay.com\/marketplace-disclaimer\/\r\n    - \"View my listings\" on the success screen -> this is an app page,\r\n      not a WordPress page. Point it at your actual app URL.\r\n-->\r\n\r\n<div class=\"mafp-onboard\">\r\n<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\r\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\r\n<link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/@tabler\/icons-webfont@3.1.0\/dist\/tabler-icons.min.css\">\r\n\r\n<style>\r\n.mafp-onboard{\r\n  --navy:#152948;--green:#2E9E44;--green-bg:#E8F7EC;\r\n  --white:#ffffff;--bg:#F5F7FA;--border:#E5E9EF;\r\n  --text-dark:#152948;--text-mid:#4A5568;--text-light:#8A93A6;--red:#E24B4A;--amber:#EF9F27;\r\n  --radius-sm:8px;--radius-md:12px;--radius-lg:16px;\r\n  font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\r\n  color:var(--text-dark);-webkit-font-smoothing:antialiased;\r\n  background:var(--bg);\r\n}\r\n.mafp-onboard *{box-sizing:border-box;}\r\n.mafp-onboard button{font-family:inherit;cursor:pointer;border:none;background:none;}\r\n.mafp-onboard input,.mafp-onboard select,.mafp-onboard textarea{font-family:inherit;}\r\n.mafp-onboard a{color:inherit;}\r\n.mafp-onboard :focus-visible{outline:2px solid var(--green);outline-offset:2px;}\r\n\r\n.mafp-onboard .topbar{height:64px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 24px;gap:14px;}\r\n.mafp-onboard .back-btn{font-size:20px;color:var(--text-mid);cursor:pointer;}\r\n.mafp-onboard .topbar-title{font-size:16px;font-weight:700;}\r\n\r\n.mafp-onboard .wrap{max-width:680px;margin:0 auto;padding:28px 20px 80px;}\r\n\r\n.mafp-onboard .progress{display:flex;gap:6px;margin-bottom:8px;}\r\n.mafp-onboard .prog-step{flex:1;height:4px;border-radius:4px;background:var(--border);transition:background 0.3s;}\r\n.mafp-onboard .prog-step.done{background:var(--green);}\r\n.mafp-onboard .prog-step.active{background:#A8D5B5;}\r\n.mafp-onboard .prog-label{font-size:11px;color:var(--text-light);text-align:right;margin-bottom:20px;}\r\n\r\n.mafp-onboard .panel{background:var(--white);border:1px solid var(--border);border-radius:var(--radius-lg);padding:24px;margin-bottom:16px;}\r\n.mafp-onboard .step-icon{width:48px;height:48px;border-radius:var(--radius-md);background:var(--green-bg);display:flex;align-items:center;justify-content:center;font-size:22px;color:var(--green);margin-bottom:16px;}\r\n.mafp-onboard .step-title{font-size:18px;font-weight:700;margin-bottom:6px;}\r\n.mafp-onboard .step-sub{font-size:13px;color:var(--text-light);margin-bottom:22px;line-height:1.5;}\r\n\r\n.mafp-onboard .field{margin-bottom:14px;}\r\n.mafp-onboard .field label{display:block;font-size:12px;font-weight:600;color:var(--text-mid);margin-bottom:6px;}\r\n.mafp-onboard .req{color:var(--red);}\r\n.mafp-onboard .field input,.mafp-onboard .field select,.mafp-onboard .field textarea{width:100%;padding:10px 13px;border:1.5px solid var(--border);border-radius:var(--radius-sm);font-size:13.5px;color:var(--text-dark);background:var(--white);transition:border-color 0.15s;}\r\n.mafp-onboard .field input:focus,.mafp-onboard .field select:focus,.mafp-onboard .field textarea:focus{outline:none;border-color:var(--green);}\r\n.mafp-onboard .field textarea{resize:vertical;min-height:100px;}\r\n.mafp-onboard .field-row{display:grid;grid-template-columns:1fr 1fr;gap:12px;}\r\n.mafp-onboard .field-hint{font-size:11px;color:var(--text-light);margin-top:4px;}\r\n\r\n.mafp-onboard .tabs{display:flex;gap:8px;margin-bottom:20px;}\r\n.mafp-onboard .tab-btn{flex:1;padding:10px;border-radius:var(--radius-sm);border:1.5px solid var(--border);background:var(--white);font-weight:700;font-size:13.5px;color:var(--text-mid);}\r\n.mafp-onboard .tab-btn.selected{border-color:var(--green);background:var(--green-bg);color:var(--green);}\r\n\r\n.mafp-onboard .type-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:20px;}\r\n.mafp-onboard .type-card{border:1.5px solid var(--border);border-radius:var(--radius-md);padding:16px;cursor:pointer;transition:all 0.15s;text-align:center;}\r\n.mafp-onboard .type-card:hover{border-color:var(--green);}\r\n.mafp-onboard .type-card.selected{border-color:var(--green);background:var(--green-bg);}\r\n.mafp-onboard .type-icon{font-size:28px;margin-bottom:8px;}\r\n.mafp-onboard .type-name{font-size:13px;font-weight:700;color:var(--text-dark);}\r\n.mafp-onboard .type-desc{font-size:11.5px;color:var(--text-light);margin-top:3px;line-height:1.4;}\r\n\r\n.mafp-onboard .image-upload-area{border:2px dashed var(--border);border-radius:var(--radius-md);padding:28px;text-align:center;cursor:pointer;transition:border-color 0.15s;}\r\n.mafp-onboard .image-upload-area:hover{border-color:var(--green);}\r\n.mafp-onboard .image-upload-area i{font-size:32px;color:var(--text-light);margin-bottom:8px;}\r\n.mafp-onboard .image-upload-area p{font-size:13px;color:var(--text-light);}\r\n.mafp-onboard .image-previews{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}\r\n.mafp-onboard .image-preview{width:80px;height:80px;border-radius:var(--radius-sm);object-fit:cover;}\r\n.mafp-onboard .image-preview-wrap{position:relative;flex-shrink:0;}\r\n.mafp-onboard .image-remove{position:absolute;top:-6px;right:-6px;width:18px;height:18px;background:var(--red);color:#fff;border-radius:50%;font-size:11px;display:flex;align-items:center;justify-content:center;cursor:pointer;}\r\n\r\n.mafp-onboard .kyc-upload-row{display:flex;gap:12px;margin-bottom:16px;}\r\n.mafp-onboard .kyc-upload-box{flex:1;border:1.5px dashed var(--border);border-radius:var(--radius-md);padding:20px;text-align:center;cursor:pointer;transition:border-color 0.15s;}\r\n.mafp-onboard .kyc-upload-box:hover{border-color:var(--green);}\r\n.mafp-onboard .kyc-upload-box.done{border-color:var(--green);background:var(--green-bg);}\r\n.mafp-onboard .kyc-upload-box i{font-size:28px;color:var(--text-light);display:block;margin-bottom:8px;}\r\n.mafp-onboard .kyc-upload-box.done i{color:var(--green);}\r\n.mafp-onboard .kyc-upload-box p{font-size:12px;color:var(--text-light);}\r\n\r\n.mafp-onboard .price-summary{background:var(--bg);border-radius:var(--radius-md);padding:16px 20px;margin-bottom:20px;}\r\n.mafp-onboard .price-row{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;font-size:13.5px;}\r\n.mafp-onboard .price-row:last-child{margin-bottom:0;font-weight:700;font-size:15px;border-top:1px solid var(--border);padding-top:10px;margin-top:8px;}\r\n\r\n.mafp-onboard .btn-row{display:flex;gap:10px;margin-top:20px;}\r\n.mafp-onboard .btn-primary{flex:1;padding:13px;background:var(--green);color:#fff;border-radius:var(--radius-sm);font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;gap:8px;text-decoration:none;}\r\n.mafp-onboard .btn-primary:hover{background:#268A3B;}\r\n.mafp-onboard .btn-primary:disabled{opacity:0.6;cursor:not-allowed;}\r\n.mafp-onboard .btn-secondary{padding:13px 18px;background:var(--bg);color:var(--text-mid);border-radius:var(--radius-sm);font-size:14px;font-weight:600;text-decoration:none;display:inline-flex;align-items:center;}\r\n.mafp-onboard .btn-outline-sm{display:inline-flex;align-items:center;gap:6px;padding:9px 16px;border:1.5px solid var(--border);border-radius:var(--radius-sm);font-size:12.5px;font-weight:600;color:var(--text-mid);background:#fff;}\r\n.mafp-onboard .btn-outline-sm:hover{border-color:var(--green);color:var(--green);}\r\n.mafp-onboard .type-toggle-chip{padding:8px 14px;border:1.5px solid var(--border);border-radius:20px;font-size:12px;font-weight:600;color:var(--text-mid);cursor:pointer;}\r\n.mafp-onboard .type-toggle-chip.selected{border-color:var(--green);background:var(--green-bg);color:var(--green);}\r\n\r\n.mafp-onboard .error-msg{background:#FCEBEB;color:var(--red);font-size:12.5px;padding:10px 14px;border-radius:var(--radius-sm);margin-bottom:14px;display:none;align-items:center;gap:8px;}\r\n.mafp-onboard .error-msg.show{display:flex;}\r\n.mafp-onboard .toast{background:var(--navy);color:#fff;font-size:13px;padding:12px 16px;border-radius:var(--radius-sm);margin-bottom:14px;display:none;align-items:center;gap:8px;}\r\n.mafp-onboard .toast.show{display:flex;}\r\n.mafp-onboard .toast.success{background:var(--green);}\r\n.mafp-onboard .toast.error{background:var(--red);}\r\n\r\n.mafp-onboard .spinner{width:16px;height:16px;border:2px solid rgba(255,255,255,0.4);border-top-color:#fff;border-radius:50%;animation:mafp-spin 0.7s linear infinite;display:none;}\r\n.mafp-onboard .spinner.show{display:inline-block;}\r\n@keyframes mafp-spin{to{transform:rotate(360deg);}}\r\n\r\n.mafp-onboard .success-wrap{text-align:center;padding:20px 0;}\r\n.mafp-onboard .success-icon{width:64px;height:64px;border-radius:50%;background:var(--green-bg);color:var(--green);display:flex;align-items:center;justify-content:center;font-size:30px;margin:0 auto 16px;}\r\n\r\n@media(max-width:560px){\r\n  .mafp-onboard .type-grid{grid-template-columns:1fr;}\r\n  .mafp-onboard .field-row{grid-template-columns:1fr;}\r\n  .mafp-onboard .kyc-upload-row{flex-direction:column;}\r\n}\r\n<\/style>\r\n\r\n<header class=\"topbar\">\r\n  <i class=\"ti ti-arrow-left back-btn\" onclick=\"window.location.href='https:\/\/mutualafricapay.com\/marketplace\/'\"><\/i>\r\n  <div class=\"topbar-title\">List an opportunity<\/div>\r\n<\/header>\r\n\r\n<div class=\"wrap\">\r\n\r\n  <div class=\"progress\">\r\n    <div class=\"prog-step active\" id=\"mo-ps0\"><\/div>\r\n    <div class=\"prog-step\" id=\"mo-ps1\"><\/div>\r\n    <div class=\"prog-step\" id=\"mo-ps2\"><\/div>\r\n    <div class=\"prog-step\" id=\"mo-ps3\"><\/div>\r\n    <div class=\"prog-step\" id=\"mo-ps4\"><\/div>\r\n  <\/div>\r\n  <div class=\"prog-label\" id=\"mo-progLabel\">Step 1 of 5<\/div>\r\n\r\n  <div class=\"error-msg\" id=\"mo-errorMsg\"><i class=\"ti ti-alert-circle\"><\/i><span id=\"mo-errorText\"><\/span><\/div>\r\n  <div class=\"toast\" id=\"mo-toast\"><span class=\"spinner\" id=\"mo-toastSpinner\"><\/span><span id=\"mo-toastText\"><\/span><\/div>\r\n\r\n  <!-- Step 0: Account (new \u2014 the app itself assumes you're already logged in) -->\r\n  <div id=\"mo-step0\">\r\n    <div class=\"panel\">\r\n      <div class=\"step-icon\"><i class=\"ti ti-user-circle\"><\/i><\/div>\r\n      <div class=\"step-title\">Create your account<\/div>\r\n      <div class=\"step-sub\">You'll use this to manage your listing and hear from buyers or investors.<\/div>\r\n\r\n      <div class=\"tabs\">\r\n        <button class=\"tab-btn selected\" id=\"mo-tabSignup\" onclick=\"mafpOnboard.setAuthMode('signup')\">Sign up<\/button>\r\n        <button class=\"tab-btn\" id=\"mo-tabLogin\" onclick=\"mafpOnboard.setAuthMode('login')\">Log in<\/button>\r\n      <\/div>\r\n\r\n      <div class=\"field\">\r\n        <label for=\"mo-authEmail\">Email <span class=\"req\">*<\/span><\/label>\r\n        <input type=\"email\" id=\"mo-authEmail\" placeholder=\"you@business.com\" autocomplete=\"email\">\r\n      <\/div>\r\n      <div class=\"field\">\r\n        <label for=\"mo-authPassword\">Password <span class=\"req\">*<\/span><\/label>\r\n        <input type=\"password\" id=\"mo-authPassword\" placeholder=\"At least 8 characters\" autocomplete=\"new-password\">\r\n      <\/div>\r\n\r\n      <div class=\"btn-row\">\r\n        <button class=\"btn-primary\" id=\"mo-authSubmit\" onclick=\"mafpOnboard.submitAuth()\">\r\n          <span class=\"spinner\" id=\"mo-authSpinner\"><\/span>\r\n          Continue <i class=\"ti ti-arrow-right\"><\/i>\r\n        <\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- Step 1: Listing type -->\r\n  <div id=\"mo-step1\" style=\"display:none;\">\r\n    <div class=\"panel\">\r\n      <div class=\"step-icon\"><i class=\"ti ti-list-check\"><\/i><\/div>\r\n      <div class=\"step-title\">What are you listing?<\/div>\r\n      <div class=\"step-sub\">Choose the type of opportunity you want to publish.<\/div>\r\n      <div class=\"type-grid\">\r\n        <div class=\"type-card selected\" id=\"mo-type-business-for-sale\" onclick=\"mafpOnboard.selectType('business-for-sale')\">\r\n          <div class=\"type-icon\">\ud83c\udfe2<\/div>\r\n          <div class=\"type-name\">Business for sale<\/div>\r\n          <div class=\"type-desc\">Selling an existing business or company<\/div>\r\n        <\/div>\r\n        <div class=\"type-card\" id=\"mo-type-franchise\" onclick=\"mafpOnboard.selectType('franchise')\">\r\n          <div class=\"type-icon\">\ud83d\udd17<\/div>\r\n          <div class=\"type-name\">Franchise opportunity<\/div>\r\n          <div class=\"type-desc\">Offering or seeking a franchise arrangement<\/div>\r\n        <\/div>\r\n        <div class=\"type-card\" id=\"mo-type-professional-service\" onclick=\"mafpOnboard.selectType('professional-service')\">\r\n          <div class=\"type-icon\">\ud83d\udcbc<\/div>\r\n          <div class=\"type-name\">Professional service<\/div>\r\n          <div class=\"type-desc\">Offering business consulting, legal, finance, or other services<\/div>\r\n        <\/div>\r\n        <div class=\"type-card\" id=\"mo-type-investment-opportunity\" onclick=\"mafpOnboard.selectType('investment-opportunity')\">\r\n          <div class=\"type-icon\">\ud83d\udcc8<\/div>\r\n          <div class=\"type-name\">Investment opportunity<\/div>\r\n          <div class=\"type-desc\">Seeking funding, investors, or business partners<\/div>\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"btn-row\">\r\n        <button class=\"btn-primary\" onclick=\"mafpOnboard.goStep(2)\">Next <i class=\"ti ti-arrow-right\"><\/i><\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- Step 2: Listing details -->\r\n  <div id=\"mo-step2\" style=\"display:none;\">\r\n    <div class=\"panel\">\r\n      <div class=\"step-icon\"><i class=\"ti ti-edit\"><\/i><\/div>\r\n      <div class=\"step-title\">Listing details<\/div>\r\n      <div class=\"step-sub\">Provide accurate information. Misleading listings will be removed and no refunds will be issued.<\/div>\r\n\r\n      <div class=\"field\">\r\n        <label for=\"mo-listingTitle\">Listing title <span class=\"req\">*<\/span><\/label>\r\n        <input type=\"text\" id=\"mo-listingTitle\" placeholder=\"e.g. Profitable car wash in Sandton, established 5 years\">\r\n      <\/div>\r\n      <div class=\"field\">\r\n        <label for=\"mo-listingDesc\">Description <span class=\"req\">*<\/span><\/label>\r\n        <textarea id=\"mo-listingDesc\" placeholder=\"Describe the opportunity in detail. Include key highlights, reason for listing, and what makes this attractive to buyers or investors.\"><\/textarea>\r\n        <div class=\"field-hint\">Minimum 50 characters.<\/div>\r\n      <\/div>\r\n      <div class=\"field-row\">\r\n        <div class=\"field\">\r\n          <label for=\"mo-listingCountry\">Country <span class=\"req\">*<\/span><\/label>\r\n          <select id=\"mo-listingCountry\">\r\n            <option value=\"\">Select country<\/option>\r\n            <option value=\"South Africa\">South Africa<\/option>\r\n            <option value=\"Nigeria\">Nigeria<\/option>\r\n            <option value=\"Kenya\">Kenya<\/option>\r\n            <option value=\"Ghana\">Ghana<\/option>\r\n            <option value=\"Zimbabwe\">Zimbabwe<\/option>\r\n            <option value=\"Zambia\">Zambia<\/option>\r\n            <option value=\"Tanzania\">Tanzania<\/option>\r\n            <option value=\"Uganda\">Uganda<\/option>\r\n            <option value=\"Ethiopia\">Ethiopia<\/option>\r\n            <option value=\"Botswana\">Botswana<\/option>\r\n            <option value=\"Namibia\">Namibia<\/option>\r\n            <option value=\"Mozambique\">Mozambique<\/option>\r\n            <option value=\"Rwanda\">Rwanda<\/option>\r\n            <option value=\"Malawi\">Malawi<\/option>\r\n            <option value=\"Other\">Other<\/option>\r\n          <\/select>\r\n        <\/div>\r\n        <div class=\"field\">\r\n          <label for=\"mo-listingCity\">City<\/label>\r\n          <input type=\"text\" id=\"mo-listingCity\" placeholder=\"e.g. Johannesburg\">\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"field\">\r\n        <label for=\"mo-listingIndustry\">Industry<\/label>\r\n        <select id=\"mo-listingIndustry\">\r\n          <option value=\"\">Select industry<\/option>\r\n          <option value=\"Agriculture\">Agriculture<\/option>\r\n          <option value=\"Construction\">Construction<\/option>\r\n          <option value=\"Education\">Education<\/option>\r\n          <option value=\"Finance & Insurance\">Finance &amp; Insurance<\/option>\r\n          <option value=\"Food & Beverage\">Food &amp; Beverage<\/option>\r\n          <option value=\"Healthcare\">Healthcare<\/option>\r\n          <option value=\"Hospitality & Tourism\">Hospitality &amp; Tourism<\/option>\r\n          <option value=\"ICT & Technology\">ICT &amp; Technology<\/option>\r\n          <option value=\"Manufacturing\">Manufacturing<\/option>\r\n          <option value=\"Media & Creative\">Media &amp; Creative<\/option>\r\n          <option value=\"Real Estate\">Real Estate<\/option>\r\n          <option value=\"Retail & Wholesale\">Retail &amp; Wholesale<\/option>\r\n          <option value=\"Transport & Logistics\">Transport &amp; Logistics<\/option>\r\n          <option value=\"Other\">Other<\/option>\r\n        <\/select>\r\n      <\/div>\r\n\r\n      <div id=\"mo-fields-business-for-sale\">\r\n        <div class=\"field-row\">\r\n          <div class=\"field\"><label for=\"mo-askingPrice\">Asking price (USD)<\/label><input type=\"number\" id=\"mo-askingPrice\" placeholder=\"0\"><\/div>\r\n          <div class=\"field\"><label for=\"mo-annualRevenue\">Annual revenue (USD)<\/label><input type=\"number\" id=\"mo-annualRevenue\" placeholder=\"0\"><\/div>\r\n        <\/div>\r\n        <div class=\"field-row\">\r\n          <div class=\"field\"><label for=\"mo-annualProfit\">Annual profit (USD)<\/label><input type=\"number\" id=\"mo-annualProfit\" placeholder=\"0\"><\/div>\r\n          <div class=\"field\"><label for=\"mo-employees\">Number of employees<\/label><input type=\"number\" id=\"mo-employees\" placeholder=\"0\"><\/div>\r\n        <\/div>\r\n        <div class=\"field-row\">\r\n          <div class=\"field\"><label for=\"mo-establishedYear\">Year established<\/label><input type=\"number\" id=\"mo-establishedYear\" placeholder=\"2015\"><\/div>\r\n          <div class=\"field\"><label for=\"mo-reasonForSale\">Reason for sale<\/label><input type=\"text\" id=\"mo-reasonForSale\" placeholder=\"Retirement \/ relocation \/ other\"><\/div>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div id=\"mo-fields-investment-opportunity\" style=\"display:none;\">\r\n        <div class=\"field-row\">\r\n          <div class=\"field\"><label for=\"mo-fundingAmount\">Funding amount sought (USD)<\/label><input type=\"number\" id=\"mo-fundingAmount\" placeholder=\"0\"><\/div>\r\n          <div class=\"field\"><label for=\"mo-equityOffered\">Equity offered (%)<\/label><input type=\"number\" id=\"mo-equityOffered\" placeholder=\"0\" min=\"0\" max=\"100\"><\/div>\r\n        <\/div>\r\n        <div class=\"field\">\r\n          <label for=\"mo-fundingStage\">Funding stage<\/label>\r\n          <select id=\"mo-fundingStage\">\r\n            <option value=\"\">Select<\/option>\r\n            <option value=\"idea\">Idea \/ concept<\/option>\r\n            <option value=\"pre-revenue\">Pre-revenue<\/option>\r\n            <option value=\"seed\">Seed<\/option>\r\n            <option value=\"growth\">Growth<\/option>\r\n            <option value=\"expansion\">Expansion<\/option>\r\n          <\/select>\r\n        <\/div>\r\n\r\n        <div class=\"field\">\r\n          <label>Pitch deck <span style=\"font-weight:400;color:var(--text-light);\">(optional \u2014 PDF or PPTX)<\/span><\/label>\r\n          <div id=\"mo-pitchDeckPreview\" style=\"font-size:12.5px;color:var(--text-mid);margin-bottom:8px;\"><\/div>\r\n          <button type=\"button\" class=\"btn-outline-sm\" onclick=\"document.getElementById('mo-pitchDeckInput').click()\"><i class=\"ti ti-upload\"><\/i> Upload pitch deck<\/button>\r\n          <input type=\"file\" id=\"mo-pitchDeckInput\" accept=\".pdf,.ppt,.pptx\" style=\"display:none;\" onchange=\"mafpOnboard.handlePitchDeckUpload(event)\">\r\n        <\/div>\r\n\r\n        <div class=\"field\">\r\n          <label>Video pitch <span style=\"font-weight:400;color:var(--text-light);\">(optional)<\/span><\/label>\r\n          <div style=\"display:flex;gap:8px;margin-bottom:10px;\">\r\n            <div class=\"type-toggle-chip selected\" id=\"mo-vpt-link\" onclick=\"mafpOnboard.setVideoPitchType('link')\">\ud83d\udd17 Link (YouTube\/Vimeo\/Loom)<\/div>\r\n            <div class=\"type-toggle-chip\" id=\"mo-vpt-upload\" onclick=\"mafpOnboard.setVideoPitchType('upload')\">\ud83d\udce4 Upload file<\/div>\r\n          <\/div>\r\n          <div id=\"mo-videoPitchLinkField\">\r\n            <input type=\"url\" id=\"mo-videoPitchUrlInput\" placeholder=\"https:\/\/youtube.com\/watch?v=...\">\r\n          <\/div>\r\n          <div id=\"mo-videoPitchUploadField\" style=\"display:none;\">\r\n            <div id=\"mo-videoPitchPreview\" style=\"font-size:12.5px;color:var(--text-mid);margin-bottom:8px;\"><\/div>\r\n            <button type=\"button\" class=\"btn-outline-sm\" onclick=\"document.getElementById('mo-videoPitchInput').click()\"><i class=\"ti ti-upload\"><\/i> Upload video<\/button>\r\n            <input type=\"file\" id=\"mo-videoPitchInput\" accept=\"video\/mp4,video\/quicktime,video\/webm\" style=\"display:none;\" onchange=\"mafpOnboard.handleVideoPitchUpload(event)\">\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div id=\"mo-fields-professional-service\" style=\"display:none;\">\r\n        <div class=\"field-row\">\r\n          <div class=\"field\"><label for=\"mo-serviceType\">Service type<\/label><input type=\"text\" id=\"mo-serviceType\" placeholder=\"e.g. Business consulting, Legal, Accounting\"><\/div>\r\n          <div class=\"field\"><label for=\"mo-rateFrom\">Rate from (USD)<\/label><input type=\"number\" id=\"mo-rateFrom\" placeholder=\"0\"><\/div>\r\n        <\/div>\r\n        <div class=\"field\">\r\n          <label for=\"mo-ratePeriod\">Rate period<\/label>\r\n          <select id=\"mo-ratePeriod\">\r\n            <option value=\"hourly\">Per hour<\/option>\r\n            <option value=\"daily\">Per day<\/option>\r\n            <option value=\"project\">Per project<\/option>\r\n            <option value=\"monthly\">Per month<\/option>\r\n          <\/select>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"field\">\r\n        <label>Images <span style=\"font-weight:400;color:var(--text-light);\">(up to 5, optional)<\/span><\/label>\r\n        <div class=\"image-upload-area\" onclick=\"document.getElementById('mo-imageInput').click()\">\r\n          <i class=\"ti ti-photo\"><\/i>\r\n          <p>Click to upload images of your business or opportunity<\/p>\r\n          <p style=\"font-size:11px;margin-top:4px;\">JPG or PNG, max 5MB each<\/p>\r\n        <\/div>\r\n        <input type=\"file\" id=\"mo-imageInput\" accept=\"image\/*\" multiple style=\"display:none;\" onchange=\"mafpOnboard.handleImageUpload(event)\">\r\n        <div class=\"image-previews\" id=\"mo-imagePreviews\"><\/div>\r\n      <\/div>\r\n\r\n      <div class=\"btn-row\">\r\n        <button class=\"btn-secondary\" onclick=\"mafpOnboard.goStep(1)\">Back<\/button>\r\n        <button class=\"btn-primary\" onclick=\"mafpOnboard.goStep(3)\">Next <i class=\"ti ti-arrow-right\"><\/i><\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- Step 3: KYC -->\r\n  <div id=\"mo-step3\" style=\"display:none;\">\r\n    <div class=\"panel\">\r\n      <div class=\"step-icon\"><i class=\"ti ti-shield-check\"><\/i><\/div>\r\n      <div class=\"step-title\">Identity verification<\/div>\r\n      <div class=\"step-sub\">Required for all listers to protect buyers and investors from fraud. Your information is securely stored and never shared with other users.<\/div>\r\n\r\n      <div id=\"mo-kycAlreadyDone\" style=\"display:none;background:var(--green-bg);border:1px solid #C3E6CB;border-radius:var(--radius-md);padding:16px;margin-bottom:16px;\">\r\n        <div style=\"display:flex;align-items:center;gap:10px;font-size:13.5px;font-weight:600;color:var(--green);\">\r\n          <i class=\"ti ti-shield-check\" style=\"font-size:20px;\"><\/i>\r\n          Your identity is already verified. You can proceed to payment.\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div id=\"mo-kycForm\">\r\n        <div class=\"field-row\">\r\n          <div class=\"field\">\r\n            <label for=\"mo-kycFullName\">Full legal name <span class=\"req\">*<\/span><\/label>\r\n            <input type=\"text\" id=\"mo-kycFullName\" placeholder=\"As it appears on your ID\">\r\n          <\/div>\r\n          <div class=\"field\">\r\n            <label for=\"mo-kycIdNumber\">ID \/ Passport number <span class=\"req\">*<\/span><\/label>\r\n            <input type=\"text\" id=\"mo-kycIdNumber\" placeholder=\"ID or passport number\">\r\n          <\/div>\r\n        <\/div>\r\n        <div class=\"field-row\">\r\n          <div class=\"field\">\r\n            <label for=\"mo-kycDob\">Date of birth <span class=\"req\">*<\/span><\/label>\r\n            <input type=\"date\" id=\"mo-kycDob\">\r\n          <\/div>\r\n          <div class=\"field\">\r\n            <label for=\"mo-kycNationality\">Nationality <span class=\"req\">*<\/span><\/label>\r\n            <input type=\"text\" id=\"mo-kycNationality\" placeholder=\"e.g. South African\">\r\n          <\/div>\r\n        <\/div>\r\n\r\n        <div class=\"kyc-upload-row\">\r\n          <div class=\"kyc-upload-box\" id=\"mo-kycIdBox\" onclick=\"document.getElementById('mo-kycIdInput').click()\">\r\n            <i class=\"ti ti-id-badge\"><\/i>\r\n            <div style=\"font-size:13px;font-weight:600;margin-bottom:4px;\">ID document<\/div>\r\n            <p>Upload a clear photo of your ID, passport, or driver's licence<\/p>\r\n          <\/div>\r\n          <div class=\"kyc-upload-box\" id=\"mo-kycSelfieBox\" onclick=\"document.getElementById('mo-kycSelfieInput').click()\">\r\n            <i class=\"ti ti-camera-selfie\"><\/i>\r\n            <div style=\"font-size:13px;font-weight:600;margin-bottom:4px;\">Selfie<\/div>\r\n            <p>Take or upload a clear selfie holding your ID document<\/p>\r\n          <\/div>\r\n        <\/div>\r\n        <input type=\"file\" id=\"mo-kycIdInput\" accept=\"image\/*\" style=\"display:none;\" onchange=\"mafpOnboard.handleKycUpload('id',event)\">\r\n        <input type=\"file\" id=\"mo-kycSelfieInput\" accept=\"image\/*\" capture=\"user\" style=\"display:none;\" onchange=\"mafpOnboard.handleKycUpload('selfie',event)\">\r\n        <div id=\"mo-kycProcessing\" style=\"display:none;text-align:center;padding:20px;font-size:13px;color:var(--text-light);\">\r\n          <i class=\"ti ti-loader-2\" style=\"font-size:24px;display:block;margin-bottom:8px;animation:mafp-spin 0.7s linear infinite;\"><\/i>\r\n          Verifying your identity...\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"btn-row\">\r\n        <button class=\"btn-secondary\" onclick=\"mafpOnboard.goStep(2)\">Back<\/button>\r\n        <button class=\"btn-primary\" id=\"mo-kycNextBtn\" onclick=\"mafpOnboard.submitKyc()\">\r\n          <span class=\"spinner\" id=\"mo-kycSpinner\"><\/span>\r\n          Submit &amp; continue\r\n        <\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- Step 4: Payment -->\r\n  <div id=\"mo-step4\" style=\"display:none;\">\r\n    <div class=\"panel\">\r\n      <div class=\"step-icon\"><i class=\"ti ti-credit-card\"><\/i><\/div>\r\n      <div class=\"step-title\">Publish your listing<\/div>\r\n      <div class=\"step-sub\">Pay the one-time listing fee to make your opportunity visible to buyers and investors across Africa.<\/div>\r\n\r\n      <div class=\"price-summary\">\r\n        <div class=\"price-row\"><span>Listing fee<\/span><span>$34.99<\/span><\/div>\r\n        <div class=\"price-row\"><span>Platform service<\/span><span>Included<\/span><\/div>\r\n        <div class=\"price-row\"><span>Listing duration<\/span><span>12 months<\/span><\/div>\r\n        <div class=\"price-row\"><span><strong>Total<\/strong><\/span><span><strong>$34.99<\/strong><\/span><\/div>\r\n      <\/div>\r\n\r\n      <div style=\"background:#FEF9EC;border:1px solid #F9E4A0;border-radius:var(--radius-md);padding:14px 18px;margin-bottom:20px;font-size:12.5px;color:#92610A;line-height:1.6;\">\r\n        <strong>Disclaimer:<\/strong> Mutual Africa Pay Marketplace is a listing and introduction platform only. We do not verify the accuracy of your listing, broker any transaction, or participate in any agreement between parties. By paying and publishing, you confirm your listing is accurate and you have read our <a href=\"https:\/\/mutualafricapay.com\/marketplace-disclaimer\/\" target=\"_blank\" rel=\"noopener\" style=\"color:#92610A;font-weight:700;\">full disclaimer<\/a>.\r\n      <\/div>\r\n\r\n      <div id=\"mo-paypalButtonContainer\" style=\"min-height:50px;\"><\/div>\r\n\r\n      <div class=\"btn-row\" style=\"margin-top:12px;\">\r\n        <button class=\"btn-secondary\" onclick=\"mafpOnboard.goStep(3)\">Back<\/button>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- Success -->\r\n  <div id=\"mo-stepSuccess\" style=\"display:none;\">\r\n    <div class=\"panel\">\r\n      <div class=\"success-wrap\">\r\n        <div class=\"success-icon\"><i class=\"ti ti-check\"><\/i><\/div>\r\n        <div class=\"step-title\" style=\"text-align:center;\">Listing published!<\/div>\r\n        <div class=\"step-sub\" style=\"text-align:center;margin-top:8px;\">Your opportunity is now live on MAP Marketplace. Interested parties can find and enquire about it directly.<\/div>\r\n        <div style=\"display:flex;gap:10px;justify-content:center;margin-top:20px;flex-wrap:wrap;\">\r\n          <a href=\"https:\/\/app.mutualafricapay.com\/marketplace-my-listings.html\" class=\"btn-primary\" style=\"flex:none;padding:13px 20px;\">View my listings<\/a>\r\n          <a href=\"https:\/\/mutualafricapay.com\/marketplace\/\" class=\"btn-secondary\">Back to marketplace<\/a>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n<\/div>\r\n\r\n<script src=\"https:\/\/www.paypal.com\/sdk\/js?client-id=AZBhVV9pn8sSYiA6RB-K0lQfQlmgp7KaVyIhZ8pAi2izcElOOZi8Wib-YT-Ju7qj8h9wW_ONpV17PWvi&currency=USD\"><\/script>\r\n<script>\r\n(function(){\r\n  \/\/ ============================================================\r\n  \/\/ CONFIG \u2014 fill these in before publishing (same values as\r\n  \/\/ config.js in the app)\r\n  \/\/ ============================================================\r\n  const SUPABASE_URL = \"https:\/\/jwpcxobneytohjqtofgi.supabase.co\";\r\n  const SUPABASE_ANON_KEY = \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imp3cGN4b2JuZXl0b2hqcXRvZmdpIiwicm9sZSI6ImFub24iLCJpYXQiOjE3Nzc4ODgyMjUsImV4cCI6MjA5MzQ2NDIyNX0.Gl8rNghtxr64z-Re5Lgy3176M72G9HTyheXSOQ3HPfs\";\r\n\r\n  let currentStep = 0;\r\n  let authMode = 'signup';\r\n  let selectedType = 'business-for-sale';\r\n  let kycIdFile = null;\r\n  let kycSelfieFile = null;\r\n  let kycVerified = false;\r\n  let uploadedImages = [];\r\n  let createdListingId = null;\r\n  let currentUserId = null;\r\n  let videoPitchType = 'link';\r\n  let pitchDeckUrl = null;\r\n  let videoPitchUploadedUrl = null;\r\n\r\n  function showError(m){document.getElementById('mo-errorText').textContent=m;document.getElementById('mo-errorMsg').classList.add('show');window.scrollTo(0,0);}\r\n  function hideError(){document.getElementById('mo-errorMsg').classList.remove('show');}\r\n\r\n  function showToast(text, kind){\r\n    const t = document.getElementById('mo-toast');\r\n    document.getElementById('mo-toastText').textContent = text;\r\n    document.getElementById('mo-toastSpinner').classList.toggle('show', kind === 'loading');\r\n    t.classList.remove('success','error');\r\n    if(kind === 'success') t.classList.add('success');\r\n    if(kind === 'error') t.classList.add('error');\r\n    t.classList.add('show');\r\n  }\r\n  function hideToast(){ document.getElementById('mo-toast').classList.remove('show'); }\r\n\r\n  async function sbFetch(path, opts={}){\r\n    const token = localStorage.getItem('map_access_token');\r\n    const res = await fetch(`${SUPABASE_URL}\/rest\/v1\/${path}`, {...opts, headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':'application\/json', ...(opts.headers||{})}});\r\n    if(!res.ok) throw new Error(`${res.status}: ${await res.text().catch(()=> '')}`);\r\n    const txt = await res.text();\r\n    return txt ? JSON.parse(txt) : null;\r\n  }\r\n\r\n  \/\/ ---------------- Step 0: Account ----------------\r\n  function setAuthMode(mode){\r\n    authMode = mode;\r\n    document.getElementById('mo-tabSignup').classList.toggle('selected', mode==='signup');\r\n    document.getElementById('mo-tabLogin').classList.toggle('selected', mode==='login');\r\n    document.getElementById('mo-authSubmit').innerHTML = mode==='signup'\r\n      ? '<span class=\"spinner\" id=\"mo-authSpinner\"><\/span>Continue <i class=\"ti ti-arrow-right\"><\/i>'\r\n      : '<span class=\"spinner\" id=\"mo-authSpinner\"><\/span>Log in <i class=\"ti ti-arrow-right\"><\/i>';\r\n  }\r\n\r\n  async function submitAuth(){\r\n    hideError();\r\n    const email = document.getElementById('mo-authEmail').value.trim();\r\n    const password = document.getElementById('mo-authPassword').value;\r\n\r\n    if(!email || !password){\r\n      showError('Enter your email and password.');\r\n      return;\r\n    }\r\n    if(authMode === 'signup' && password.length < 8){\r\n      showError('Choose a password of at least 8 characters.');\r\n      return;\r\n    }\r\n\r\n    const btn = document.getElementById('mo-authSubmit');\r\n    btn.disabled = true;\r\n    document.getElementById('mo-authSpinner')?.classList.add('show');\r\n\r\n    try{\r\n      const endpoint = authMode === 'signup' ? 'signup' : 'token?grant_type=password';\r\n      const res = await fetch(`${SUPABASE_URL}\/auth\/v1\/${endpoint}`, {\r\n        method:'POST',\r\n        headers:{apikey:SUPABASE_ANON_KEY, 'Content-Type':'application\/json'},\r\n        body: JSON.stringify({email, password}),\r\n      });\r\n      const body = await res.json();\r\n      if(!res.ok) throw new Error(body.msg || body.error_description || body.error || 'Could not sign you in.');\r\n\r\n      if(!body.access_token){\r\n        \/\/ Signup succeeded but email confirmation is required\r\n        showError('Check your inbox to confirm your email, then come back and log in.');\r\n        btn.disabled = false;\r\n        document.getElementById('mo-authSpinner')?.classList.remove('show');\r\n        setAuthMode('login');\r\n        return;\r\n      }\r\n\r\n      localStorage.setItem('map_access_token', body.access_token);\r\n      currentUserId = body.user?.id;\r\n      if(!currentUserId){\r\n        const userRes = await fetch(`${SUPABASE_URL}\/auth\/v1\/user`, {headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${body.access_token}`}});\r\n        if(userRes.ok){ const u = await userRes.json(); currentUserId = u.id; }\r\n      }\r\n\r\n      await checkExistingKyc();\r\n      goStep(1);\r\n    }catch(e){\r\n      console.error(e);\r\n      showError(e.message);\r\n    }finally{\r\n      btn.disabled = false;\r\n      document.getElementById('mo-authSpinner')?.classList.remove('show');\r\n    }\r\n  }\r\n\r\n  async function checkExistingKyc(){\r\n    try{\r\n      const kyc = await sbFetch(`marketplace_kyc?user_id=eq.${currentUserId}&select=status`);\r\n      if(kyc?.[0]?.status === 'verified'){\r\n        kycVerified = true;\r\n        document.getElementById('mo-kycAlreadyDone').style.display = 'block';\r\n        document.getElementById('mo-kycForm').style.display = 'none';\r\n      }\r\n    }catch(e){ console.error(e); }\r\n  }\r\n\r\n  \/\/ ---------------- Step 1: Listing type ----------------\r\n  function selectType(type){\r\n    selectedType = type;\r\n    document.querySelectorAll('.mafp-onboard .type-card').forEach(c=>c.classList.remove('selected'));\r\n    document.getElementById('mo-type-'+type).classList.add('selected');\r\n  }\r\n\r\n  function showTypeFields(type){\r\n    ['business-for-sale','investment-opportunity','professional-service'].forEach(t=>{\r\n      const el = document.getElementById('mo-fields-'+t);\r\n      if(el) el.style.display = (t===type ? 'block' : 'none');\r\n    });\r\n  }\r\n\r\n  \/\/ ---------------- Step 2: pitch deck \/ video pitch ----------------\r\n  function setVideoPitchType(type){\r\n    videoPitchType = type;\r\n    document.getElementById('mo-vpt-link').classList.toggle('selected', type==='link');\r\n    document.getElementById('mo-vpt-upload').classList.toggle('selected', type==='upload');\r\n    document.getElementById('mo-videoPitchLinkField').style.display = type==='link' ? 'block' : 'none';\r\n    document.getElementById('mo-videoPitchUploadField').style.display = type==='upload' ? 'block' : 'none';\r\n  }\r\n\r\n  async function handlePitchDeckUpload(event){\r\n    const file = event.target.files[0];\r\n    if(!file) return;\r\n    const preview = document.getElementById('mo-pitchDeckPreview');\r\n    preview.textContent = 'Uploading...';\r\n    try{\r\n      const token = localStorage.getItem('map_access_token');\r\n      const ext = file.name.split('.').pop();\r\n      const path = `${currentUserId}\/${Date.now()}.${ext}`;\r\n      const up = await fetch(`${SUPABASE_URL}\/storage\/v1\/object\/pitch-decks\/${path}`, {\r\n        method:'POST',\r\n        headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':file.type||'application\/pdf', 'x-upsert':'true'},\r\n        body:file,\r\n      });\r\n      if(!up.ok) throw new Error(await up.text().catch(()=>'Upload failed'));\r\n      pitchDeckUrl = `${SUPABASE_URL}\/storage\/v1\/object\/public\/pitch-decks\/${path}`;\r\n      preview.innerHTML = `<i class=\"ti ti-file-check\" style=\"color:var(--green);\"><\/i> ${file.name}`;\r\n    }catch(e){\r\n      console.error(e);\r\n      preview.textContent = 'Upload failed \u2014 make sure the pitch-decks storage bucket exists.';\r\n    }\r\n  }\r\n\r\n  async function handleVideoPitchUpload(event){\r\n    const file = event.target.files[0];\r\n    if(!file) return;\r\n    const preview = document.getElementById('mo-videoPitchPreview');\r\n    preview.textContent = 'Uploading...';\r\n    try{\r\n      const token = localStorage.getItem('map_access_token');\r\n      const ext = file.name.split('.').pop();\r\n      const path = `${currentUserId}\/${Date.now()}.${ext}`;\r\n      const up = await fetch(`${SUPABASE_URL}\/storage\/v1\/object\/pitch-videos\/${path}`, {\r\n        method:'POST',\r\n        headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':file.type||'video\/mp4', 'x-upsert':'true'},\r\n        body:file,\r\n      });\r\n      if(!up.ok) throw new Error(await up.text().catch(()=>'Upload failed'));\r\n      videoPitchUploadedUrl = `${SUPABASE_URL}\/storage\/v1\/object\/public\/pitch-videos\/${path}`;\r\n      preview.innerHTML = `<i class=\"ti ti-video\" style=\"color:var(--green);\"><\/i> ${file.name}`;\r\n    }catch(e){\r\n      console.error(e);\r\n      preview.textContent = 'Upload failed \u2014 make sure the pitch-videos storage bucket exists.';\r\n    }\r\n  }\r\n\r\n  \/\/ ---------------- Step 2: images ----------------\r\n  function handleImageUpload(event){\r\n    const files = Array.from(event.target.files).slice(0, 5 - uploadedImages.length);\r\n    files.forEach(file=>{\r\n      const url = URL.createObjectURL(file);\r\n      uploadedImages.push({file, url});\r\n    });\r\n    renderImagePreviews();\r\n  }\r\n  function renderImagePreviews(){\r\n    document.getElementById('mo-imagePreviews').innerHTML = uploadedImages.map((img,i)=>`\r\n      <div class=\"image-preview-wrap\">\r\n        <img decoding=\"async\" class=\"image-preview\" src=\"${img.url}\" alt=\"Preview ${i+1}\">\r\n        <div class=\"image-remove\" onclick=\"mafpOnboard.removeImage(${i})\">\u00d7<\/div>\r\n      <\/div>`).join('');\r\n  }\r\n  function removeImage(idx){ uploadedImages.splice(idx,1); renderImagePreviews(); }\r\n\r\n  \/\/ ---------------- Step 3: KYC ----------------\r\n  function handleKycUpload(type, event){\r\n    const file = event.target.files[0];\r\n    if(!file) return;\r\n    if(type === 'id'){\r\n      kycIdFile = file;\r\n      const box = document.getElementById('mo-kycIdBox');\r\n      box.classList.add('done');\r\n      box.querySelector('i').className = 'ti ti-check';\r\n      box.querySelector('div').textContent = 'ID document uploaded \u2713';\r\n    } else {\r\n      kycSelfieFile = file;\r\n      const box = document.getElementById('mo-kycSelfieBox');\r\n      box.classList.add('done');\r\n      box.querySelector('i').className = 'ti ti-check';\r\n      box.querySelector('div').textContent = 'Selfie uploaded \u2713';\r\n    }\r\n  }\r\n\r\n  async function submitKyc(){\r\n    if(!validate(3)) return;\r\n    if(kycVerified){ goStep(4); return; }\r\n\r\n    const btn = document.getElementById('mo-kycNextBtn');\r\n    const spinner = document.getElementById('mo-kycSpinner');\r\n    btn.disabled = true; spinner.classList.add('show');\r\n    document.getElementById('mo-kycProcessing').style.display = 'block';\r\n    hideError();\r\n\r\n    const token = localStorage.getItem('map_access_token');\r\n    let idUrl = null, selfieUrl = null;\r\n\r\n    \/\/ Upload ID document \u2014 failure here is logged, not blocking.\r\n    try{\r\n      const idExt = kycIdFile.name.split('.').pop();\r\n      const idPath = `marketplace-kyc\/${currentUserId}\/id.${idExt}`;\r\n      const idUp = await fetch(`${SUPABASE_URL}\/storage\/v1\/object\/marketplace-kyc\/${idPath}`, {\r\n        method:'POST', headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':kycIdFile.type||'image\/jpeg', 'x-upsert':'true'}, body:kycIdFile,\r\n      });\r\n      if(idUp.ok) idUrl = `${SUPABASE_URL}\/storage\/v1\/object\/public\/marketplace-kyc\/${idPath}`;\r\n      else console.error('ID upload failed:', await idUp.text().catch(()=>''));\r\n    }catch(e){ console.error('ID upload error:', e); }\r\n\r\n    \/\/ Upload selfie \u2014 independent of the ID upload's outcome.\r\n    try{\r\n      const selfieExt = kycSelfieFile.name.split('.').pop();\r\n      const selfiePath = `marketplace-kyc\/${currentUserId}\/selfie.${selfieExt}`;\r\n      const selfieUp = await fetch(`${SUPABASE_URL}\/storage\/v1\/object\/marketplace-kyc\/${selfiePath}`, {\r\n        method:'POST', headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':kycSelfieFile.type||'image\/jpeg', 'x-upsert':'true'}, body:kycSelfieFile,\r\n      });\r\n      if(selfieUp.ok) selfieUrl = `${SUPABASE_URL}\/storage\/v1\/object\/public\/marketplace-kyc\/${selfiePath}`;\r\n      else console.error('Selfie upload failed:', await selfieUp.text().catch(()=>''));\r\n    }catch(e){ console.error('Selfie upload error:', e); }\r\n\r\n    \/\/ Save whatever we have \u2014 a human reviews this afterward, so a\r\n    \/\/ partially-missing upload is something for them to catch, not a\r\n    \/\/ reason to trap the user on this step.\r\n    try{\r\n      const existing = await sbFetch(`marketplace_kyc?user_id=eq.${currentUserId}&select=id`);\r\n      const kycPayload = {\r\n        user_id: currentUserId,\r\n        full_name: document.getElementById('mo-kycFullName').value.trim(),\r\n        id_number: document.getElementById('mo-kycIdNumber').value.trim(),\r\n        date_of_birth: document.getElementById('mo-kycDob').value,\r\n        nationality: document.getElementById('mo-kycNationality').value.trim(),\r\n        id_document_url: idUrl,\r\n        selfie_url: selfieUrl,\r\n        status: 'submitted',\r\n        submitted_at: new Date().toISOString(),\r\n      };\r\n      if(existing?.length){\r\n        await sbFetch(`marketplace_kyc?id=eq.${existing[0].id}`, {method:'PATCH', headers:{Prefer:'return=minimal'}, body:JSON.stringify(kycPayload)});\r\n      } else {\r\n        await sbFetch('marketplace_kyc', {method:'POST', headers:{Prefer:'return=minimal'}, body:JSON.stringify(kycPayload)});\r\n      }\r\n    }catch(e){ console.error('marketplace_kyc save failed:', e); }\r\n\r\n    \/\/ Always move on \u2014 verification is manual, after submission, not\r\n    \/\/ a gate the user waits at.\r\n    kycVerified = true;\r\n    document.getElementById('mo-kycProcessing').style.display = 'none';\r\n    goStep(4);\r\n  }\r\n\r\n  \/\/ ---------------- Step 4: listing + payment ----------------\r\n  async function saveListing(){\r\n    const payload = {\r\n      user_id: currentUserId,\r\n      listing_type: selectedType,\r\n      title: document.getElementById('mo-listingTitle').value.trim(),\r\n      description: document.getElementById('mo-listingDesc').value.trim(),\r\n      country: document.getElementById('mo-listingCountry').value,\r\n      city: document.getElementById('mo-listingCity').value.trim() || null,\r\n      industry: document.getElementById('mo-listingIndustry').value || null,\r\n      status: 'pending_payment',\r\n      asking_price: parseFloat(document.getElementById('mo-askingPrice')?.value) || null,\r\n      annual_revenue: parseFloat(document.getElementById('mo-annualRevenue')?.value) || null,\r\n      annual_profit: parseFloat(document.getElementById('mo-annualProfit')?.value) || null,\r\n      employees: parseInt(document.getElementById('mo-employees')?.value) || null,\r\n      established_year: parseInt(document.getElementById('mo-establishedYear')?.value) || null,\r\n      reason_for_sale: document.getElementById('mo-reasonForSale')?.value.trim() || null,\r\n      funding_amount: parseFloat(document.getElementById('mo-fundingAmount')?.value) || null,\r\n      equity_offered: parseFloat(document.getElementById('mo-equityOffered')?.value) || null,\r\n      funding_stage: document.getElementById('mo-fundingStage')?.value || null,\r\n      service_type: document.getElementById('mo-serviceType')?.value.trim() || null,\r\n      rate_from: parseFloat(document.getElementById('mo-rateFrom')?.value) || null,\r\n      rate_period: document.getElementById('mo-ratePeriod')?.value || null,\r\n      asking_currency: 'USD',\r\n      funding_currency: 'USD',\r\n      rate_currency: 'USD',\r\n      expires_at: new Date(Date.now() + 365*24*60*60*1000).toISOString(),\r\n      pitch_deck_url: selectedType==='investment-opportunity' ? pitchDeckUrl : null,\r\n      video_pitch_url: selectedType==='investment-opportunity' ? (videoPitchType==='link' ? (document.getElementById('mo-videoPitchUrlInput').value.trim()||null) : videoPitchUploadedUrl) : null,\r\n      video_pitch_type: selectedType==='investment-opportunity' ? videoPitchType : null,\r\n    };\r\n    const created = await sbFetch('marketplace_listings', {method:'POST', headers:{Prefer:'return=representation'}, body:JSON.stringify(payload)});\r\n    return created[0].id;\r\n  }\r\n\r\n  async function uploadListingImages(listingId){\r\n    if(!uploadedImages.length) return [];\r\n    const token = localStorage.getItem('map_access_token');\r\n    const urls = [];\r\n    for(let i=0;i<uploadedImages.length;i++){\r\n      const {file} = uploadedImages[i];\r\n      const ext = file.name.split('.').pop();\r\n      const path = `marketplace-listings\/${listingId}\/image-${i}.${ext}`;\r\n      const up = await fetch(`${SUPABASE_URL}\/storage\/v1\/object\/marketplace-listings\/${path}`, {\r\n        method:'POST', headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`, 'Content-Type':file.type||'image\/jpeg', 'x-upsert':'true'}, body:file,\r\n      });\r\n      if(up.ok) urls.push(`${SUPABASE_URL}\/storage\/v1\/object\/public\/marketplace-listings\/${path}`);\r\n    }\r\n    if(urls.length){\r\n      await sbFetch(`marketplace_listings?id=eq.${listingId}`, {method:'PATCH', headers:{Prefer:'return=minimal'}, body:JSON.stringify({images:urls})});\r\n    }\r\n    return urls;\r\n  }\r\n\r\n  function renderPayPalButton(){\r\n    document.getElementById('mo-paypalButtonContainer').innerHTML = '';\r\n    window.paypal.Buttons({\r\n      style:{shape:'rect', color:'blue', layout:'vertical', label:'pay'},\r\n      createOrder: (data, actions) => actions.order.create({\r\n        purchase_units:[{\r\n          amount:{value:'34.99', currency_code:'USD'},\r\n          description:'MAP Marketplace listing fee \u2014 12 months',\r\n        }],\r\n      }),\r\n      onApprove: async (data, actions) => {\r\n        showToast('Processing payment...', 'loading');\r\n        try{\r\n          const details = await actions.order.capture();\r\n          createdListingId = await saveListing();\r\n          await uploadListingImages(createdListingId);\r\n          await sbFetch(`marketplace_listings?id=eq.${createdListingId}`, {\r\n            method:'PATCH',\r\n            headers:{Prefer:'return=minimal'},\r\n            body: JSON.stringify({payment_status:'paid', paypal_order_id: details.id, status:'active'}),\r\n          });\r\n          showToast('Listing published!', 'success');\r\n          document.getElementById('mo-step4').style.display = 'none';\r\n          document.getElementById('mo-stepSuccess').style.display = 'block';\r\n          for(let i=0;i<=4;i++) document.getElementById('mo-ps'+i).classList.add('done');\r\n          document.getElementById('mo-progLabel').textContent = 'Complete';\r\n        }catch(e){\r\n          console.error(e);\r\n          showToast('Payment received but listing could not be saved \u2014 contact support with your PayPal order ID: '+data.orderID, 'error');\r\n        }\r\n      },\r\n      onError: (err) => { console.error('PayPal error', err); },\r\n      onCancel: () => { hideToast(); },\r\n    }).render('#mo-paypalButtonContainer');\r\n  }\r\n\r\n  \/\/ ---------------- Navigation ----------------\r\n  function updateProgress(step){\r\n    for(let i=0;i<=4;i++){\r\n      const el = document.getElementById('mo-ps'+i);\r\n      el.classList.remove('done','active');\r\n      if(i<step) el.classList.add('done');\r\n      else if(i===step) el.classList.add('active');\r\n    }\r\n    document.getElementById('mo-progLabel').textContent = `Step ${step+1} of 5`;\r\n  }\r\n\r\n  function validate(step){\r\n    hideError();\r\n    if(step===2){\r\n      if(!document.getElementById('mo-listingTitle').value.trim()){showError('Add a title for your listing.');return false;}\r\n      if(!document.getElementById('mo-listingDesc').value.trim() || document.getElementById('mo-listingDesc').value.trim().length<50){showError('Description must be at least 50 characters.');return false;}\r\n      if(!document.getElementById('mo-listingCountry').value){showError('Select a country.');return false;}\r\n    }\r\n    if(step===3 && !kycVerified){\r\n      if(!document.getElementById('mo-kycFullName').value.trim()){showError('Full name is required for identity verification.');return false;}\r\n      if(!document.getElementById('mo-kycIdNumber').value.trim()){showError('ID or passport number is required.');return false;}\r\n      if(!document.getElementById('mo-kycDob').value){showError('Date of birth is required.');return false;}\r\n      if(!document.getElementById('mo-kycNationality').value.trim()){showError('Nationality is required.');return false;}\r\n      if(!kycIdFile){showError('Upload a photo of your ID document.');return false;}\r\n      if(!kycSelfieFile){showError('Upload a selfie holding your ID document.');return false;}\r\n    }\r\n    return true;\r\n  }\r\n\r\n  function goStep(target){\r\n    if(target>currentStep && !validate(currentStep)) return;\r\n    document.getElementById('mo-step'+currentStep).style.display = 'none';\r\n    currentStep = target;\r\n    document.getElementById('mo-step'+currentStep).style.display = 'block';\r\n    if(currentStep===2) showTypeFields(selectedType);\r\n    if(currentStep===4) renderPayPalButton();\r\n    updateProgress(currentStep);\r\n    window.scrollTo(0,0);\r\n  }\r\n\r\n  async function init(){\r\n    const token = localStorage.getItem('map_access_token');\r\n    if(token){\r\n      try{\r\n        const userRes = await fetch(`${SUPABASE_URL}\/auth\/v1\/user`, {headers:{apikey:SUPABASE_ANON_KEY, Authorization:`Bearer ${token}`}});\r\n        if(userRes.ok){\r\n          const u = await userRes.json();\r\n          currentUserId = u.id;\r\n          await checkExistingKyc();\r\n          goStep(1);\r\n          return;\r\n        }\r\n      }catch(e){ console.error(e); }\r\n    }\r\n    updateProgress(0);\r\n  }\r\n\r\n  window.mafpOnboard = {\r\n    setAuthMode, submitAuth, selectType, goStep,\r\n    handlePitchDeckUpload, handleVideoPitchUpload, setVideoPitchType,\r\n    handleImageUpload, removeImage, handleKycUpload, submitKyc,\r\n  };\r\n\r\n  init();\r\n})();\r\n<\/script>\r\n<\/div>\t\t\t\t<\/div>\n\t\t\n<\/div>\n\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>List an opportunity Step 1 of 5 Create your account You&#8217;ll use this to manage your listing and hear from buyers or investors. Sign up Log in Email * Password * Continue What are you listing? Choose the type of opportunity you want to publish. \ud83c\udfe2 Business for sale Selling an existing business or company \ud83d\udd17 Franchise opportunity Offering or<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":7388,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-7436","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/pages\/7436","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/comments?post=7436"}],"version-history":[{"count":7,"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/pages\/7436\/revisions"}],"predecessor-version":[{"id":7457,"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/pages\/7436\/revisions\/7457"}],"up":[{"embeddable":true,"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/pages\/7388"}],"wp:attachment":[{"href":"https:\/\/mutualafricapay.com\/marketplace\/wp-json\/wp\/v2\/media?parent=7436"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}