PagesStructures['id1748522037059'] = {"structures":[{"type":"page","id":"id1748522037059","elementProperties":{"tagName":"DIV","behaviours":null,"isLocked":false,"name":"TESTING","baseName":"Setting Up an Organisation","templateID":null,"templateName":null,"order":0,"customStates":null,"isElementWasEditedInMode":true,"showHoverEffects":true,"skinInformation":{"skinId":"500918-1403784258535","categoryId":227},"isSupportsMobile":false,"isVisibleInMode":true,"isFixed":false,"topZIndex":0,"elementOpacity":1,"staticMargin":{"horizontalMargin":0,"verticalMargin":0},"sizeAndPosition":{"left":0,"width":0,"top":0,"height":1068},"lastResizingSize":{"width":0,"height":1068,"top":0,"left":0},"isVisibleInLastSection":true,"isVisibleInLastDataItem":true,"attributes":null,"metaProps":null,"metaItemType":null,"metaTitle":null,"styleBindingId":null,"styleBindingFields":null,"shadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"glow":{"spread":0,"blur":0,"color":"black"},"isParallaxBackground":false,"parallaxBackgroundSpeed":0.5,"isParallaxElement":{"isActive":false,"speed":1,"delay":0},"effectsOptions":{"isParallax":false,"oneTimeAnimation":false,"duration":1200,"startDistance":200,"startVisibility":0.8,"distance":300,"measureDistanceType":3,"delay":0,"distanceDelay":0,"runInLoop":false,"flyDirection":2,"direction":"horizontal","movementDistanceHorizontally":200,"movementDistanceVertically":0,"autoStartDistanceSettings":true,"autoDistanceSettings":true},"effects":null,"binding":null,"verticalAlign":"center","horizontalAlign":"center","horizontalContentAlign":"center","basicUserCanEdit":true,"overflow":false,"isGrowable":{"bottom":false,"right":false},"rowsItems":null,"childrenAnchor":"center","backgroundColor":"#fff","imageUrl":"","imageLayout":"repeat","imagePosition":{"horizontal":"center","vertical":"top"},"origImage":"","imageOpacity":0.26,"backgroundOverlay":"transparent","gridLines":[],"fixedBackground":true,"statesGroups":null},"metaData":{"position":{"left":-1164.5,"top":0},"size":{"width":2329,"height":3545}},"childElements":[{"type":"SectionElement","id":"id1748522037066","elementProperties":{"tagName":"SECTION","behaviours":null,"isLocked":false,"name":"Section 3","baseName":null,"templateID":null,"templateName":null,"order":4,"customStates":null,"isElementWasEditedInMode":false,"showHoverEffects":true,"skinInformation":{},"isSupportsMobile":false,"isVisibleInMode":true,"isFixed":false,"topZIndex":0,"elementOpacity":1,"staticMargin":{"horizontalMargin":0,"verticalMargin":0},"sizeAndPosition":{"height":2840,"left":0,"width":2329,"top":0},"lastResizingSize":{"width":2329,"height":2840},"isVisibleInLastSection":true,"isVisibleInLastDataItem":true,"attributes":null,"metaProps":null,"metaItemType":null,"metaTitle":null,"styleBindingId":null,"styleBindingFields":null,"shadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"glow":{"spread":0,"blur":0,"color":"black"},"isParallaxBackground":false,"parallaxBackgroundSpeed":0.5,"isParallaxElement":{"isActive":false,"speed":1,"delay":0},"effectsOptions":{"isParallax":false,"oneTimeAnimation":false,"duration":1200,"startDistance":200,"startVisibility":0.8,"distance":300,"measureDistanceType":3,"delay":0,"distanceDelay":0,"runInLoop":false,"flyDirection":2,"direction":"horizontal","movementDistanceHorizontally":200,"movementDistanceVertically":0,"autoStartDistanceSettings":true,"autoDistanceSettings":true},"effects":null,"binding":null,"innerGlow":{"spread":0,"blur":0,"color":"black"},"innerShadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"padding":{"top":0,"left":0,"bottom":0,"right":0},"basicUserCanEdit":true,"backgroundColor":"rgb(244,240,235)","imageUrl":"","imageLayout":"stretchedToFill","imagePosition":{"vertical":"center","horizontal":"center"},"origImage":"","imageOpacity":1,"backgroundOverlay":"transparent","verticalLayout":"normal","horizontalLayout":"normal","fillLayout":{"fillVertical":false,"fillHorizontal":false},"margin":{"top":0,"left":0,"bottom":0,"right":0},"overflow":false,"isGrowable":{"bottom":false,"right":false},"rowsItems":null,"childrenAnchor":"center","resizingGripPosiotion":"s","sectionName":"Section C","pageContentType":1,"borderWidth":{"top":0,"left":0,"bottom":0,"right":0},"roundness":{"topLeft":0,"bottomLeft":0,"topRight":0,"bottomRight":0},"borderColor":{"top":"Black","left":"Black","bottom":"Black","right":"Black"},"borderStyle":{"top":"solid","left":"solid","bottom":"solid","right":"solid"}},"metaData":{"position":{"left":-1164.5,"top":271},"size":{"width":2329,"height":2840}},"childElements":[{"type":"ExternalAppembed_html_app1","id":"id1762423119510","elementProperties":{"tagName":"DIV","behaviours":null,"isLocked":false,"name":"Embed Html 2","baseName":null,"templateID":"templateid1762423119511","templateName":null,"order":94,"customStates":null,"isElementWasEditedInMode":false,"showHoverEffects":true,"skinInformation":{},"isSupportsMobile":false,"isVisibleInMode":true,"isFixed":false,"topZIndex":0,"elementOpacity":1,"staticMargin":{"horizontalMargin":0,"verticalMargin":0},"sizeAndPosition":{"width":1062,"height":1352,"left":-480,"top":659},"lastResizingSize":{"width":1062,"height":1352},"isVisibleInLastSection":true,"isVisibleInLastDataItem":true,"attributes":null,"metaProps":null,"metaItemType":null,"metaTitle":null,"styleBindingId":null,"styleBindingFields":null,"shadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"glow":{"spread":0,"blur":0,"color":"black"},"isParallaxBackground":false,"parallaxBackgroundSpeed":0.5,"isParallaxElement":{"isActive":false,"speed":1,"delay":0},"effectsOptions":{"isParallax":false,"oneTimeAnimation":false,"duration":1200,"startDistance":200,"startVisibility":0.8,"distance":300,"measureDistanceType":3,"delay":0,"distanceDelay":0,"runInLoop":false,"flyDirection":2,"direction":"horizontal","movementDistanceHorizontally":200,"movementDistanceVertically":0,"autoStartDistanceSettings":true,"autoDistanceSettings":true},"effects":null,"binding":null,"innerGlow":{"spread":0,"blur":0,"color":"black"},"innerShadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"basicUserCanEdit":true,"backgroundColor":"transparent","padding":{"top":0,"left":0,"bottom":0,"right":0},"imageUrl":"","imageLayout":"stretchedToFill","imagePosition":{"vertical":"center","horizontal":"center"},"origImage":"","imageOpacity":1,"backgroundOverlay":"transparent","verticalLayout":"normal","horizontalLayout":"normal","fillLayout":{"fillHorizontal":false,"fillVertical":false},"margin":{"top":0,"left":0,"bottom":0,"right":0},"isVisible":true,"borderWidth":{"top":0,"left":0,"bottom":0,"right":0},"roundness":{"topLeft":0,"bottomLeft":0,"topRight":0,"bottomRight":0},"borderColor":{"top":"Black","left":"Black","bottom":"Black","right":"Black"},"borderStyle":{"top":"solid","left":"solid","bottom":"solid","right":"solid"},"rotationAngle":0,"display":"fixed","cellItem":null,"cellHorizontalAlignment":"none","verticalAlign":"center","horizontalAlign":"center","horizontalContentAlign":"center","embed_html_app1html":"<!doctype html>\n<html lang=\"en-GB\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Swale CVS Activities Directory</title>\n<meta name=\"description\" content=\"Find and submit activities across Swale that help people connect and reduce loneliness.\">\n<link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\" crossorigin=\"\">\n<style>\n:root{--blue:#2563eb;--blue-dark:#1e3a8a;--ink:#0f172a;--soft:#475569;--bg:#f8fafc;--card:#fff;--border:#e2e8f0;--focus:#93c5fd;--danger:#dc2626}\n*{box-sizing:border-box}\nbody{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;background:var(--bg);color:var(--ink)}\na{color:var(--blue);text-decoration:none}\na:hover{text-decoration:underline}\nheader{background:linear-gradient(90deg,var(--blue),var(--blue-dark));color:#fff;padding:16px 20px;position:sticky;top:0;z-index:10}\n.wrap{max-width:1100px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;gap:12px}\n.brand{display:flex;align-items:center;gap:10px;font-weight:800;font-size:1.2rem}\n.container{max-width:1100px;margin:24px auto;padding:0 20px}\n.hero{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px;box-shadow:0 4px 20px rgba(2,6,23,.05)}\nh1{margin:0 0 6px;font-size:1.6rem}\n.hero p{margin:2px 0 0;color:var(--soft)}\n.toolbar{margin-top:14px;background:var(--card);border:1px solid var(--border);border-radius:14px;padding:12px;display:grid;gap:8px;box-shadow:0 6px 24px rgba(2,6,23,.05)}\n.controls{display:grid;grid-template-columns:1fr 220px 220px 220px;gap:10px}\n@media (max-width:1000px){.controls{grid-template-columns:1fr 1fr 1fr}}\n@media (max-width:670px){.controls{grid-template-columns:1fr 1fr}}\n@media (max-width:480px){.controls{grid-template-columns:1fr}}\n.input,.select,.btn{width:100%;padding:12px;border:1px solid var(--border);border-radius:10px;background:#fff;color:var(--ink);font-size:15px}\n.btn{background:var(--blue);border-color:transparent;color:#fff;font-weight:700;cursor:pointer}\n.btn.secondary{background:#eef2ff;color:#1e3a8a;border-color:#c7d2fe}\n.info{display:flex;align-items:center;justify-content:space-between;gap:10px;color:#64748b;font-size:14px}\n.viewtabs{display:flex;gap:8px}\n.tab{padding:8px 12px;border:1px solid var(--border);border-radius:999px;background:#fff;color:#64748b;cursor:pointer;font-weight:700}\n.tab.active{background:#eff6ff;color:#1e3a8a;border-color:#c7d2fe}\n.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:16px;margin-top:14px}\n\n/* >>> COLOURFUL CARDS BY CATEGORY <<< */\n.card{\n  position:relative;\n  border-radius:14px;\n  padding:14px;\n  box-shadow:0 6px 18px rgba(15,23,42,.12);\n  overflow:hidden;\n  border:1px solid #c7d2fe;\n  background:linear-gradient(135deg,#eef2ff,#e0f2fe); /* default (bluey) */\n}\n.card::before{\n  content:\"\";\n  position:absolute;\n  inset:-40%;\n  background:radial-gradient(circle at top left,rgba(59,130,246,.25),transparent 55%);\n  opacity:0.85;\n  pointer-events:none;\n}\n\n/* Social / Befriending – soft blues/purples */\n.card--social{\n  background:linear-gradient(135deg,#eef2ff,#e0f2fe);\n  border-color:#c7d2fe;\n}\n.card--social::before{\n  background:radial-gradient(circle at top left,rgba(79,70,229,.3),transparent 55%);\n}\n\n/* Sports / Wellbeing – greens/teals */\n.card--sports{\n  background:linear-gradient(135deg,#dcfce7,#ccfbf1);\n  border-color:#86efac;\n}\n.card--sports::before{\n  background:radial-gradient(circle at top left,rgba(34,197,94,.3),transparent 55%);\n}\n\n/* Arts / Culture – pinks/purples */\n.card--arts{\n  background:linear-gradient(135deg,#fce7f3,#e0e7ff);\n  border-color:#f9a8d4;\n}\n.card--arts::before{\n  background:radial-gradient(circle at top left,rgba(219,39,119,.3),transparent 55%);\n}\n\n/* Skills / Volunteering – warm yellows/oranges */\n.card--skills{\n  background:linear-gradient(135deg,#fef9c3,#ffedd5);\n  border-color:#facc15;\n}\n.card--skills::before{\n  background:radial-gradient(circle at top left,rgba(234,179,8,.3),transparent 55%);\n}\n\n/* Other – neutral teal/grey */\n.card--other{\n  background:linear-gradient(135deg,#e2f3ff,#e5e7eb);\n  border-color:#94a3b8;\n}\n.card--other::before{\n  background:radial-gradient(circle at top left,rgba(15,118,110,.25),transparent 55%);\n}\n\n.card-inner{\n  position:relative;\n  display:grid;\n  gap:6px;\n}\n.card-header{\n  display:flex;\n  justify-content:space-between;\n  align-items:flex-start;\n  gap:8px;\n  padding-bottom:4px;\n  border-bottom:1px dashed rgba(148,163,184,.7);\n}\n.card h3{\n  margin:0;\n  font-size:1.1rem;\n  color:var(--blue-dark);\n}\n.card-body{\n  margin-top:6px;\n  display:grid;\n  gap:4px;\n}\n.meta{font-size:.95rem;color:#0f172a}\n.desc{font-size:.95rem;color:#1f2933}\n\n/* colourful badge */\n.badge{\n  display:inline-flex;\n  align-items:center;\n  gap:6px;\n  padding:4px 10px;\n  border-radius:999px;\n  background:linear-gradient(135deg,#22c55e,#0ea5e9);\n  color:#f9fafb;\n  font-weight:700;\n  font-size:12px;\n  box-shadow:0 2px 6px rgba(15,23,42,.25);\n}\n\n#loading{color:#64748b;margin:10px 0}\n.error{color:#b91c1c;font-weight:700}\n.footer{margin:26px 0 10px;text-align:center;color:#64748b;font-size:14px}\n#map{height:520px;border:1px solid var(--border);border-radius:14px;margin-top:14px}\n.hidden{display:none}\n\n/* Modal */\n.modal-backdrop{\n  position:fixed;\n  inset:0;\n  background:rgba(2,6,23,.55);\n  display:none;\n  align-items:center;\n  justify-content:center;\n  z-index:1000; /* sits above the Leaflet map */\n}\n.modal-backdrop.open{display:flex}\n.modal{\n  background:#fff;\n  max-width:760px;\n  width:calc(100% - 28px);\n  border-radius:16px;\n  border:1px solid var(--border);\n  box-shadow:0 20px 60px rgba(2,6,23,.35);\n  max-height:85vh;\n  overflow:auto;\n}\n.modal header{\n  position:sticky;\n  top:0;\n  background:#fff;\n  color:var(--ink);\n  padding:14px 18px;\n  border-bottom:1px solid var(--border);\n}\n.modal h2{margin:0;font-size:1.25rem}\n.modal .content{padding:16px}\n.modal .close{\n  appearance:none;\n  border:1px solid var(--border);\n  background:#fff;\n  border-radius:10px;\n  padding:6px 10px;\n  cursor:pointer;\n  margin-left:auto;\n}\nlabel{font-weight:600;font-size:14px}\ntextarea{min-height:110px;resize:vertical}\n.help{font-size:12px;color:#64748b}\n.required{color:var(--danger)}\n.msg{margin-top:6px;font-weight:600}\n.success{\n  display:none;\n  border:1px solid #bbf7d0;\n  background:#f0fdf4;\n  color:#14532d;\n  padding:14px;\n  border-radius:12px;\n  font-weight:700;\n}\n.input:focus,.select:focus,textarea:focus{outline:3px solid var(--focus);outline-offset:2px}\n</style>\n</head>\n<body>\n<header>\n  <div class=\"wrap\">\n    <div class=\"brand\">Swale CVS Activities Directory</div>\n    <div class=\"link-row\">\n      <button id=\"openModalBtn\" class=\"btn secondary\" type=\"button\">Add your activity</button>\n    </div>\n  </div>\n</header>\n\n<main class=\"container\">\n  <section class=\"hero\">\n    <h1>Find things to do across Swale</h1>\n    <p>Explore community groups and activities that help people connect and reduce loneliness.</p>\n  </section>\n\n  <section class=\"toolbar\" aria-labelledby=\"filters\">\n    <h2 id=\"filters\" style=\"margin:0;font-size:1.05rem;color:#475569\">Search &amp; filters</h2>\n    <div class=\"controls\">\n      <input id=\"q\" class=\"input\" type=\"search\" placeholder=\"Search by keyword\" aria-label=\"Keyword\">\n      <select id=\"cat\" class=\"select\" aria-label=\"Category filter\"><option value=\"\">All categories</option></select>\n      <select id=\"age\" class=\"select\" aria-label=\"Age range filter\"><option value=\"\">All ages</option></select>\n      <div class=\"viewtabs\" role=\"tablist\" aria-label=\"View selection\">\n        <button id=\"listTab\" class=\"tab active\" role=\"tab\" aria-selected=\"true\" aria-controls=\"listView\">List</button>\n        <button id=\"mapTab\" class=\"tab\" role=\"tab\" aria-selected=\"false\" aria-controls=\"mapView\">Map</button>\n      </div>\n    </div>\n    <div class=\"info\">\n      <div id=\"count\">0 activities</div>\n      <div id=\"loading\">Loading activities…</div>\n    </div>\n  </section>\n\n  <section id=\"listView\" class=\"grid\" aria-live=\"polite\"></section>\n  <section id=\"mapView\" class=\"hidden\" aria-live=\"polite\">\n    <div id=\"map\" role=\"region\" aria-label=\"Activity map of Swale\"></div>\n  </section>\n\n  <p class=\"footer\">© Swale CVS - Tackling Loneliness Project</p>\n</main>\n\n<!-- Modal for submissions -->\n<div id=\"submitModal\" class=\"modal-backdrop\" aria-hidden=\"true\">\n  <div class=\"modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"submitTitle\" aria-describedby=\"submitDesc\">\n    <header style=\"display:flex;align-items:center;gap:10px\">\n      <h2 id=\"submitTitle\">Submit an activity</h2>\n      <button id=\"closeModalBtnTop\" class=\"close\" type=\"button\" aria-label=\"Close\">Close ✕</button>\n    </header>\n    <div class=\"content\">\n      <p id=\"submitDesc\" class=\"help\" style=\"margin:0 0 12px\">Tell us about your group, club or event. Submissions are moderated before publishing.</p>\n\n      <form id=\"activityForm\" novalidate style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px\">\n        <div class=\"field\">\n          <label for=\"title\">Activity name <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"title\" name=\"title\" required placeholder=\"e.g., Memory Café\">\n        </div>\n        <div class=\"field\">\n          <label for=\"organisation\">Organisation <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"organisation\" name=\"organisation\" required placeholder=\"Your group or organisation\">\n        </div>\n\n        <div class=\"field\" style=\"grid-column:1/-1\">\n          <label for=\"description\">Short description <span class=\"required\">*</span></label>\n          <textarea class=\"input\" id=\"description\" name=\"description\" maxlength=\"500\" required placeholder=\"What is it? Who is it for? Where and when? (max 500 chars)\"></textarea>\n          <div class=\"help\">Focus on who it helps, when it runs, and how to join.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"category\">Category</label>\n          <select class=\"select\" id=\"category\" name=\"category\">\n            <option>Social / Befriending</option>\n            <option>Sports / Wellbeing</option>\n            <option>Arts / Culture</option>\n            <option>Skills / Volunteering</option>\n            <option>Other</option>\n          </select>\n        </div>\n        <div class=\"field\">\n          <label for=\"audience\">Audience / age range</label>\n          <input class=\"input\" id=\"audience\" name=\"audience\" placeholder=\"e.g., 65+; carers; adults\">\n          <div class=\"help\">Use commas, semicolons or slashes to list multiple.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"schedule\">When does it run?</label>\n          <input class=\"input\" id=\"schedule\" name=\"schedule\" placeholder=\"e.g., Tuesdays 10:00-12:00 (term time)\">\n        </div>\n\n        <!-- New expiry related fields -->\n        <div class=\"field\">\n          <label for=\"eventType\">Is this a one off event or ongoing activity?</label>\n          <select class=\"select\" id=\"eventType\" name=\"eventType\">\n            <option value=\"Ongoing\" selected>Ongoing activity</option>\n            <option value=\"One off\">One off event</option>\n          </select>\n          <div class=\"help\">One off events will automatically be hidden after the event date has passed.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"endDate\">If one off, what is the event date?</label>\n          <input class=\"input\" id=\"endDate\" name=\"endDate\" type=\"date\" placeholder=\"YYYY-MM-DD\">\n          <div class=\"help\">We use this to hide the event from the directory once the date has passed.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"location\">Venue / location</label>\n          <input class=\"input\" id=\"location\" name=\"location\" placeholder=\"Name, address, postcode\">\n        </div>\n\n        <div class=\"field\">\n          <label for=\"cost\">Cost</label>\n          <input class=\"input\" id=\"cost\" name=\"cost\" placeholder=\"Free / £2 per session\">\n        </div>\n        <div class=\"field\">\n          <label for=\"access\">Accessibility</label>\n          <input class=\"input\" id=\"access\" name=\"access\" placeholder=\"Wheelchair accessible, hearing loop, etc.\">\n        </div>\n        <div class=\"field\">\n          <label for=\"email\">Contact email <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"email\" type=\"email\" name=\"email\" required placeholder=\"name@example.org\">\n        </div>\n        <div class=\"field\">\n          <label for=\"website\">Website (optional)</label>\n          <input class=\"input\" id=\"website\" type=\"url\" name=\"website\" placeholder=\"https://…\">\n        </div>\n\n        <!-- Honeypot -->\n        <input type=\"text\" id=\"hp_field\" name=\"hp_field\" style=\"position:absolute;left:-9999px\" tabindex=\"-1\" autocomplete=\"off\">\n\n        <div style=\"grid-column:1/-1;display:flex;gap:10px;align-items:center\">\n          <button class=\"btn\" type=\"submit\">Submit activity</button>\n          <button id=\"closeModalBtnBottom\" class=\"btn secondary\" type=\"button\">Cancel</button>\n          <span class=\"help\">We will review before publishing.</span>\n        </div>\n        <p id=\"formMsg\" class=\"msg\" role=\"status\" aria-live=\"polite\" style=\"grid-column:1/-1\"></p>\n      </form>\n\n      <div id=\"successPanel\" class=\"success\" role=\"alert\" aria-live=\"assertive\">\n        ✓ Thanks. Your activity has been submitted for review.\n      </div>\n    </div>\n  </div>\n</div>\n\n<script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\" crossorigin=\"\"></script>\n<script>\n/* ===== CONFIG ===== */\nconst CSV_URL = \"https://docs.google.com/spreadsheets/d/e/2PACX-1vQw3HYPhxQZ5M0g-g7xzunr-a8KHNuTsyuxY5hMpniBSMM29O1opGorcvGsj6OD8_bZLfn0G75dNN42/pub?gid=0&single=true&output=csv\";\nconst APPS_SCRIPT_URL = \"https://script.google.com/macros/s/AKfycby2KRNSpl1_5TubvIgkJbWqSPYBZDzGqtM1iLJf5-vfkBnB3qj02RsiTEI140vp5G_tWw/exec\";\n\n/* Column map (must match your Sheet headers) */\nconst COL = {\n  title:\"Title\", organisation:\"Organisation\", description:\"Description\",\n  category:\"Category\", audience:\"Audience\",\n  location:\"Location\", schedule:\"Schedule\",\n  contact:\"ContactEmail\", website:\"Website\",\n  lat:\"Latitude\", lng:\"Longitude\",\n  status:\"Status\",\n  eventType:\"EventType\",   // new\n  endDate:\"EndDate\"        // new\n};\n\n/* Swale map defaults */\nconst SWALE_BOUNDS = L.latLngBounds([51.27, 0.57], [51.46, 0.93]); // Sittingbourne ↔ Sheppey\nconst SWALE_CENTRE = [51.34, 0.77];\nconst SWALE_ZOOM   = 11;\n\n/* Moderation */\nconst STATUS_COL = \"Status\";\nconst PUBLISH_VALUE = \"Approved\";\n\n/* ===== HELPERS ===== */\nfunction escapeHTML(s){return String(s).replace(/[&<>\"']/g,m=>({\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"\\\"\":\"&quot;\",\"'\":\"&#39;\"}[m]));}\nfunction escapeAttr(s){return String(s).replace(/\"/g,\"&quot;\");}\nfunction isUrl(v){return /^https?:\\/\\//i.test(String(v||\"\").trim());}\nfunction displayHost(url){try{const u=new URL(url);return u.hostname.replace(/^www\\./,'');}catch{return url;}}\n\n/* Date based expiry for one off events */\nfunction isExpired(row){\n  const type = String(row[COL.eventType] || \"\").trim().toLowerCase();\n  if (!(type === \"one off\" || type === \"one-off\" || type === \"oneoff\" || type === \"one_off\")) {\n    return false; // only expire one off events\n  }\n\n  const raw = String(row[COL.endDate] || \"\").trim();\n  if (!raw) return false;\n\n  const d = new Date(raw);\n  if (Number.isNaN(d.getTime())) return false;\n\n  const today = new Date();\n  today.setHours(0,0,0,0);\n  d.setHours(0,0,0,0);\n\n  return d < today;\n}\n\n/* Robust CSV parser (handles quotes/commas) */\nfunction parseCSV(text){\n  const lines = text.split(/\\r?\\n/).filter(l=>l.length>0);\n  if(!lines.length) return [];\n  const parseLine = line => line.match(/(\"(\\\\\"|[^\"])*\"|[^,])+/g).map(v=>{\n    v=v.trim();\n    if(v.startsWith('\"') && v.endsWith('\"')) v=v.slice(1,-1).replace(/\"\"/g,'\"');\n    return v;\n  });\n  const header = parseLine(lines[0]);\n  return lines.slice(1).map(line=>{\n    const cells = parseLine(line);\n    const row = {};\n    header.forEach((h,i)=> row[h] = cells[i] ?? \"\");\n    return row;\n  });\n}\n\n/* ===== STATE ===== */\nlet rows=[], filtered=[], map, markersLayer;\n\n/* ===== RENDER LIST ===== */\nfunction renderList(list){\n  const grid=document.getElementById(\"listView\");\n  const count=document.getElementById(\"count\");\n  grid.innerHTML=\"\";\n  count.textContent=`${list.length} activit${list.length===1?'y':'ies'}`;\n  if(!list.length){\n    grid.innerHTML=\"<p>No activities match your filters.</p>\";\n    return;\n  }\n\n  for(const r of list){\n    const title = r[COL.title] || \"Untitled\";\n    const org   = r[COL.organisation] || \"\";\n    const aud   = r[COL.audience] || \"\";\n    const desc  = r[COL.description] || \"\";\n    let   loc   = r[COL.location] || \"\";\n    const mail  = r[COL.contact] || \"\";\n    const web   = r[COL.website] || \"\";\n    const rawCat = r[COL.category] || \"\";\n    const sched = r[COL.schedule] || \"\";\n\n    // Work out card colour class based on category\n    const catKey = rawCat.toLowerCase();\n    let cardClassExtra = \"\";\n    if (catKey.includes(\"social\")) {\n      cardClassExtra = \"card--social\";\n    } else if (catKey.includes(\"sport\") || catKey.includes(\"wellbeing\") || catKey.includes(\"well-being\")) {\n      cardClassExtra = \"card--sports\";\n    } else if (catKey.includes(\"art\") || catKey.includes(\"culture\")) {\n      cardClassExtra = \"card--arts\";\n    } else if (catKey.includes(\"skill\") || catKey.includes(\"volunteer\")) {\n      cardClassExtra = \"card--skills\";\n    } else if (rawCat) {\n      cardClassExtra = \"card--other\";\n    }\n\n    let websiteHtml = \"\";\n    if (loc && isUrl(loc)) {\n      websiteHtml = `🌐 <a href=\"${escapeAttr(loc)}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(loc))}</a>`;\n      loc=\"\";\n    }\n    if (web) {\n      websiteHtml += (websiteHtml ? \"<br>\" : \"\") + `🌐 <a href=\"${escapeAttr(web)}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(web))}</a>`;\n    }\n\n    const lines = [];\n    if (org) lines.push(`<div class=\"meta\"><strong>${escapeHTML(org)}</strong></div>`);\n    if (aud) lines.push(`<div class=\"meta\">👥 ${escapeHTML(aud)}</div>`);\n    if (sched) lines.push(`<div class=\"meta\">📅 ${escapeHTML(sched)}</div>`);\n    if (desc) lines.push(`<div class=\"desc\">${escapeHTML(desc)}</div>`);\n    if (loc)  lines.push(`<div class=\"meta\">📍 ${escapeHTML(loc)}</div>`);\n    if (mail) lines.push(`<div class=\"meta\">✉️ <a href=\"mailto:${escapeAttr(mail)}\">${escapeHTML(mail)}</a></div>`);\n    if (websiteHtml) lines.push(`<div class=\"meta\">${websiteHtml}</div>`);\n\n    const badge = rawCat ? `<span class=\"badge\">${escapeHTML(rawCat)}</span>` : \"\";\n\n    const card=document.createElement(\"div\");\n    card.className = `card ${cardClassExtra}`.trim();\n    card.innerHTML=`\n      <div class=\"card-inner\">\n        <div class=\"card-header\">\n          <h3>${escapeHTML(title)}</h3>\n          ${badge}\n        </div>\n        <div class=\"card-body\">\n          ${lines.join(\"\")}\n        </div>\n      </div>\n    `;\n    grid.appendChild(card);\n  }\n}\n\n/* ===== FILTERS ===== */\nfunction normaliseAudience(value){\n  return String(value||\"\").split(/[,;/]/).map(v=>v.trim()).filter(Boolean);\n}\nfunction applyFilters(){\n  const q=document.getElementById(\"q\").value.trim().toLowerCase();\n  const cat=document.getElementById(\"cat\").value;\n  const age=document.getElementById(\"age\").value;\n\n  filtered = rows.filter(r=>{\n    const inCat = !cat || (r[COL.category]||\"\") === cat;\n    if(!inCat) return false;\n\n    const audiences = normaliseAudience(r[COL.audience]);\n    const inAge = !age || audiences.map(v=>v.toLowerCase()).includes(age.toLowerCase());\n    if(!inAge) return false;\n\n    if(!q) return true;\n    const hay=[r[COL.title],r[COL.organisation],r[COL.description],r[COL.location],r[COL.schedule],r[COL.category],r[COL.audience]]\n      .map(v=>String(v||\"\").toLowerCase()).join(\" \");\n    return hay.includes(q);\n  });\n\n  renderList(filtered);\n  updateMap(filtered);\n}\nfunction populateSelectFromColumn(selectId, extractor){\n  const select=document.getElementById(selectId);\n  const set=new Set();\n  rows.forEach(r=>{extractor(r).forEach(v=>set.add(v));});\n  Array.from(set).sort((a,b)=>a.localeCompare(b)).forEach(v=>{\n    const opt=document.createElement(\"option\");opt.value=v;opt.textContent=v;select.appendChild(opt);\n  });\n}\nfunction populateFilters(){\n  populateSelectFromColumn(\"cat\", r => r[COL.category] ? [r[COL.category].trim()] : []);\n  populateSelectFromColumn(\"age\", r => normaliseAudience(r[COL.audience]));\n}\n\n/* ===== MAP ===== */\nfunction ensureMap(){\n  if (map) return;\n\n  map = L.map('map', {\n    minZoom: 10,\n    maxZoom: 15,\n    maxBounds: SWALE_BOUNDS.pad(0.12),\n    maxBoundsViscosity: 0.8,\n    worldCopyJump: false\n  }).setView(SWALE_CENTRE, SWALE_ZOOM);\n\n  L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {\n    attribution:'© OpenStreetMap contributors'\n  }).addTo(map);\n\n  markersLayer = L.layerGroup().addTo(map);\n\n  // If created while hidden, fix size and recenter\n  setTimeout(() => { map.invalidateSize(true); map.setView(SWALE_CENTRE, SWALE_ZOOM); }, 0);\n}\n\nfunction updateMap(list){\n  ensureMap();\n  markersLayer.clearLayers();\n\n  const bounds = L.latLngBounds([]);\n  let valid = 0;\n\n  list.forEach(r=>{\n    let lat = parseFloat(r[COL.lat]);\n    let lng = parseFloat(r[COL.lng]);\n\n    // Auto fix if someone swapped lat/lng in the sheet\n    const looksSwale = (lat > 51 && lat < 52) && (lng > 0.4 && lng < 1.1);\n    const looksSwap  = (lng > 51 && lng < 52) && (lat > 0.4 && lat < 1.1);\n    if (!looksSwale && looksSwap) { const t = lat; lat = lng; lng = t; }\n\n    if (Number.isFinite(lat) && Number.isFinite(lng)) {\n      valid++;\n      const popup = `\n        <strong style=\"color:#2563eb\">${escapeHTML(r[COL.title]||\"Untitled\")}</strong><br>\n        ${r[COL.organisation]?`${escapeHTML(r[COL.organisation])}<br>`:\"\"}\n        ${r[COL.location] && !isUrl(r[COL.location]) ? `📍 ${escapeHTML(r[COL.location])}<br>`:\"\"}\n        ${r[COL.schedule]?`<strong>${escapeHTML(r[COL.schedule])}</strong><br>`:\"\"}\n        ${r[COL.category]?`<span style=\"display:inline-block;margin-top:4px;padding:2px 6px;border-radius:999px;background:#ecfeff;color:#155e75;font-weight:700;font-size:12px\">${escapeHTML(r[COL.category])}</span><br>`:\"\"}\n        ${r[COL.audience]?`👥 ${escapeHTML(r[COL.audience])}<br>`:\"\"}\n        ${r[COL.contact]?`✉️ <a href=\"mailto:${escapeAttr(r[COL.contact])}\">${escapeHTML(r[COL.contact])}</a><br>`:\"\"}\n        ${r[COL.website]?`🌐 <a href=\"${escapeAttr(r[COL.website])}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(r[COL.website]))}</a>`:\"\"}\n      `;\n      L.marker([lat,lng]).addTo(markersLayer).bindPopup(popup);\n      bounds.extend([lat,lng]);\n    }\n  });\n\n  // Default to Swale; only refit when multiple pins merit it\n  if (valid === 0 || valid === 1) {\n    map.setView(SWALE_CENTRE, SWALE_ZOOM);\n  } else {\n    map.fitBounds(bounds.pad(0.12));\n    if (map.getZoom() < 10) map.setZoom(SWALE_ZOOM);\n    if (map.getZoom() > 14) map.setZoom(13);\n  }\n}\n\n/* ===== LOAD DIRECTORY DATA (Approved only, not expired) ===== */\nfetch(CSV_URL)\n  .then(r=>r.ok?r.text():Promise.reject(r.status))\n  .then(parseCSV)\n  .then(data=>{\n    rows = data\n      .filter(r => String(r[STATUS_COL] || \"\").trim().toLowerCase() === PUBLISH_VALUE.toLowerCase())\n      .filter(r => !isExpired(r));\n\n    document.getElementById(\"loading\").style.display=\"none\";\n    populateFilters();\n    applyFilters();\n  })\n  .catch(err=>{\n    const el=document.getElementById(\"loading\");\n    el.innerHTML = `<span class=\"error\">Unable to load activities. Check your Sheet publish to web CSV link.</span>`;\n    console.error(err);\n  });\n\n/* ===== EVENTS ===== */\ndocument.getElementById(\"q\").addEventListener(\"input\", ()=>applyFilters());\ndocument.getElementById(\"cat\").addEventListener(\"change\", applyFilters);\ndocument.getElementById(\"age\").addEventListener(\"change\", applyFilters);\ndocument.getElementById(\"listTab\").addEventListener(\"click\", ()=>{\n  document.getElementById(\"listTab\").classList.add(\"active\");\n  document.getElementById(\"mapTab\").classList.remove(\"active\");\n  document.getElementById(\"listView\").classList.remove(\"hidden\");\n  document.getElementById(\"mapView\").classList.add(\"hidden\");\n});\ndocument.getElementById(\"mapTab\").addEventListener(\"click\", ()=>{\n  document.getElementById(\"mapTab\").classList.add(\"active\");\n  document.getElementById(\"listTab\").classList.remove(\"active\");\n  document.getElementById(\"listView\").classList.add(\"hidden\");\n  document.getElementById(\"mapView\").classList.remove(\"hidden\");\n  ensureMap();\n  updateMap(filtered.length ? filtered : rows);\n  setTimeout(()=>{ map.invalidateSize(true); map.setView(SWALE_CENTRE, SWALE_ZOOM); }, 0);\n});\n\n/* ===== MODAL HANDLERS ===== */\nconst modal = document.getElementById('submitModal');\nconst openBtn = document.getElementById('openModalBtn');\nconst closeTop = document.getElementById('closeModalBtnTop');\nconst closeBottom = document.getElementById('closeModalBtnBottom');\nfunction openModal(){ modal.classList.add('open'); modal.setAttribute('aria-hidden','false'); setTimeout(()=>document.getElementById('title').focus(), 50); }\nfunction closeModal(){ modal.classList.remove('open'); modal.setAttribute('aria-hidden','true'); openBtn.focus(); }\nopenBtn.addEventListener('click', openModal);\ncloseTop.addEventListener('click', closeModal);\ncloseBottom.addEventListener('click', closeModal);\nmodal.addEventListener('click', (e)=>{ if(e.target===modal) closeModal(); });\ndocument.addEventListener('keydown', (e)=>{ if(e.key==='Escape' && modal.classList.contains('open')) closeModal(); });\n\n/* ===== SUBMISSION HANDLER ===== */\nconst form = document.getElementById('activityForm');\nconst msg  = document.getElementById('formMsg');\nconst success = document.getElementById('successPanel');\nfunction validEmail(v){ return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v||''); }\n\nform.addEventListener('submit', async (e) => {\n  e.preventDefault();\n  msg.textContent=\"\"; msg.style.color=\"\";\n  const data = Object.fromEntries(new FormData(form).entries());\n\n  const required = ['title','organisation','description','email'];\n  const missing = required.filter(k => !String(data[k]||'').trim());\n  if (missing.length){\n    msg.textContent = \"Please complete required fields marked with *.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n  if (!validEmail(data.email)){\n    msg.textContent = \"Please enter a valid email.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n\n  // Extra validation for one off events\n  if (String(data.eventType || \"\").toLowerCase().startsWith(\"one\") &&\n      !String(data.endDate || \"\").trim()){\n    msg.textContent = \"Please enter the event date for one off events.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n\n  msg.textContent = \"Sending…\";\n  try{\n    await fetch(APPS_SCRIPT_URL, {\n      method: \"POST\",\n      mode: \"no-cors\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify(data)\n    });\n    form.reset();\n    success.style.display = 'block';\n    setTimeout(()=>{ success.style.display='none'; closeModal(); }, 2000);\n  }catch(err){\n    console.error(err);\n    msg.textContent = \"Sorry, something went wrong. Please try again later.\";\n    msg.style.color = \"var(--danger)\";\n  }\n});\n</script>\n</body>\n</html>\n","embed_html_app1headerHtml":""},"metaData":{"position":{"left":-480,"top":930},"size":{"width":1062,"height":1352}}},{"type":"image","id":"id1765205814453","elementProperties":{"tagName":"DIV","behaviours":null,"isLocked":false,"name":"Image 2","baseName":null,"templateID":"templateid1765205814454","templateName":null,"order":95,"customStates":null,"isElementWasEditedInMode":false,"showHoverEffects":true,"skinInformation":{},"isSupportsMobile":false,"isVisibleInMode":true,"isFixed":false,"topZIndex":0,"elementOpacity":1,"staticMargin":{"horizontalMargin":0,"verticalMargin":0},"sizeAndPosition":{"width":1159,"height":656,"left":-535,"top":3},"lastResizingSize":{"width":1159,"height":656},"isVisibleInLastSection":true,"isVisibleInLastDataItem":true,"attributes":null,"metaProps":null,"metaItemType":null,"metaTitle":null,"styleBindingId":null,"styleBindingFields":null,"shadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"glow":{"spread":0,"blur":0,"color":"black"},"isParallaxBackground":false,"parallaxBackgroundSpeed":0.5,"isParallaxElement":{"isActive":false,"speed":1,"delay":0},"effectsOptions":{"isParallax":false,"oneTimeAnimation":false,"duration":1200,"startDistance":200,"startVisibility":0.8,"distance":300,"measureDistanceType":3,"delay":0,"distanceDelay":0,"runInLoop":false,"flyDirection":2,"direction":"horizontal","movementDistanceHorizontally":200,"movementDistanceVertically":0,"autoStartDistanceSettings":true,"autoDistanceSettings":true},"effects":null,"binding":null,"innerGlow":{"spread":0,"blur":0,"color":"black"},"innerShadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"display":"fixed","cellItem":null,"cellHorizontalAlignment":"none","basicUserCanEdit":true,"isVisible":true,"backgroundColor":"transparent","verticalLayout":"normal","horizontalLayout":"normal","fillLayout":{"fillVertical":false,"fillHorizontal":false},"margin":{"top":0,"left":0,"bottom":0,"right":0},"startSelected":false,"urlAddress":null,"openInNewTab":false,"openInTopWindow":false,"shouldUnderlineChildren":false,"templateOpener":false,"popupType":"DefaultPopup","imageUrl":{"id":154719136,"imagePath":"https://storage.googleapis.com/production-websitebuilder-v1-0-9/669/1825669/ZzMlG5Po/6e8b6a915a8e4131b24bccf97f456a9a","storageServer":0,"imageWidth":1920,"imageHeight":1080},"imageLayout":"stretchedToFill","imagePosition":{"vertical":"center","horizontal":"center"},"origImage":"","imageOpacity":1,"borderWidth":{"top":0,"left":0,"bottom":0,"right":0},"roundness":{"topLeft":0,"bottomLeft":0,"topRight":0,"bottomRight":0},"borderColor":{"top":"black","left":"black","bottom":"black","right":"black"},"borderStyle":{"top":"solid","left":"solid","bottom":"solid","right":"solid"},"rotationAngle":0,"backgroundOverlay":"transparent","dataBinding":null,"imageLoadingOverlay":{"background":"rgba(255,255,255,0.7)","imageInfo":{}},"imageLoadingSettings":{"hideImage":false,"showOverlay":false,"overlayBackground":"rgba(255,255,255,0.7)"},"alt":null},"metaData":{"position":{"left":-535,"top":274},"size":{"width":1159,"height":656}}},{"type":"ExternalAppembed_html_app1","id":"id1765206961633","elementProperties":{"tagName":"DIV","behaviours":null,"isLocked":false,"name":"Embed Html 4","baseName":null,"templateID":"templateid1765206961634","templateName":null,"order":96,"customStates":null,"isElementWasEditedInMode":false,"showHoverEffects":true,"skinInformation":{},"isSupportsMobile":false,"isVisibleInMode":true,"isFixed":false,"topZIndex":0,"elementOpacity":1,"staticMargin":{"horizontalMargin":0,"verticalMargin":0},"sizeAndPosition":{"width":1029,"height":799,"left":-460,"top":1163},"lastResizingSize":{"width":1029,"height":799},"isVisibleInLastSection":true,"isVisibleInLastDataItem":true,"attributes":null,"metaProps":null,"metaItemType":null,"metaTitle":null,"styleBindingId":null,"styleBindingFields":null,"shadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"glow":{"spread":0,"blur":0,"color":"black"},"isParallaxBackground":false,"parallaxBackgroundSpeed":0.5,"isParallaxElement":{"isActive":false,"speed":1,"delay":0},"effectsOptions":{"isParallax":false,"oneTimeAnimation":false,"duration":1200,"startDistance":200,"startVisibility":0.8,"distance":300,"measureDistanceType":3,"delay":0,"distanceDelay":0,"runInLoop":false,"flyDirection":2,"direction":"horizontal","movementDistanceHorizontally":200,"movementDistanceVertically":0,"autoStartDistanceSettings":true,"autoDistanceSettings":true},"effects":null,"binding":null,"innerGlow":{"spread":0,"blur":0,"color":"black"},"innerShadow":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"},"basicUserCanEdit":true,"backgroundColor":"transparent","padding":{"top":0,"left":0,"bottom":0,"right":0},"imageUrl":"","imageLayout":"stretchedToFill","imagePosition":{"vertical":"center","horizontal":"center"},"origImage":"","imageOpacity":1,"backgroundOverlay":"transparent","verticalLayout":"normal","horizontalLayout":"normal","fillLayout":{"fillHorizontal":false,"fillVertical":false},"margin":{"top":0,"left":0,"bottom":0,"right":0},"isVisible":true,"borderWidth":{"top":0,"left":0,"bottom":0,"right":0},"roundness":{"topLeft":0,"bottomLeft":0,"topRight":0,"bottomRight":0},"borderColor":{"top":"Black","left":"Black","bottom":"Black","right":"Black"},"borderStyle":{"top":"solid","left":"solid","bottom":"solid","right":"solid"},"rotationAngle":0,"display":"fixed","cellItem":null,"cellHorizontalAlignment":"none","verticalAlign":"center","horizontalAlign":"center","horizontalContentAlign":"center","embed_html_app1html":"<!doctype html>\n<html lang=\"en-GB\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Swale CVS Activities Directory</title>\n<meta name=\"description\" content=\"Find and submit activities across Swale that help people connect and reduce loneliness.\">\n<link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\" crossorigin=\"\">\n<style>\n:root{\n  --blue:#2563eb;\n  --blue-dark:#1e3a8a;\n  --ink:#0f172a;\n  --soft:#475569;\n  --bg:#f8fafc;\n  --card:#fff;\n  --border:#e2e8f0;\n  --focus:#93c5fd;\n  --danger:#dc2626;\n}\n\n*{box-sizing:border-box}\nbody{\n  margin:0;\n  font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;\n  background:var(--bg);\n  color:var(--ink);\n}\na{color:var(--blue);text-decoration:none}\na:hover{text-decoration:underline}\n\n/* ===== UPDATED HEADER (BEIGE GRADIENT) ===== */\nheader{\n  background:linear-gradient(90deg,#f3e9dd,#e8dcc7,#dfcfb6);\n  color:#3b2f2f;\n  padding:16px 20px;\n  position:sticky;\n  top:0;\n  z-index:10;\n}\n\n.wrap{\n  max-width:1100px;\n  margin:0 auto;\n  display:flex;\n  align-items:center;\n  justify-content:space-between;\n  gap:12px;\n}\n.brand{\n  display:flex;\n  align-items:center;\n  gap:10px;\n  font-weight:800;\n  font-size:1.2rem;\n  color:#3b2f2f;\n}\n\n.container{\n  max-width:1100px;\n  margin:24px auto;\n  padding:0 20px;\n}\n\n.hero{\n  background:var(--card);\n  border:1px solid var(--border);\n  border-radius:14px;\n  padding:18px;\n  box-shadow:0 4px 20px rgba(2,6,23,.05);\n}\nh1{\n  margin:0 0 6px;\n  font-size:1.6rem;\n}\n.hero p{\n  margin:2px 0 0;\n  color:var(--soft);\n}\n\n.toolbar{\n  margin-top:14px;\n  background:var(--card);\n  border:1px solid var(--border);\n  border-radius:14px;\n  padding:12px;\n  display:grid;\n  gap:8px;\n  box-shadow:0 6px 24px rgba(2,6,23,.05);\n}\n\n<!doctype html>\n<html lang=\"en-GB\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Swale CVS Activities Directory</title>\n<meta name=\"description\" content=\"Find and submit activities across Swale that help people connect and reduce loneliness.\">\n<link rel=\"stylesheet\" href=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.css\" crossorigin=\"\">\n<style>\n:root{\n  --blue:#2563eb;\n  --blue-dark:#1e3a8a;\n  --ink:#0f172a;\n  --soft:#475569;\n  --bg:#f8fafc;\n  --card:#fff;\n  --border:#e2e8f0;\n  --focus:#93c5fd;\n  --danger:#dc2626;\n  --beige:#f5eee4;\n  --beige-deep:#e4d4c3;\n  --beige-accent:#d7bfa1;\n}\n*{box-sizing:border-box}\nbody{\n  margin:0;\n  font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;\n  background:var(--bg);\n  color:var(--ink);\n}\na{color:var(--blue);text-decoration:none}\na:hover{text-decoration:underline}\n\n/* Header now beige like the poster */\nheader{\n  background:radial-gradient(circle at top left,var(--beige),var(--beige-deep));\n  color:#3b2f2f;\n  padding:16px 20px;\n  position:sticky;\n  top:0;\n  z-index:10;\n  border-bottom:1px solid rgba(148,126,98,0.3);\n}\n.wrap{\n  max-width:1100px;\n  margin:0 auto;\n  display:flex;\n  align-items:center;\n  justify-content:space-between;\n  gap:12px;\n}\n.brand{\n  display:flex;\n  align-items:center;\n  gap:10px;\n  font-weight:800;\n  font-size:1.2rem;\n  letter-spacing:0.06em;\n  text-transform:uppercase;\n}\n.brand span.small{\n  display:block;\n  font-weight:500;\n  font-size:.75rem;\n  letter-spacing:0.18em;\n  text-transform:uppercase;\n  color:#7b5a34;\n}\n\n.container{max-width:1100px;margin:24px auto;padding:0 20px}\n.hero{\n  background:var(--card);\n  border:1px solid var(--border);\n  border-radius:14px;\n  padding:18px;\n  box-shadow:0 4px 20px rgba(2,6,23,.05);\n}\nh1{margin:0 0 6px;font-size:1.6rem}\n.hero p{margin:2px 0 0;color:var(--soft)}\n\n.toolbar{\n  margin-top:14px;\n  background:var(--card);\n  border:1px solid var(--border);\n  border-radius:14px;\n  padding:12px;\n  display:grid;\n  gap:8px;\n  box-shadow:0 6px 24px rgba(2,6,23,.05);\n}\n.controls{\n  display:grid;\n  grid-template-columns:1fr 220px 220px 220px;\n  gap:10px;\n}\n@media (max-width:1000px){.controls{grid-template-columns:1fr 1fr 1fr}}\n@media (max-width:670px){.controls{grid-template-columns:1fr 1fr}}\n@media (max-width:480px){.controls{grid-template-columns:1fr}}\n\n.input,.select,.btn{\n  width:100%;\n  padding:12px;\n  border:1px solid var(--border);\n  border-radius:10px;\n  background:#fff;\n  color:var(--ink);\n  font-size:15px;\n}\n.btn{\n  background:var(--blue);\n  border-color:transparent;\n  color:#fff;\n  font-weight:700;\n  cursor:pointer;\n}\n.btn.secondary{\n  background:#fef3c7;\n  color:#7c2d12;\n  border-color:#fed7aa;\n}\n.btn.secondary:hover{\n  background:#fde68a;\n}\n\n.info{\n  display:flex;\n  align-items:center;\n  justify-content:space-between;\n  gap:10px;\n  color:#64748b;\n  font-size:14px;\n}\n.viewtabs{display:flex;gap:8px}\n.tab{\n  padding:8px 12px;\n  border:1px solid var(--border);\n  border-radius:999px;\n  background:#fff;\n  color:#64748b;\n  cursor:pointer;\n  font-weight:700;\n}\n.tab.active{\n  background:#eff6ff;\n  color:#1e3a8a;\n  border-color:#c7d2fe;\n}\n.grid{\n  display:grid;\n  grid-template-columns:repeat(auto-fit,minmax(260px,1fr));\n  gap:16px;\n  margin-top:14px;\n}\n\n/* Colourful cards by category */\n.card{\n  position:relative;\n  border-radius:14px;\n  padding:14px;\n  box-shadow:0 6px 18px rgba(15,23,42,.12);\n  overflow:hidden;\n  border:1px solid #c7d2fe;\n  background:linear-gradient(135deg,#eef2ff,#e0f2fe);\n}\n.card::before{\n  content:\"\";\n  position:absolute;\n  inset:-40%;\n  background:radial-gradient(circle at top left,rgba(59,130,246,.25),transparent 55%);\n  opacity:0.85;\n  pointer-events:none;\n}\n\n/* Social / Befriending */\n.card--social{\n  background:linear-gradient(135deg,#eef2ff,#e0f2fe);\n  border-color:#c7d2fe;\n}\n.card--social::before{\n  background:radial-gradient(circle at top left,rgba(79,70,229,.3),transparent 55%);\n}\n\n/* Sports / Wellbeing */\n.card--sports{\n  background:linear-gradient(135deg,#dcfce7,#ccfbf1);\n  border-color:#86efac;\n}\n.card--sports::before{\n  background:radial-gradient(circle at top left,rgba(34,197,94,.3),transparent 55%);\n}\n\n/* Arts / Culture */\n.card--arts{\n  background:linear-gradient(135deg,#fce7f3,#e0e7ff);\n  border-color:#f9a8d4;\n}\n.card--arts::before{\n  background:radial-gradient(circle at top left,rgba(219,39,119,.3),transparent 55%);\n}\n\n/* Skills / Volunteering */\n.card--skills{\n  background:linear-gradient(135deg,#fef9c3,#ffedd5);\n  border-color:#facc15;\n}\n.card--skills::before{\n  background:radial-gradient(circle at top left,rgba(234,179,8,.3),transparent 55%);\n}\n\n/* Other */\n.card--other{\n  background:linear-gradient(135deg,#e2f3ff,#e5e7eb);\n  border-color:#94a3b8;\n}\n.card--other::before{\n  background:radial-gradient(circle at top left,rgba(15,118,110,.25),transparent 55%);\n}\n\n.card-inner{\n  position:relative;\n  display:grid;\n  gap:6px;\n}\n.card-header{\n  display:flex;\n  justify-content:space-between;\n  align-items:flex-start;\n  gap:8px;\n  padding-bottom:4px;\n  border-bottom:1px dashed rgba(148,163,184,.7);\n}\n.card h3{\n  margin:0;\n  font-size:1.1rem;\n  color:var(--blue-dark);\n}\n.card-body{\n  margin-top:6px;\n  display:grid;\n  gap:4px;\n}\n.meta{font-size:.95rem;color:#0f172a}\n.desc{font-size:.95rem;color:#1f2933}\n\n/* Badge */\n.badge{\n  display:inline-flex;\n  align-items:center;\n  gap:6px;\n  padding:4px 10px;\n  border-radius:999px;\n  background:linear-gradient(135deg,#22c55e,#0ea5e9);\n  color:#f9fafb;\n  font-weight:700;\n  font-size:12px;\n  box-shadow:0 2px 6px rgba(15,23,42,.25);\n}\n\n#loading{color:#64748b;margin:10px 0}\n.error{color:#b91c1c;font-weight:700}\n.footer{\n  margin:26px 0 10px;\n  text-align:center;\n  color:#64748b;\n  font-size:14px;\n}\n#map{\n  height:520px;\n  border:1px solid var(--border);\n  border-radius:14px;\n  margin-top:14px;\n}\n.hidden{display:none}\n\n/* Modal */\n.modal-backdrop{\n  position:fixed;\n  inset:0;\n  background:rgba(2,6,23,.55);\n  display:none;\n  align-items:center;\n  justify-content:center;\n  z-index:1000;\n}\n.modal-backdrop.open{display:flex}\n.modal{\n  background:#fff;\n  max-width:760px;\n  width:calc(100% - 28px);\n  border-radius:16px;\n  border:1px solid var(--border);\n  box-shadow:0 20px 60px rgba(2,6,23,.35);\n  max-height:85vh;\n  overflow:auto;\n}\n.modal header{\n  position:sticky;\n  top:0;\n  background:#fff;\n  color:var(--ink);\n  padding:14px 18px;\n  border-bottom:1px solid var(--border);\n}\n.modal h2{margin:0;font-size:1.25rem}\n.modal .content{padding:16px}\n.modal .close{\n  appearance:none;\n  border:1px solid var(--border);\n  background:#fff;\n  border-radius:10px;\n  padding:6px 10px;\n  cursor:pointer;\n  margin-left:auto;\n}\nlabel{font-weight:600;font-size:14px}\ntextarea{min-height:110px;resize:vertical}\n.help{font-size:12px;color:#64748b}\n.required{color:var(--danger)}\n.msg{margin-top:6px;font-weight:600}\n.success{\n  display:none;\n  border:1px solid #bbf7d0;\n  background:#f0fdf4;\n  color:#14532d;\n  padding:14px;\n  border-radius:12px;\n  font-weight:700;\n}\n.input:focus,.select:focus,textarea:focus{\n  outline:3px solid var(--focus);\n  outline-offset:2px;\n}\n</style>\n</head>\n<body>\n<header>\n  <div class=\"wrap\">\n    <div class=\"brand\">\n      Swale CVS Activities Directory\n      <span class=\"small\">Community activities across Swale</span>\n    </div>\n    <div class=\"link-row\">\n      <button id=\"openModalBtn\" class=\"btn secondary\" type=\"button\">Add your activity</button>\n    </div>\n  </div>\n</header>\n\n<main class=\"container\">\n  <section class=\"hero\">\n    <h1>Find things to do across Swale</h1>\n    <p>Explore community groups and activities that help people connect and reduce loneliness.</p>\n  </section>\n\n  <section class=\"toolbar\" aria-labelledby=\"filters\">\n    <h2 id=\"filters\" style=\"margin:0;font-size:1.05rem;color:#475569\">Search and filters</h2>\n    <div class=\"controls\">\n      <input id=\"q\" class=\"input\" type=\"search\" placeholder=\"Search by keyword\" aria-label=\"Keyword\">\n      <select id=\"cat\" class=\"select\" aria-label=\"Category filter\"><option value=\"\">All categories</option></select>\n      <select id=\"age\" class=\"select\" aria-label=\"Age range filter\"><option value=\"\">All ages</option></select>\n      <div class=\"viewtabs\" role=\"tablist\" aria-label=\"View selection\">\n        <button id=\"listTab\" class=\"tab active\" role=\"tab\" aria-selected=\"true\" aria-controls=\"listView\">List</button>\n        <button id=\"mapTab\" class=\"tab\" role=\"tab\" aria-selected=\"false\" aria-controls=\"mapView\">Map</button>\n      </div>\n    </div>\n    <div class=\"info\">\n      <div id=\"count\">0 activities</div>\n      <div id=\"loading\">Loading activities…</div>\n    </div>\n  </section>\n\n  <section id=\"listView\" class=\"grid\" aria-live=\"polite\"></section>\n  <section id=\"mapView\" class=\"hidden\" aria-live=\"polite\">\n    <div id=\"map\" role=\"region\" aria-label=\"Activity map of Swale\"></div>\n  </section>\n\n  <p class=\"footer\">© Swale CVS - Tackling Loneliness Project</p>\n</main>\n\n<!-- Modal for submissions -->\n<div id=\"submitModal\" class=\"modal-backdrop\" aria-hidden=\"true\">\n  <div class=\"modal\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"submitTitle\" aria-describedby=\"submitDesc\">\n    <header style=\"display:flex;align-items:center;gap:10px\">\n      <h2 id=\"submitTitle\">Submit an activity</h2>\n      <button id=\"closeModalBtnTop\" class=\"close\" type=\"button\" aria-label=\"Close\">Close ✕</button>\n    </header>\n    <div class=\"content\">\n      <p id=\"submitDesc\" class=\"help\" style=\"margin:0 0 12px\">Tell us about your group, club or event. Submissions are moderated before publishing.</p>\n\n      <form id=\"activityForm\" novalidate style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px\">\n        <div class=\"field\">\n          <label for=\"title\">Activity name <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"title\" name=\"title\" required placeholder=\"e.g., Memory Café\">\n        </div>\n        <div class=\"field\">\n          <label for=\"organisation\">Organisation <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"organisation\" name=\"organisation\" required placeholder=\"Your group or organisation\">\n        </div>\n\n        <div class=\"field\" style=\"grid-column:1/-1\">\n          <label for=\"description\">Short description <span class=\"required\">*</span></label>\n          <textarea class=\"input\" id=\"description\" name=\"description\" maxlength=\"500\" required placeholder=\"What is it? Who is it for? Where and when? (max 500 chars)\"></textarea>\n          <div class=\"help\">Focus on who it helps, when it runs, and how to join.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"category\">Category</label>\n          <select class=\"select\" id=\"category\" name=\"category\">\n            <option>Social / Befriending</option>\n            <option>Sports / Wellbeing</option>\n            <option>Arts / Culture</option>\n            <option>Skills / Volunteering</option>\n            <option>Other</option>\n          </select>\n        </div>\n        <div class=\"field\">\n          <label for=\"audience\">Audience / age range</label>\n          <input class=\"input\" id=\"audience\" name=\"audience\" placeholder=\"e.g., 65+; carers; adults\">\n          <div class=\"help\">Use commas, semicolons or slashes to list multiple.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"schedule\">When does it run?</label>\n          <input class=\"input\" id=\"schedule\" name=\"schedule\" placeholder=\"e.g., Tuesdays 10:00-12:00 (term time)\">\n        </div>\n\n        <!-- New expiry related fields -->\n        <div class=\"field\">\n          <label for=\"eventType\">Is this a one off event or ongoing activity?</label>\n          <select class=\"select\" id=\"eventType\" name=\"eventType\">\n            <option value=\"Ongoing\" selected>Ongoing activity</option>\n            <option value=\"One off\">One off event</option>\n          </select>\n          <div class=\"help\">One off events will automatically be hidden after the event date has passed.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"endDate\">If one off, what is the event date?</label>\n          <input class=\"input\" id=\"endDate\" name=\"endDate\" type=\"date\" placeholder=\"YYYY-MM-DD\">\n          <div class=\"help\">We use this to hide the event from the directory once the date has passed.</div>\n        </div>\n\n        <div class=\"field\">\n          <label for=\"location\">Venue / location</label>\n          <input class=\"input\" id=\"location\" name=\"location\" placeholder=\"Name, address, postcode\">\n        </div>\n\n        <div class=\"field\">\n          <label for=\"cost\">Cost</label>\n          <input class=\"input\" id=\"cost\" name=\"cost\" placeholder=\"Free / £2 per session\">\n        </div>\n        <div class=\"field\">\n          <label for=\"access\">Accessibility</label>\n          <input class=\"input\" id=\"access\" name=\"access\" placeholder=\"Wheelchair accessible, hearing loop, etc.\">\n        </div>\n        <div class=\"field\">\n          <label for=\"email\">Contact email <span class=\"required\">*</span></label>\n          <input class=\"input\" id=\"email\" type=\"email\" name=\"email\" required placeholder=\"name@example.org\">\n        </div>\n        <div class=\"field\">\n          <label for=\"website\">Website (optional)</label>\n          <input class=\"input\" id=\"website\" type=\"url\" name=\"website\" placeholder=\"https://…\">\n        </div>\n\n        <!-- Honeypot -->\n        <input type=\"text\" id=\"hp_field\" name=\"hp_field\" style=\"position:absolute;left:-9999px\" tabindex=\"-1\" autocomplete=\"off\">\n\n        <div style=\"grid-column:1/-1;display:flex;gap:10px;align-items:center\">\n          <button class=\"btn\" type=\"submit\">Submit activity</button>\n          <button id=\"closeModalBtnBottom\" class=\"btn secondary\" type=\"button\">Cancel</button>\n          <span class=\"help\">We will review before publishing.</span>\n        </div>\n        <p id=\"formMsg\" class=\"msg\" role=\"status\" aria-live=\"polite\" style=\"grid-column:1/-1\"></p>\n      </form>\n\n      <div id=\"successPanel\" class=\"success\" role=\"alert\" aria-live=\"assertive\">\n        ✓ Thanks. Your activity has been submitted for review.\n      </div>\n    </div>\n  </div>\n</div>\n\n<script src=\"https://unpkg.com/leaflet@1.9.4/dist/leaflet.js\" crossorigin=\"\"></script>\n<script>\n/* Config */\nconst CSV_URL = \"https://docs.google.com/spreadsheets/d/e/2PACX-1vQw3HYPhxQZ5M0g-g7xzunr-a8KHNuTsyuxY5hMpniBSMM29O1opGorcvGsj6OD8_bZLfn0G75dNN42/pub?gid=0&single=true&output=csv\";\nconst APPS_SCRIPT_URL = \"https://script.google.com/macros/s/AKfycby2KRNSpl1_5TubvIgkJbWqSPYBZDzGqtM1iLJf5-vfkBnB3qj02RsiTEI140vp5G_tWw/exec\";\n\nconst COL = {\n  title:\"Title\",\n  organisation:\"Organisation\",\n  description:\"Description\",\n  category:\"Category\",\n  audience:\"Audience\",\n  location:\"Location\",\n  schedule:\"Schedule\",\n  contact:\"ContactEmail\",\n  website:\"Website\",\n  lat:\"Latitude\",\n  lng:\"Longitude\",\n  status:\"Status\",\n  eventType:\"EventType\",\n  endDate:\"EndDate\"\n};\n\nconst SWALE_BOUNDS = L.latLngBounds([51.27, 0.57], [51.46, 0.93]);\nconst SWALE_CENTRE = [51.34, 0.77];\nconst SWALE_ZOOM   = 11;\n\nconst STATUS_COL = \"Status\";\nconst PUBLISH_VALUE = \"Approved\";\n\nfunction escapeHTML(s){\n  return String(s).replace(/[&<>\"']/g,m=>({\"&\":\"&amp;\",\"<\":\"&lt;\",\">\":\"&gt;\",\"\\\"\":\"&quot;\",\"'\":\"&#39;\"}[m]));\n}\nfunction escapeAttr(s){\n  return String(s).replace(/\"/g,\"&quot;\");\n}\nfunction isUrl(v){return /^https?:\\/\\//i.test(String(v||\"\").trim());}\nfunction displayHost(url){\n  try{\n    const u=new URL(url);\n    return u.hostname.replace(/^www\\./,\"\");\n  }catch{return url;}\n}\n\n/* Expiry for one off events */\nfunction isExpired(row){\n  const type = String(row[COL.eventType] || \"\").trim().toLowerCase();\n  if (!(type === \"one off\" || type === \"one-off\" || type === \"oneoff\" || type === \"one_off\")) {\n    return false;\n  }\n  const raw = String(row[COL.endDate] || \"\").trim();\n  if (!raw) return false;\n  const d = new Date(raw);\n  if (Number.isNaN(d.getTime())) return false;\n  const today = new Date();\n  today.setHours(0,0,0,0);\n  d.setHours(0,0,0,0);\n  return d < today;\n}\n\n/* Simple CSV parser */\nfunction parseCSV(text){\n  const lines = text.split(/\\r?\\n/).filter(l=>l.length>0);\n  if(!lines.length) return [];\n  const parseLine = line => line.match(/(\"(\\\\\"|[^\"])*\"|[^,])+/g).map(v=>{\n    v=v.trim();\n    if(v.startsWith('\"') && v.endsWith('\"')) v=v.slice(1,-1).replace(/\"\"/g,'\"');\n    return v;\n  });\n  const header = parseLine(lines[0]);\n  return lines.slice(1).map(line=>{\n    const cells = parseLine(line);\n    const row = {};\n    header.forEach((h,i)=> row[h] = cells[i] ?? \"\");\n    return row;\n  });\n}\n\nlet rows=[], filtered=[], map, markersLayer;\n\n/* List render */\nfunction renderList(list){\n  const grid=document.getElementById(\"listView\");\n  const count=document.getElementById(\"count\");\n  grid.innerHTML=\"\";\n  count.textContent=`${list.length} activit${list.length===1?\"y\":\"ies\"}`;\n  if(!list.length){\n    grid.innerHTML=\"<p>No activities match your filters.</p>\";\n    return;\n  }\n\n  for(const r of list){\n    const title = r[COL.title] || \"Untitled\";\n    const org   = r[COL.organisation] || \"\";\n    const aud   = r[COL.audience] || \"\";\n    const desc  = r[COL.description] || \"\";\n    let   loc   = r[COL.location] || \"\";\n    const mail  = r[COL.contact] || \"\";\n    const web   = r[COL.website] || \"\";\n    const rawCat = r[COL.category] || \"\";\n    const sched = r[COL.schedule] || \"\";\n\n    const catKey = rawCat.toLowerCase();\n    let cardClassExtra = \"\";\n    if (catKey.includes(\"social\")) {\n      cardClassExtra = \"card--social\";\n    } else if (catKey.includes(\"sport\") || catKey.includes(\"wellbeing\") || catKey.includes(\"well-being\")) {\n      cardClassExtra = \"card--sports\";\n    } else if (catKey.includes(\"art\") || catKey.includes(\"culture\")) {\n      cardClassExtra = \"card--arts\";\n    } else if (catKey.includes(\"skill\") || catKey.includes(\"volunteer\")) {\n      cardClassExtra = \"card--skills\";\n    } else if (rawCat) {\n      cardClassExtra = \"card--other\";\n    }\n\n    let websiteHtml = \"\";\n    if (loc && isUrl(loc)) {\n      websiteHtml = `🌐 <a href=\"${escapeAttr(loc)}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(loc))}</a>`;\n      loc=\"\";\n    }\n    if (web) {\n      websiteHtml += (websiteHtml ? \"<br>\" : \"\") + `🌐 <a href=\"${escapeAttr(web)}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(web))}</a>`;\n    }\n\n    const lines = [];\n    if (org) lines.push(`<div class=\"meta\"><strong>${escapeHTML(org)}</strong></div>`);\n    if (aud) lines.push(`<div class=\"meta\">👥 ${escapeHTML(aud)}</div>`);\n    if (sched) lines.push(`<div class=\"meta\">📅 ${escapeHTML(sched)}</div>`);\n    if (desc) lines.push(`<div class=\"desc\">${escapeHTML(desc)}</div>`);\n    if (loc)  lines.push(`<div class=\"meta\">📍 ${escapeHTML(loc)}</div>`);\n    if (mail) lines.push(`<div class=\"meta\">✉️ <a href=\"mailto:${escapeAttr(mail)}\">${escapeHTML(mail)}</a></div>`);\n    if (websiteHtml) lines.push(`<div class=\"meta\">${websiteHtml}</div>`);\n\n    const badge = rawCat ? `<span class=\"badge\">${escapeHTML(rawCat)}</span>` : \"\";\n\n    const card=document.createElement(\"div\");\n    card.className = `card ${cardClassExtra}`.trim();\n    card.innerHTML=`\n      <div class=\"card-inner\">\n        <div class=\"card-header\">\n          <h3>${escapeHTML(title)}</h3>\n          ${badge}\n        </div>\n        <div class=\"card-body\">\n          ${lines.join(\"\")}\n        </div>\n      </div>\n    `;\n    grid.appendChild(card);\n  }\n}\n\n/* Filters */\nfunction normaliseAudience(value){\n  return String(value||\"\").split(/[,;/]/).map(v=>v.trim()).filter(Boolean);\n}\nfunction applyFilters(){\n  const q=document.getElementById(\"q\").value.trim().toLowerCase();\n  const cat=document.getElementById(\"cat\").value;\n  const age=document.getElementById(\"age\").value;\n\n  filtered = rows.filter(r=>{\n    const inCat = !cat || (r[COL.category]||\"\") === cat;\n    if(!inCat) return false;\n\n    const audiences = normaliseAudience(r[COL.audience]);\n    const inAge = !age || audiences.map(v=>v.toLowerCase()).includes(age.toLowerCase());\n    if(!inAge) return false;\n\n    if(!q) return true;\n    const hay=[r[COL.title],r[COL.organisation],r[COL.description],r[COL.location],r[COL.schedule],r[COL.category],r[COL.audience]]\n      .map(v=>String(v||\"\").toLowerCase()).join(\" \");\n    return hay.includes(q);\n  });\n\n  renderList(filtered);\n  updateMap(filtered);\n}\nfunction populateSelectFromColumn(selectId, extractor){\n  const select=document.getElementById(selectId);\n  const set=new Set();\n  rows.forEach(r=>{extractor(r).forEach(v=>set.add(v));});\n  Array.from(set).sort((a,b)=>a.localeCompare(b)).forEach(v=>{\n    const opt=document.createElement(\"option\");\n    opt.value=v;\n    opt.textContent=v;\n    select.appendChild(opt);\n  });\n}\nfunction populateFilters(){\n  populateSelectFromColumn(\"cat\", r => r[COL.category] ? [r[COL.category].trim()] : []);\n  populateSelectFromColumn(\"age\", r => normaliseAudience(r[COL.audience]));\n}\n\n/* Map */\nfunction ensureMap(){\n  if (map) return;\n\n  map = L.map(\"map\", {\n    minZoom:10,\n    maxZoom:15,\n    maxBounds:SWALE_BOUNDS.pad(0.12),\n    maxBoundsViscosity:0.8,\n    worldCopyJump:false\n  }).setView(SWALE_CENTRE, SWALE_ZOOM);\n\n  L.tileLayer(\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\", {\n    attribution:\"© OpenStreetMap contributors\"\n  }).addTo(map);\n\n  markersLayer = L.layerGroup().addTo(map);\n\n  setTimeout(() => { map.invalidateSize(true); map.setView(SWALE_CENTRE, SWALE_ZOOM); }, 0);\n}\n\nfunction updateMap(list){\n  ensureMap();\n  markersLayer.clearLayers();\n\n  const bounds = L.latLngBounds([]);\n  let valid = 0;\n\n  list.forEach(r=>{\n    let lat = parseFloat(r[COL.lat]);\n    let lng = parseFloat(r[COL.lng]);\n\n    const looksSwale = (lat > 51 && lat < 52) && (lng > 0.4 && lng < 1.1);\n    const looksSwap  = (lng > 51 && lng < 52) && (lat > 0.4 && lat < 1.1);\n    if (!looksSwale && looksSwap) { const t = lat; lat = lng; lng = t; }\n\n    if (Number.isFinite(lat) && Number.isFinite(lng)) {\n      valid++;\n      const popup = `\n        <strong style=\"color:#2563eb\">${escapeHTML(r[COL.title]||\"Untitled\")}</strong><br>\n        ${r[COL.organisation]?`${escapeHTML(r[COL.organisation])}<br>`:\"\"}\n        ${r[COL.location] && !isUrl(r[COL.location]) ? `📍 ${escapeHTML(r[COL.location])}<br>`:\"\"}\n        ${r[COL.schedule]?`<strong>${escapeHTML(r[COL.schedule])}</strong><br>`:\"\"}\n        ${r[COL.category]?`<span style=\"display:inline-block;margin-top:4px;padding:2px 6px;border-radius:999px;background:#ecfeff;color:#155e75;font-weight:700;font-size:12px\">${escapeHTML(r[COL.category])}</span><br>`:\"\"}\n        ${r[COL.audience]?`👥 ${escapeHTML(r[COL.audience])}<br>`:\"\"}\n        ${r[COL.contact]?`✉️ <a href=\"mailto:${escapeAttr(r[COL.contact])}\">${escapeHTML(r[COL.contact])}</a><br>`:\"\"}\n        ${r[COL.website]?`🌐 <a href=\"${escapeAttr(r[COL.website])}\" target=\"_blank\" rel=\"noopener\">${escapeHTML(displayHost(r[COL.website]))}</a>`:\"\"}\n      `;\n      L.marker([lat,lng]).addTo(markersLayer).bindPopup(popup);\n      bounds.extend([lat,lng]);\n    }\n  });\n\n  if (valid === 0 || valid === 1) {\n    map.setView(SWALE_CENTRE, SWALE_ZOOM);\n  } else {\n    map.fitBounds(bounds.pad(0.12));\n    if (map.getZoom() < 10) map.setZoom(SWALE_ZOOM);\n    if (map.getZoom() > 14) map.setZoom(13);\n  }\n}\n\n/* Load data */\nfetch(CSV_URL)\n  .then(r=>r.ok?r.text():Promise.reject(r.status))\n  .then(parseCSV)\n  .then(data=>{\n    rows = data\n      .filter(r => String(r[STATUS_COL] || \"\").trim().toLowerCase() === PUBLISH_VALUE.toLowerCase())\n      .filter(r => !isExpired(r));\n\n    document.getElementById(\"loading\").style.display=\"none\";\n    populateFilters();\n    applyFilters();\n  })\n  .catch(err=>{\n    const el=document.getElementById(\"loading\");\n    el.innerHTML = `<span class=\"error\">Unable to load activities. Check your Sheet publish to web CSV link.</span>`;\n    console.error(err);\n  });\n\n/* Events */\ndocument.getElementById(\"q\").addEventListener(\"input\", applyFilters);\ndocument.getElementById(\"cat\").addEventListener(\"change\", applyFilters);\ndocument.getElementById(\"age\").addEventListener(\"change\", applyFilters);\n\ndocument.getElementById(\"listTab\").addEventListener(\"click\", ()=>{\n  document.getElementById(\"listTab\").classList.add(\"active\");\n  document.getElementById(\"mapTab\").classList.remove(\"active\");\n  document.getElementById(\"listView\").classList.remove(\"hidden\");\n  document.getElementById(\"mapView\").classList.add(\"hidden\");\n});\ndocument.getElementById(\"mapTab\").addEventListener(\"click\", ()=>{\n  document.getElementById(\"mapTab\").classList.add(\"active\");\n  document.getElementById(\"listTab\").classList.remove(\"active\");\n  document.getElementById(\"listView\").classList.add(\"hidden\");\n  document.getElementById(\"mapView\").classList.remove(\"hidden\");\n  ensureMap();\n  updateMap(filtered.length ? filtered : rows);\n  setTimeout(()=>{ map.invalidateSize(true); map.setView(SWALE_CENTRE, SWALE_ZOOM); }, 0);\n});\n\n/* Modal handlers */\nconst modal = document.getElementById(\"submitModal\");\nconst openBtn = document.getElementById(\"openModalBtn\");\nconst closeTop = document.getElementById(\"closeModalBtnTop\");\nconst closeBottom = document.getElementById(\"closeModalBtnBottom\");\n\nfunction openModal(){\n  modal.classList.add(\"open\");\n  modal.setAttribute(\"aria-hidden\",\"false\");\n  setTimeout(()=>document.getElementById(\"title\").focus(), 50);\n}\nfunction closeModal(){\n  modal.classList.remove(\"open\");\n  modal.setAttribute(\"aria-hidden\",\"true\");\n  openBtn.focus();\n}\nopenBtn.addEventListener(\"click\", openModal);\ncloseTop.addEventListener(\"click\", closeModal);\ncloseBottom.addEventListener(\"click\", closeModal);\nmodal.addEventListener(\"click\", (e)=>{ if(e.target===modal) closeModal(); });\ndocument.addEventListener(\"keydown\", (e)=>{ if(e.key===\"Escape\" && modal.classList.contains(\"open\")) closeModal(); });\n\n/* Submission handler */\nconst form = document.getElementById(\"activityForm\");\nconst msg  = document.getElementById(\"formMsg\");\nconst success = document.getElementById(\"successPanel\");\n\nfunction validEmail(v){ return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v||\"\"); }\n\nform.addEventListener(\"submit\", async (e) => {\n  e.preventDefault();\n  msg.textContent=\"\";\n  msg.style.color=\"\";\n  const data = Object.fromEntries(new FormData(form).entries());\n\n  const required = [\"title\",\"organisation\",\"description\",\"email\"];\n  const missing = required.filter(k => !String(data[k]||\"\").trim());\n  if (missing.length){\n    msg.textContent = \"Please complete required fields marked with *.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n  if (!validEmail(data.email)){\n    msg.textContent = \"Please enter a valid email.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n\n  if (String(data.eventType || \"\").toLowerCase().startsWith(\"one\") &&\n      !String(data.endDate || \"\").trim()){\n    msg.textContent = \"Please enter the event date for one off events.\";\n    msg.style.color = \"var(--danger)\";\n    return;\n  }\n\n  msg.textContent = \"Sending…\";\n  try{\n    await fetch(APPS_SCRIPT_URL, {\n      method: \"POST\",\n      mode: \"no-cors\",\n      headers: { \"Content-Type\": \"application/json\" },\n      body: JSON.stringify(data)\n    });\n    form.reset();\n    success.style.display = \"block\";\n    setTimeout(()=>{ success.style.display=\"none\"; closeModal(); }, 2000);\n  }catch(err){\n    console.error(err);\n    msg.textContent = \"Sorry, something went wrong. Please try again later.\";\n    msg.style.color = \"var(--danger)\";\n  }\n});\n</script>\n</body>\n</html>\n\n","embed_html_app1headerHtml":""},"metaData":{"position":{"left":-460,"top":1434},"size":{"width":1029,"height":799}}}]}],"offset":{"left":231,"top":-1036}}],"connections":{"targetConnections":{},"sourceConnections":{}},"customStatesProperties":{"id1748522037059":{"order":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":0}}},"isElementWasEditedInMode":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"isVisibleInMode":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"isFixed":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":false}}},"topZIndex":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":0}}},"elementOpacity":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":1}}},"staticMargin":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"horizontalMargin":0,"verticalMargin":0}}}},"sizeAndPosition":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"left":0,"width":0,"top":0,"height":1068}}}},"lastResizingSize":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"width":362,"height":2563}}}},"isVisibleInLastSection":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"isVisibleInLastDataItem":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"shadow":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"top":0,"left":0,"spread":0,"blur":0,"color":"black"}}}},"glow":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"spread":0,"blur":0,"color":"black"}}}},"verticalAlign":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":"center"}}},"horizontalAlign":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":"center"}}},"horizontalContentAlign":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":"center"}}},"basicUserCanEdit":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"overflow":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":false}}},"rowsItems":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":null}}},"backgroundColor":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"mainColor":0,"variation":0,"opacity":1,"brightness":0}}}},"imageUrl":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":""}}},"imageLayout":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":"stretchedToFill"}}},"imagePosition":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"horizontal":"right","vertical":"top"}}}},"origImage":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":""}}},"imageOpacity":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":1}}},"backgroundOverlay":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":"transparent"}}},"fixedBackground":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":false}}}},"id1748522037066":{"order":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":6}}},"isElementWasEditedInMode":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":true}}},"sizeAndPosition":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"height":525,"left":0,"width":1830,"top":0}}}},"rowsItems":{"MobileResponsiveStateId1234":{"coordinator":{"stateValue":{"1748523699746":{"id":1748523699746}}}}}}},"pageId":"id1748522037059","version":17.004,"id":"id1765207639199","relativePoint":{"left":231,"top":-1036}}