{"id":28,"date":"2026-07-30T14:03:10","date_gmt":"2026-07-30T14:03:10","guid":{"rendered":"https:\/\/mahall.org\/?page_id=28"},"modified":"2026-07-30T14:04:20","modified_gmt":"2026-07-30T14:04:20","slug":"onboard","status":"publish","type":"page","link":"https:\/\/mahall.org\/index.php\/onboard\/","title":{"rendered":"onboard"},"content":{"rendered":"\n<!--\n  MAHALL \u2014 owner onboarding wizard (v0.3)\n  ------------------------------------------------------------------\n  HOW TO USE\n  Paste into a WordPress page as Custom HTML, e.g. mahall.org\/list-your-venue\n\n  WHAT'S REAL vs DEMO RIGHT NOW\n  - Step navigation, validation, photo reordering\/cover selection,\n    video preview, embed-link preview, and the 360 viewer all work\n    with zero backend \u2014 pure front-end, works the moment you paste it.\n  - Actual file PERSISTENCE (saving photos\/video to the server) is not\n    wired yet \u2014 previews use local object URLs that vanish on refresh.\n    Real storage needs a WordPress REST call to wp\/v2\/media with a\n    logged-in nonce, OR a custom endpoint that streams to your media\n    library. That's scheduled for the v1.0 integration pass once the\n    Venue custom post type exists \u2014 there's no good \"owner dashboard\"\n    to attach uploads to before then.\n  - Final submit posts to \/wp-json\/mahall\/v1\/owner-onboarding with a\n    local fallback so the flow still feels complete today.\n\n  3D TOUR \u2014 TWO PATHS FOR OWNERS\n  - Path A: paste an embed URL from Matterport \/ Kuula \/ iStaging \/\n    EyeSpy360 (for owners who've paid for a professional scan).\n  - Path B: upload one 360\u00b0\/equirectangular photo (shot on any modern\n    phone's panorama or 360 mode) and Mahall renders an interactive\n    look-around viewer itself, via the open-source Pannellum library\n    (loaded from a public CDN \u2014 no account or subscription needed).\n------------------------------------------------------------------- -->\n<style>\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Fraunces:ital,opsz,wght@0,9..144,300..700;1,9..144,400..600&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');\n\n#mahall-owner{\n  --bg:#16130F; --bg-raised:#1E1A14; --bg-card:#241F18;\n  --ink:#F3ECE0; --ink-soft:#BBAE99; --ink-faint:#7A7163;\n  --gold:#D98E33; --gold-soft:#E8B978;\n  --teal:#2F6F62; --teal-soft:#5C988B;\n  --line:rgba(243,236,224,0.12);\n  --radius:14px;\n  font-family:'Inter',sans-serif; background:var(--bg); color:var(--ink);\n  -webkit-font-smoothing:antialiased; padding-bottom:60px;\n}\n#mahall-owner *{box-sizing:border-box; margin:0; padding:0;}\n#mahall-owner a{color:inherit;}\n#mahall-owner .wrap{max-width:760px; margin:0 auto; padding:0 24px;}\n#mahall-owner h1,#mahall-owner h2,#mahall-owner h3{font-family:'Fraunces',serif; font-weight:500; color:var(--ink); line-height:1.15;}\n#mahall-owner p{color:var(--ink-soft); line-height:1.6;}\n#mahall-owner .eyebrow{font-family:'JetBrains Mono',monospace; font-size:11px; letter-spacing:.12em; text-transform:uppercase; color:var(--gold-soft);}\n#mahall-owner .star{position:relative; width:16px; height:16px; flex:none;}\n#mahall-owner .star span{position:absolute; inset:0; border:1.4px solid var(--gold);}\n#mahall-owner .star span:last-child{transform:rotate(45deg);}\n\n#mahall-owner .topbar{display:flex; align-items:center; gap:10px; padding:24px 0 8px;}\n#mahall-owner .topbar .logo{font-family:'Fraunces',serif; font-size:18px; display:flex; gap:8px; align-items:center;}\n\n\/* stepper *\/\n#mahall-owner .stepper{display:flex; gap:8px; margin:24px 0 36px;}\n#mahall-owner .step-pill{\n  flex:1; text-align:center; padding:10px 6px; border-radius:10px; font-size:12px;\n  font-family:'JetBrains Mono',monospace; letter-spacing:.04em; color:var(--ink-faint);\n  background:var(--bg-card); border:1px solid var(--line); position:relative;\n}\n#mahall-owner .step-pill.active{color:var(--gold-soft); border-color:var(--gold);}\n#mahall-owner .step-pill.done{color:var(--teal-soft); border-color:var(--teal-soft);}\n\n#mahall-owner .panel{display:none;}\n#mahall-owner .panel.active{display:block;}\n#mahall-owner .card{background:var(--bg-raised); border:1px solid var(--line); border-radius:var(--radius); padding:28px;}\n#mahall-owner .card + .card{margin-top:18px;}\n\n#mahall-owner .field{margin-bottom:16px;}\n#mahall-owner .field label{display:block; font-size:13px; color:var(--ink); margin-bottom:6px; font-weight:500;}\n#mahall-owner .field .hint{font-size:12px; color:var(--ink-faint); margin-top:4px; display:block;}\n#mahall-owner .field input, #mahall-owner .field select, #mahall-owner .field textarea{\n  width:100%; background:var(--bg); border:1px solid var(--line); border-radius:8px;\n  padding:11px 13px; color:var(--ink); font-family:'Inter',sans-serif; font-size:14px; outline:none;\n}\n#mahall-owner .field input:focus, #mahall-owner .field select:focus, #mahall-owner .field textarea:focus{border-color:var(--gold-soft);}\n#mahall-owner .field-row{display:flex; gap:14px;}\n#mahall-owner .field-row .field{flex:1;}\n\n#mahall-owner .btn{display:inline-flex; align-items:center; gap:8px; font-weight:500; font-size:14px; padding:12px 24px; border-radius:999px; border:1px solid var(--line); cursor:pointer; background:transparent; color:var(--ink); transition:border-color .2s, transform .15s;}\n#mahall-owner .btn:hover{border-color:var(--gold-soft); transform:translateY(-1px);}\n#mahall-owner .btn-solid{background:var(--gold); border-color:var(--gold); color:#1A1305;}\n#mahall-owner .btn-solid:hover{background:var(--gold-soft);}\n#mahall-owner .btn[disabled]{opacity:.4; cursor:not-allowed; transform:none;}\n#mahall-owner .nav-row{display:flex; justify-content:space-between; margin-top:24px;}\n\n\/* media tabs within step 2 *\/\n#mahall-owner .media-tabs{display:flex; gap:8px; margin-bottom:18px;}\n#mahall-owner .mtab{font-size:12px; font-family:'JetBrains Mono',monospace; padding:7px 14px; border-radius:999px; border:1px solid var(--line); color:var(--ink-soft); cursor:pointer; background:transparent;}\n#mahall-owner .mtab.active{border-color:var(--gold); color:var(--gold-soft);}\n#mahall-owner .mpanel{display:none;}\n#mahall-owner .mpanel.active{display:block;}\n\n#mahall-owner .dropzone{\n  border:1.5px dashed var(--line); border-radius:var(--radius); padding:32px; text-align:center;\n  cursor:pointer; transition:border-color .2s, background .2s;\n}\n#mahall-owner .dropzone:hover, #mahall-owner .dropzone.drag{border-color:var(--gold-soft); background:rgba(217,142,51,.05);}\n#mahall-owner .dropzone .ic{font-size:26px; display:block; margin-bottom:10px;}\n#mahall-owner .dropzone b{font-size:14px; color:var(--ink); display:block; margin-bottom:4px;}\n#mahall-owner .dropzone span{font-size:12px; color:var(--ink-faint);}\n\n#mahall-owner .photo-grid{display:grid; grid-template-columns:repeat(4,1fr); gap:10px; margin-top:16px;}\n#mahall-owner .photo-item{\n  position:relative; aspect-ratio:1; border-radius:10px; overflow:hidden; border:1px solid var(--line);\n  cursor:grab; background:var(--bg-card);\n}\n#mahall-owner .photo-item img, #mahall-owner .photo-item video{width:100%; height:100%; object-fit:cover; display:block;}\n#mahall-owner .photo-item.dragging{opacity:.4;}\n#mahall-owner .photo-item .cover-flag{\n  position:absolute; top:6px; left:6px; font-size:10px; font-family:'JetBrains Mono',monospace;\n  background:var(--gold); color:#1A1305; padding:2px 7px; border-radius:999px; display:none;\n}\n#mahall-owner .photo-item.is-cover .cover-flag{display:block;}\n#mahall-owner .photo-item .actions{position:absolute; bottom:6px; right:6px; display:flex; gap:4px;}\n#mahall-owner .photo-item button{\n  width:24px; height:24px; border-radius:50%; border:none; background:rgba(22,19,15,0.75); color:var(--ink);\n  font-size:12px; cursor:pointer; display:flex; align-items:center; justify-content:center;\n}\n#mahall-owner .photo-item button:hover{background:var(--gold); color:#1A1305;}\n#mahall-owner .empty-note{font-size:12px; color:var(--ink-faint); margin-top:10px;}\n\n#mahall-owner .video-input-row{display:flex; gap:10px; margin-top:14px;}\n#mahall-owner .video-input-row input{flex:1;}\n#mahall-owner .video-preview{margin-top:16px; border-radius:var(--radius); overflow:hidden; border:1px solid var(--line); display:none;}\n#mahall-owner .video-preview.show{display:block;}\n#mahall-owner .video-preview video, #mahall-owner .video-preview iframe{width:100%; height:280px; display:block; border:none;}\n\n#mahall-owner .tour-sub-tabs{display:flex; gap:6px; margin-bottom:14px;}\n#mahall-owner .tour-sub{font-size:12px; padding:6px 12px; border-radius:8px; border:1px solid var(--line); color:var(--ink-soft); cursor:pointer; background:var(--bg-card);}\n#mahall-owner .tour-sub.active{border-color:var(--teal-soft); color:var(--teal-soft);}\n#mahall-owner .tour-pane{display:none;}\n#mahall-owner .tour-pane.active{display:block;}\n#mahall-owner .pano-viewer{width:100%; height:280px; border-radius:var(--radius); border:1px solid var(--line); margin-top:14px; display:none; overflow:hidden;}\n#mahall-owner .pano-viewer.show{display:block;}\n#mahall-owner .tour-frame{width:100%; height:280px; border-radius:var(--radius); border:1px solid var(--line); margin-top:14px; display:none;}\n#mahall-owner .tour-frame.show{display:block;}\n#mahall-owner .provider-note{font-size:12px; color:var(--ink-faint); margin-top:10px; line-height:1.6;}\n#mahall-owner .provider-note a{color:var(--gold-soft); text-decoration:underline;}\n\n\/* plan cards *\/\n#mahall-owner .plan-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:14px;}\n#mahall-owner .plan-card{background:var(--bg-card); border:1.5px solid var(--line); border-radius:var(--radius); padding:20px; cursor:pointer; transition:border-color .2s;}\n#mahall-owner .plan-card.selected{border-color:var(--gold);}\n#mahall-owner .plan-card h3{font-size:16px; margin-bottom:4px;}\n#mahall-owner .plan-card .price{font-family:'Fraunces',serif; font-size:22px; color:var(--gold-soft); margin-bottom:12px; display:block;}\n#mahall-owner .plan-card ul{list-style:none; font-size:12px; color:var(--ink-soft);}\n#mahall-owner .plan-card li{padding:4px 0;}\n#mahall-owner .badge{font-size:10px; font-family:'JetBrains Mono',monospace; background:var(--teal); color:#0D1F1A; padding:3px 8px; border-radius:999px; margin-bottom:10px; display:inline-block;}\n\n\/* review *\/\n#mahall-owner .review-row{display:flex; justify-content:space-between; padding:10px 0; border-bottom:1px solid var(--line); font-size:13px;}\n#mahall-owner .review-row span:first-child{color:var(--ink-faint);}\n#mahall-owner .terms{display:flex; align-items:flex-start; gap:10px; margin-top:18px; font-size:12px; color:var(--ink-soft);}\n#mahall-owner .submit-msg{font-size:13px; margin-top:14px; display:none;}\n#mahall-owner .submit-msg.show{display:block;}\n#mahall-owner .submit-msg.ok{color:var(--teal-soft);}\n\n@media (max-width:640px){\n  #mahall-owner .photo-grid{grid-template-columns:repeat(3,1fr);}\n  #mahall-owner .field-row{flex-direction:column;}\n  #mahall-owner .plan-grid{grid-template-columns:1fr;}\n  #mahall-owner .stepper{flex-wrap:wrap;}\n}\n<\/style>\n\n<div id=\"mahall-owner\">\n  <div class=\"wrap\">\n    <div class=\"topbar\">\n      <div class=\"logo\"><span class=\"star\"><span><\/span><span><\/span><\/span> Mahall<\/div>\n    <\/div>\n    <span class=\"eyebrow\">List your venue<\/span>\n    <h1 style=\"font-size:26px; margin:6px 0 4px;\">Get discovered by the right events<\/h1>\n    <p>Four short steps. You can save and come back any time.<\/p>\n\n    <div class=\"stepper\" id=\"stepper\"><\/div>\n\n    <!-- STEP 1 -->\n    <div class=\"panel active\" data-step=\"1\">\n      <div class=\"card\">\n        <h2 style=\"font-size:18px; margin-bottom:18px;\">Venue basics<\/h2>\n        <div class=\"field\">\n          <label for=\"o-name\">Venue name<\/label>\n          <input id=\"o-name\" placeholder=\"e.g. Riad Zahra\" required>\n        <\/div>\n        <div class=\"field-row\">\n          <div class=\"field\">\n            <label for=\"o-cat\">Category<\/label>\n            <select id=\"o-cat\">\n              <option value=\"heritage\">Heritage &#038; cultural<\/option>\n              <option value=\"resort\">Hospitality &#038; resort<\/option>\n              <option value=\"private\">Private &#038; exclusive<\/option>\n              <option value=\"nature\">Nature &#038; adventure<\/option>\n              <option value=\"coastal\">Coastal &#038; leisure<\/option>\n              <option value=\"urban\">Urban &#038; lifestyle<\/option>\n              <option value=\"corporate\">Corporate &#038; MICE<\/option>\n              <option value=\"entertainment\">Entertainment &#038; production<\/option>\n            <\/select>\n          <\/div>\n          <div class=\"field\">\n            <label for=\"o-city\">City \/ region<\/label>\n            <input id=\"o-city\" placeholder=\"e.g. Marrakech\">\n          <\/div>\n        <\/div>\n        <div class=\"field\">\n          <label for=\"o-capacity\">Maximum guest capacity<\/label>\n          <input id=\"o-capacity\" type=\"number\" min=\"1\" placeholder=\"e.g. 80\">\n        <\/div>\n        <div class=\"field\">\n          <label for=\"o-desc\">Description<\/label>\n          <textarea id=\"o-desc\" rows=\"4\" placeholder=\"What makes this venue worth booking?\"><\/textarea>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- STEP 2 -->\n    <div class=\"panel\" data-step=\"2\">\n      <div class=\"card\">\n        <h2 style=\"font-size:18px; margin-bottom:6px;\">Photos, video &#038; 3D tour<\/h2>\n        <p style=\"margin-bottom:18px;\">Listings with a complete gallery get noticeably more inquiries \u2014 aim for at least 6 photos.<\/p>\n\n        <div class=\"media-tabs\">\n          <button class=\"mtab active\" data-mtab=\"photos\">Photos<\/button>\n          <button class=\"mtab\" data-mtab=\"video\">Video<\/button>\n          <button class=\"mtab\" data-mtab=\"tour\">3D tour<\/button>\n        <\/div>\n\n        <div class=\"mpanel active\" data-mpanel=\"photos\">\n          <div class=\"dropzone\" id=\"photo-drop\">\n            <span class=\"ic\">&#128247;<\/span>\n            <b>Drag photos here, or click to browse<\/b>\n            <span>JPG or PNG, multiple at once. Drag thumbnails below to reorder \u2014 the first one is your cover photo.<\/span>\n          <\/div>\n          <input type=\"file\" id=\"photo-input\" accept=\"image\/*\" multiple style=\"display:none;\">\n          <div class=\"photo-grid\" id=\"photo-grid\"><\/div>\n          <p class=\"empty-note\" id=\"photo-empty\">No photos yet.<\/p>\n        <\/div>\n\n        <div class=\"mpanel\" data-mpanel=\"video\">\n          <div class=\"dropzone\" id=\"video-drop\">\n            <span class=\"ic\">&#127909;<\/span>\n            <b>Drag a video file here, or click to browse<\/b>\n            <span>MP4 recommended, under 200MB &mdash; or paste a YouTube \/ Vimeo link instead<\/span>\n          <\/div>\n          <input type=\"file\" id=\"video-input\" accept=\"video\/*\" style=\"display:none;\">\n          <div class=\"video-input-row\">\n            <input id=\"video-url\" placeholder=\"https:\/\/youtube.com\/watch?v=... or vimeo.com\/...\">\n            <button class=\"btn\" id=\"video-url-btn\" type=\"button\">Preview<\/button>\n          <\/div>\n          <div class=\"video-preview\" id=\"video-preview\"><\/div>\n        <\/div>\n\n        <div class=\"mpanel\" data-mpanel=\"tour\">\n          <p style=\"margin-bottom:14px;\">Already had a professional 3D scan done? Paste the link. Shooting it yourself on a phone? Upload a single 360&deg; photo instead &mdash; we&#8217;ll make it interactive.<\/p>\n          <div class=\"tour-sub-tabs\">\n            <button class=\"tour-sub active\" data-tsub=\"link\">Paste embed link<\/button>\n            <button class=\"tour-sub\" data-tsub=\"upload\">Upload 360&deg; photo<\/button>\n          <\/div>\n\n          <div class=\"tour-pane active\" data-tpane=\"link\">\n            <div class=\"field\">\n              <label for=\"tour-url\">Matterport \/ Kuula \/ iStaging \/ EyeSpy360 embed URL<\/label>\n              <input id=\"tour-url\" placeholder=\"https:\/\/my.matterport.com\/show\/?m=...\">\n            <\/div>\n            <iframe class=\"tour-frame\" id=\"tour-frame\"><\/iframe>\n            <p class=\"provider-note\">Don&#8217;t have one yet? <a href=\"https:\/\/matterport.com\" target=\"_blank\" rel=\"noopener\">Matterport<\/a> and <a href=\"https:\/\/kuula.co\" target=\"_blank\" rel=\"noopener\">Kuula<\/a> both offer scan-on-demand services in major Moroccan cities, typically priced per square meter.<\/p>\n          <\/div>\n\n          <div class=\"tour-pane\" data-tpane=\"upload\">\n            <div class=\"dropzone\" id=\"pano-drop\">\n              <span class=\"ic\">&#127759;<\/span>\n              <b>Upload one equirectangular 360&deg; photo<\/b>\n              <span>Most phones can shoot this in &#8220;Panorama&#8221; or &#8220;360&#8221; camera mode &mdash; export as a single wide JPG<\/span>\n            <\/div>\n            <input type=\"file\" id=\"pano-input\" accept=\"image\/*\" style=\"display:none;\">\n            <div class=\"pano-viewer\" id=\"pano-viewer\"><\/div>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- STEP 3 -->\n    <div class=\"panel\" data-step=\"3\">\n      <div class=\"card\">\n        <h2 style=\"font-size:18px; margin-bottom:18px;\">Choose your plan<\/h2>\n        <div class=\"plan-grid\" id=\"plan-grid\">\n          <div class=\"plan-card\" data-plan=\"free\">\n            <h3>Free listing<\/h3><span class=\"price\">0 MAD<\/span>\n            <ul><li>&#10003; Basic listing page<\/li><li>&#10003; Up to 6 photos<\/li><li>&#10003; Standard search placement<\/li><\/ul>\n          <\/div>\n          <div class=\"plan-card\" data-plan=\"featured\">\n            <span class=\"badge\">Most popular<\/span>\n            <h3>Featured<\/h3><span class=\"price\">499 MAD\/mo<\/span>\n            <ul><li>&#10003; Everything in Free<\/li><li>&#10003; Unlimited photos, video, 3D tour<\/li><li>&#10003; Priority placement in category<\/li><li>&#10003; Lead analytics<\/li><\/ul>\n          <\/div>\n          <div class=\"plan-card\" data-plan=\"premium\">\n            <h3>Premium<\/h3><span class=\"price\">1,200 MAD\/mo<\/span>\n            <ul><li>&#10003; Everything in Featured<\/li><li>&#10003; Homepage rotation<\/li><li>&#10003; Dedicated account support<\/li><\/ul>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <!-- STEP 4 -->\n    <div class=\"panel\" data-step=\"4\">\n      <div class=\"card\">\n        <h2 style=\"font-size:18px; margin-bottom:14px;\">Review &#038; submit<\/h2>\n        <div id=\"review-body\"><\/div>\n        <label class=\"terms\"><input type=\"checkbox\" id=\"o-terms\"> I confirm I&#8217;m authorized to list this venue and the details provided are accurate.<\/label>\n        <button class=\"btn btn-solid\" id=\"submit-btn\" style=\"margin-top:18px;\" type=\"button\">Submit for review &#10148;<\/button>\n        <div class=\"submit-msg\" id=\"submit-msg\"><\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"nav-row\">\n      <button class=\"btn\" id=\"back-btn\">&#8592; Back<\/button>\n      <button class=\"btn btn-solid\" id=\"next-btn\">Continue &#8594;<\/button>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/pannellum@2.5.6\/build\/pannellum.min.js\"><\/script>\n<link rel=\"stylesheet\" href=\"https:\/\/cdn.jsdelivr.net\/npm\/pannellum@2.5.6\/build\/pannellum.css\">\n\n<script>\n(function(){\n  var STEPS = ['Basics','Media','Plan','Review'];\n  var current = 1;\n  var photos = []; \/\/ {url, file}\n  var selectedPlan = null;\n\n  function renderStepper(){\n    var el = document.getElementById('stepper');\n    el.innerHTML = STEPS.map(function(label, i){\n      var n = i + 1;\n      var cls = n === current ? 'active' : (n < current ? 'done' : '');\n      return '<div class=\"step-pill ' + cls + '\">' + n + '. ' + label + '<\/div>';\n    }).join('');\n  }\n\n  function showStep(n){\n    current = n;\n    document.querySelectorAll('.panel').forEach(function(p){\n      p.classList.toggle('active', parseInt(p.getAttribute('data-step')) === n);\n    });\n    document.getElementById('back-btn').disabled = (n === 1);\n    document.getElementById('next-btn').style.display = (n === STEPS.length) ? 'none' : '';\n    if(n === STEPS.length) renderReview();\n    renderStepper();\n    window.scrollTo({top:0, behavior:'smooth'});\n  }\n\n  document.getElementById('next-btn').addEventListener('click', function(){\n    if(current < STEPS.length) showStep(current + 1);\n  });\n  document.getElementById('back-btn').addEventListener('click', function(){\n    if(current > 1) showStep(current - 1);\n  });\n\n  \/* ---------- media tabs ---------- *\/\n  document.querySelectorAll('.mtab').forEach(function(tab){\n    tab.addEventListener('click', function(){\n      document.querySelectorAll('.mtab').forEach(function(t){ t.classList.remove('active'); });\n      document.querySelectorAll('.mpanel').forEach(function(p){ p.classList.remove('active'); });\n      tab.classList.add('active');\n      document.querySelector('.mpanel[data-mpanel=\"' + tab.getAttribute('data-mtab') + '\"]').classList.add('active');\n    });\n  });\n  document.querySelectorAll('.tour-sub').forEach(function(tab){\n    tab.addEventListener('click', function(){\n      document.querySelectorAll('.tour-sub').forEach(function(t){ t.classList.remove('active'); });\n      document.querySelectorAll('.tour-pane').forEach(function(p){ p.classList.remove('active'); });\n      tab.classList.add('active');\n      document.querySelector('.tour-pane[data-tpane=\"' + tab.getAttribute('data-tsub') + '\"]').classList.add('active');\n    });\n  });\n\n  \/* ---------- photos: drag & drop, reorder, cover, remove ---------- *\/\n  function renderPhotos(){\n    var grid = document.getElementById('photo-grid');\n    document.getElementById('photo-empty').style.display = photos.length ? 'none' : 'block';\n    grid.innerHTML = photos.map(function(p, i){\n      return '<div class=\"photo-item' + (i===0?' is-cover':'') + '\" draggable=\"true\" data-i=\"' + i + '\">' +\n        '<img decoding=\"async\" src=\"' + p.url + '\" alt=\"\">' +\n        '<span class=\"cover-flag\">Cover<\/span>' +\n        '<div class=\"actions\"><button data-act=\"remove\" data-i=\"' + i + '\" title=\"Remove\">&times;<\/button><\/div>' +\n        '<\/div>';\n    }).join('');\n  }\n\n  function addPhotos(fileList){\n    Array.prototype.forEach.call(fileList, function(file){\n      if(!file.type.startsWith('image\/')) return;\n      photos.push({url: URL.createObjectURL(file), file: file});\n    });\n    renderPhotos();\n  }\n\n  var photoDrop = document.getElementById('photo-drop');\n  var photoInput = document.getElementById('photo-input');\n  photoDrop.addEventListener('click', function(){ photoInput.click(); });\n  photoInput.addEventListener('change', function(){ addPhotos(this.files); });\n  ['dragover','dragleave','drop'].forEach(function(evt){\n    photoDrop.addEventListener(evt, function(e){\n      e.preventDefault();\n      photoDrop.classList.toggle('drag', evt === 'dragover');\n      if(evt === 'drop') addPhotos(e.dataTransfer.files);\n    });\n  });\n\n  var grid = document.getElementById('photo-grid');\n  var dragIndex = null;\n  grid.addEventListener('dragstart', function(e){\n    var item = e.target.closest('.photo-item');\n    if(!item) return;\n    dragIndex = parseInt(item.getAttribute('data-i'));\n    item.classList.add('dragging');\n  });\n  grid.addEventListener('dragend', function(e){\n    var item = e.target.closest('.photo-item');\n    if(item) item.classList.remove('dragging');\n  });\n  grid.addEventListener('dragover', function(e){ e.preventDefault(); });\n  grid.addEventListener('drop', function(e){\n    e.preventDefault();\n    var target = e.target.closest('.photo-item');\n    if(!target || dragIndex === null) return;\n    var dropIndex = parseInt(target.getAttribute('data-i'));\n    var moved = photos.splice(dragIndex, 1)[0];\n    photos.splice(dropIndex, 0, moved);\n    dragIndex = null;\n    renderPhotos();\n  });\n  grid.addEventListener('click', function(e){\n    var btn = e.target.closest('button[data-act=\"remove\"]');\n    if(!btn) return;\n    photos.splice(parseInt(btn.getAttribute('data-i')), 1);\n    renderPhotos();\n  });\n\n  \/* ---------- video ---------- *\/\n  var videoDrop = document.getElementById('video-drop');\n  var videoInput = document.getElementById('video-input');\n  var videoPreview = document.getElementById('video-preview');\n  videoDrop.addEventListener('click', function(){ videoInput.click(); });\n  videoInput.addEventListener('change', function(){\n    if(!this.files[0]) return;\n    var url = URL.createObjectURL(this.files[0]);\n    videoPreview.innerHTML = '<video src=\"' + url + '\" controls><\/video>';\n    videoPreview.classList.add('show');\n  });\n  ['dragover','dragleave','drop'].forEach(function(evt){\n    videoDrop.addEventListener(evt, function(e){\n      e.preventDefault();\n      videoDrop.classList.toggle('drag', evt === 'dragover');\n      if(evt === 'drop' && e.dataTransfer.files[0]){\n        var url = URL.createObjectURL(e.dataTransfer.files[0]);\n        videoPreview.innerHTML = '<video src=\"' + url + '\" controls><\/video>';\n        videoPreview.classList.add('show');\n      }\n    });\n  });\n  function toEmbedUrl(url){\n    var yt = url.match(\/(?:youtube\\.com\\\/watch\\?v=|youtu\\.be\\\/)([\\w-]+)\/);\n    if(yt) return 'https:\/\/www.youtube.com\/embed\/' + yt[1];\n    var vi = url.match(\/vimeo\\.com\\\/(\\d+)\/);\n    if(vi) return 'https:\/\/player.vimeo.com\/video\/' + vi[1];\n    return null;\n  }\n  document.getElementById('video-url-btn').addEventListener('click', function(){\n    var embed = toEmbedUrl(document.getElementById('video-url').value.trim());\n    if(!embed){ alert('Paste a valid YouTube or Vimeo link.'); return; }\n    videoPreview.innerHTML = '<iframe src=\"' + embed + '\" allowfullscreen><\/iframe>';\n    videoPreview.classList.add('show');\n  });\n\n  \/* ---------- 3D tour: embed link ---------- *\/\n  document.getElementById('tour-url').addEventListener('change', function(){\n    var frame = document.getElementById('tour-frame');\n    if(this.value.trim()){\n      frame.src = this.value.trim();\n      frame.classList.add('show');\n    }\n  });\n\n  \/* ---------- 3D tour: 360 photo upload via Pannellum ---------- *\/\n  var panoDrop = document.getElementById('pano-drop');\n  var panoInput = document.getElementById('pano-input');\n  panoDrop.addEventListener('click', function(){ panoInput.click(); });\n  function loadPano(file){\n    if(!file) return;\n    var url = URL.createObjectURL(file);\n    var viewer = document.getElementById('pano-viewer');\n    viewer.classList.add('show');\n    viewer.innerHTML = '';\n    if(window.pannellum){\n      window.pannellum.viewer(viewer, {\n        type: 'equirectangular',\n        panorama: url,\n        autoLoad: true,\n        compass: false\n      });\n    } else {\n      viewer.innerHTML = '<img decoding=\"async\" src=\"' + url + '\" style=\"width:100%;height:100%;object-fit:cover;\">';\n    }\n  }\n  panoInput.addEventListener('change', function(){ loadPano(this.files[0]); });\n  ['dragover','dragleave','drop'].forEach(function(evt){\n    panoDrop.addEventListener(evt, function(e){\n      e.preventDefault();\n      panoDrop.classList.toggle('drag', evt === 'dragover');\n      if(evt === 'drop') loadPano(e.dataTransfer.files[0]);\n    });\n  });\n\n  \/* ---------- plan selection ---------- *\/\n  document.getElementById('plan-grid').addEventListener('click', function(e){\n    var card = e.target.closest('.plan-card');\n    if(!card) return;\n    selectedPlan = card.getAttribute('data-plan');\n    document.querySelectorAll('.plan-card').forEach(function(c){ c.classList.toggle('selected', c === card); });\n  });\n\n  \/* ---------- review + submit ---------- *\/\n  function renderReview(){\n    var rows = [\n      ['Venue name', document.getElementById('o-name').value || '\u2014'],\n      ['Category', document.getElementById('o-cat').selectedOptions[0].textContent],\n      ['City', document.getElementById('o-city').value || '\u2014'],\n      ['Capacity', document.getElementById('o-capacity').value || '\u2014'],\n      ['Photos', photos.length + ' uploaded'],\n      ['Video', document.getElementById('video-preview').classList.contains('show') ? 'Added' : 'Not added'],\n      ['3D tour', (document.getElementById('tour-frame').classList.contains('show') || document.getElementById('pano-viewer').classList.contains('show')) ? 'Added' : 'Not added'],\n      ['Plan', selectedPlan ? selectedPlan.charAt(0).toUpperCase() + selectedPlan.slice(1) : 'Not selected']\n    ];\n    document.getElementById('review-body').innerHTML = rows.map(function(r){\n      return '<div class=\"review-row\"><span>' + r[0] + '<\/span><span>' + r[1] + '<\/span><\/div>';\n    }).join('');\n  }\n\n  document.getElementById('submit-btn').addEventListener('click', async function(){\n    var msg = document.getElementById('submit-msg');\n    if(!document.getElementById('o-terms').checked){\n      msg.className = 'submit-msg show'; msg.style.color = '#D9786E';\n      msg.textContent = 'Please confirm the checkbox above before submitting.';\n      return;\n    }\n    if(!selectedPlan){\n      msg.className = 'submit-msg show'; msg.style.color = '#D9786E';\n      msg.textContent = 'Go back to step 3 and choose a plan first.';\n      return;\n    }\n    msg.className = 'submit-msg show'; msg.style.color = '';\n    msg.textContent = 'Submitting...';\n    var payload = {\n      name: document.getElementById('o-name').value,\n      category: document.getElementById('o-cat').value,\n      city: document.getElementById('o-city').value,\n      capacity: document.getElementById('o-capacity').value,\n      description: document.getElementById('o-desc').value,\n      photoCount: photos.length,\n      plan: selectedPlan\n    };\n    try{\n      var res = await fetch('\/wp-json\/mahall\/v1\/owner-onboarding', {\n        method:'POST', headers:{'Content-Type':'application\/json'}, body: JSON.stringify(payload)\n      });\n      if(!res.ok) throw new Error('not configured');\n      msg.classList.add('ok');\n      msg.textContent = 'Submitted \u2014 our team reviews new listings within 48 hours.';\n    }catch(err){\n      \/* Demo fallback \u2014 real persistence (including the actual photo\n         files, not just their count) needs the v1.0 backend. *\/\n      msg.classList.add('ok');\n      msg.textContent = 'Captured in demo mode \u2014 connect \/wp-json\/mahall\/v1\/owner-onboarding to receive this for real.';\n    }\n  });\n\n  showStep(1);\n})();\n<\/script>\n\n","protected":false},"excerpt":{"rendered":"<p>Mahall List your venue Get discovered by the right events Four short steps. You can save and come back any time. Venue basics Venue name Category Heritage &#038; culturalHospitality &#038; resortPrivate &#038; exclusiveNature &#038; adventureCoastal &#038; leisureUrban &#038; lifestyleCorporate &#038; MICEEntertainment &#038; production City \/ region Maximum guest capacity Description Photos, video &#038; 3D tour [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"folder":[],"class_list":["post-28","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/pages\/28","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/comments?post=28"}],"version-history":[{"count":2,"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/pages\/28\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/pages\/28\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/media?parent=28"}],"wp:term":[{"taxonomy":"folder","embeddable":true,"href":"https:\/\/mahall.org\/index.php\/wp-json\/wp\/v2\/folder?post=28"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}