{"id":10,"date":"2026-06-29T12:47:47","date_gmt":"2026-06-29T12:47:47","guid":{"rendered":"https:\/\/kiosk.glenarmcastleheritage.com\/?page_id=10"},"modified":"2026-06-29T12:56:38","modified_gmt":"2026-06-29T12:56:38","slug":"test-homepage","status":"publish","type":"page","link":"https:\/\/kiosk.glenarmcastleheritage.com\/?page_id=10","title":{"rendered":"TEST HOMEPAGE"},"content":{"rendered":"\n<!-- ============================================================\n     GLENARM CASTLE DIGITAL ARCHIVE \u2014 standalone directory\n     Loads data from a CSV file on your site. No plugin, no Tippy.\n\n     HOW TO USE:\n     1. Upload your CSV to the site (Media Library or a folder).\n     2. Copy its URL.\n     3. Paste that URL into CSV_URL below (one line, near the top of the script).\n     4. Put this whole block in a Custom HTML block \/ Divi Code Module.\n\n     TO UPDATE DATA LATER: just replace the CSV file at the same URL\n     (or upload a new one and update CSV_URL). The directory refreshes\n     automatically on next page load.\n\n     CSV FORMAT (first row must be headers, exactly):\n     First Name,Surname,Date,Occupation,Photo\n     ============================================================ -->\n\n<div id=\"glenarm-archive\">\n  <div class=\"ga-searchbar\">\n    <input type=\"text\" id=\"ga-search\" placeholder=\"Search past and present staff members\u2026\" autocomplete=\"off\" disabled=\"\">\n    <span id=\"ga-count\"><\/span>\n  <\/div>\n\n  <div id=\"ga-welcome\" class=\"ga-welcome\">\n    <h2>Welcome to the Glenarm Castle Digital Archive<\/h2>\n    <p>Search above for past and present staff members.<\/p>\n  <\/div>\n\n  <div id=\"ga-loading\" class=\"ga-loading\" style=\"display:none;\">Loading archive\u2026<\/div>\n  <div id=\"ga-error\" class=\"ga-error\" style=\"display:none;\"><\/div>\n\n  <table id=\"ga-table\" class=\"ga-table\" style=\"display:none;\">\n    <thead>\n      <tr>\n        <th>First Name<\/th><th>Surname<\/th><th>Date<\/th><th>Occupation<\/th><th>Photo<\/th>\n      <\/tr>\n    <\/thead>\n    <tbody id=\"ga-tbody\"><\/tbody>\n  <\/table>\n\n  <div id=\"ga-pagination\" class=\"ga-pagination\" style=\"display:none;\"><\/div>\n<\/div>\n\n<div id=\"ga-overlay\" class=\"ga-overlay\">\n  <div id=\"ga-card\" class=\"ga-card\" role=\"dialog\" aria-modal=\"true\">\n    <button type=\"button\" id=\"ga-close\" class=\"ga-close\" aria-label=\"Close\">\u00d7<\/button>\n    <div id=\"ga-card-body\"><\/div>\n  <\/div>\n<\/div>\n\n<style>\n#glenarm-archive {\n  font-family:-apple-system,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif;\n  max-width:1100px; margin:0 auto; color:#333;\n}\n.ga-searchbar { display:flex; align-items:center; gap:14px; margin:0 0 20px; }\n#ga-search {\n  flex:1; border:1px solid #ddd; border-radius:30px; padding:14px 22px;\n  font-size:16px; box-shadow:0 2px 8px rgba(0,0,0,0.08); outline:none;\n}\n#ga-search:focus { border-color:#17a2b8; box-shadow:0 2px 12px rgba(23,162,184,0.25); }\n#ga-search:disabled { background:#f7f7f7; }\n#ga-count { font-size:14px; color:#888; white-space:nowrap; }\n\n.ga-welcome { text-align:center; padding:60px 20px; color:#555; }\n.ga-welcome h2 { color:#17a2b8; font-weight:600; margin:0 0 10px; }\n.ga-welcome p { margin:0; font-size:18px; color:#888; }\n\n.ga-loading { text-align:center; padding:40px; color:#888; font-style:italic; }\n.ga-error { text-align:center; padding:30px; color:#b94a48; background:#fdf3f3; border-radius:10px; }\n\n.ga-table { width:100%; border-collapse:separate; border-spacing:0 8px; background:transparent; }\n.ga-table th { background:#17a2b8; color:#fff; font-weight:600; padding:16px 18px; text-align:left; }\n.ga-table th:first-child { border-radius:10px 0 0 10px; }\n.ga-table th:last-child  { border-radius:0 10px 10px 0; }\n.ga-table tbody tr {\n  background:#fff; box-shadow:0 2px 6px rgba(0,0,0,0.06);\n  transition:transform .15s ease, box-shadow .15s ease; cursor:pointer;\n}\n.ga-table tbody tr:hover { transform:translateY(-2px); box-shadow:0 6px 16px rgba(0,0,0,0.12); }\n.ga-table td { padding:14px 18px; border-bottom:1px solid #f0f0f0; color:#333; vertical-align:middle; }\n.ga-table td:first-child { border-radius:10px 0 0 10px; }\n.ga-table td:last-child  { border-radius:0 10px 10px 0; }\n.ga-thumb { width:52px; height:52px; object-fit:cover; border-radius:50%; border:2px solid #17a2b8; box-shadow:0 2px 5px rgba(0,0,0,0.15); }\n.ga-thumb-empty { width:52px; height:52px; border-radius:50%; background:#e8e8e8; display:inline-block; }\n\n.ga-pagination { display:flex; justify-content:center; gap:6px; margin:20px 0; flex-wrap:wrap; }\n.ga-pagination button { border:1px solid #ddd; background:#fff; color:#333; padding:8px 14px; border-radius:8px; cursor:pointer; font-size:14px; }\n.ga-pagination button.active { background:#17a2b8; color:#fff; border-color:#17a2b8; }\n.ga-pagination button:disabled { opacity:.4; cursor:default; }\n\n.ga-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.5); z-index:999999; align-items:center; justify-content:center; padding:24px; }\n.ga-overlay.open { display:flex; }\n.ga-card {\n  position:relative; background:#f1f1f1; border-radius:16px; box-shadow:0 24px 70px rgba(0,0,0,.4);\n  max-width:420px; min-width:280px; max-height:85vh; overflow-y:auto; overflow-x:hidden;\n  padding:36px 40px; text-align:center;\n}\n.ga-close { position:absolute; top:12px; right:16px; border:none; background:transparent; font-size:30px; line-height:1; color:#999; cursor:pointer; padding:4px 10px; }\n.ga-close:hover { color:#17a2b8; }\n.ga-card .ga-line { font-size:19px; line-height:1.6; margin:6px 0; color:#333; }\n.ga-card .ga-label { font-weight:700; color:#222; }\n.ga-card .ga-photo { display:block; margin:20px auto 0; max-width:240px; max-height:300px; width:auto; height:auto; object-fit:contain; border:1px solid #ccc; border-radius:12px; }\n\n@media (max-width:600px) {\n  .ga-table, .ga-table tbody, .ga-table tr, .ga-table td { display:block; width:100%; }\n  .ga-table thead { display:none; }\n  .ga-table tbody tr { margin:0 0 14px; padding:14px 16px; border-radius:12px; }\n  .ga-table td { border:none; border-bottom:1px solid #f3f3f3; padding:8px 0; }\n  .ga-table td:last-child { border-bottom:none; }\n  .ga-table td:before { content:attr(data-label) \": \"; font-weight:700; color:#222; }\n  .ga-table td:last-child:before { content:\"\"; }\n  .ga-card { max-width:90vw; padding:28px 24px; }\n  .ga-card .ga-line { font-size:17px; }\n  .ga-card .ga-photo { max-width:70vw; max-height:40vh; }\n}\n<\/style>\n\n<script>\n(function () {\n  \/* ============================================================\n     STEP 1 \u2014 set this to your CSV's URL on the site.\n     Example after uploading to Media Library:\n     var CSV_URL = \"https:\/\/glenarmcastleheritage.com\/wp-content\/uploads\/2026\/06\/glenarm-staff.csv\";\n     ============================================================ *\/\n  var CSV_URL = \"PASTE_YOUR_CSV_URL_HERE\";\n\n  var ROWS_PER_PAGE = 5;\n  var currentPage = 1;\n  var WORKERS = [];\n  var filtered = [];\n\n  var search   = document.getElementById('ga-search');\n  var welcome  = document.getElementById('ga-welcome');\n  var loading  = document.getElementById('ga-loading');\n  var errorEl  = document.getElementById('ga-error');\n  var table    = document.getElementById('ga-table');\n  var tbody    = document.getElementById('ga-tbody');\n  var pager    = document.getElementById('ga-pagination');\n  var countEl  = document.getElementById('ga-count');\n  var overlay  = document.getElementById('ga-overlay');\n  var cardBody = document.getElementById('ga-card-body');\n\n  \/* ---------- Load + parse the CSV ---------- *\/\n  function loadCSV() {\n    if (CSV_URL === \"PASTE_YOUR_CSV_URL_HERE\") {\n      showError(\"CSV not set yet. Open this block and replace PASTE_YOUR_CSV_URL_HERE with your CSV file's URL.\");\n      return;\n    }\n    welcome.style.display = 'none';\n    loading.style.display = '';\n    \/\/ cache-bust so replacing the file shows new data on reload\n    fetch(CSV_URL + (CSV_URL.indexOf('?') === -1 ? '?' : '&') + 'v=' + Date.now())\n      .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.text(); })\n      .then(function (text) {\n        WORKERS = parseCSV(text);\n        loading.style.display = 'none';\n        welcome.style.display = '';\n        search.disabled = false;\n        search.focus();\n      })\n      .catch(function (err) {\n        showError(\"Could not load the archive data. Check the CSV URL is correct and the file is public. (\" + err.message + \")\");\n      });\n  }\n\n  function showError(msg) {\n    loading.style.display = 'none';\n    welcome.style.display = 'none';\n    errorEl.style.display = '';\n    errorEl.textContent = msg;\n  }\n\n  \/* Robust CSV parser: handles quotes, commas in quotes, CRLF *\/\n  function parseCSV(text) {\n    var rows = [];\n    var row = [], field = '', inQuotes = false;\n    for (var i = 0; i < text.length; i++) {\n      var c = text[i], next = text[i+1];\n      if (inQuotes) {\n        if (c === '\"' &#038;&#038; next === '\"') { field += '\"'; i++; }\n        else if (c === '\"') { inQuotes = false; }\n        else { field += c; }\n      } else {\n        if (c === '\"') { inQuotes = true; }\n        else if (c === ',') { row.push(field); field = ''; }\n        else if (c === '\\n') { row.push(field); rows.push(row); row = []; field = ''; }\n        else if (c === '\\r') { \/* ignore *\/ }\n        else { field += c; }\n      }\n    }\n    if (field !== '' || row.length) { row.push(field); rows.push(row); }\n    if (rows.length < 2) return [];\n\n    \/\/ Map headers to our keys (flexible about header names\/case)\n    var headers = rows[0].map(function (h) { return h.trim().toLowerCase(); });\n    function idx() {\n      for (var a = 0; a < arguments.length; a++) {\n        var k = headers.indexOf(arguments[a]);\n        if (k !== -1) return k;\n      }\n      return -1;\n    }\n    var iFirst = idx('first name','first','firstname');\n    var iSur   = idx('surname','last name','lastname','last');\n    var iDate  = idx('date','dates','year');\n    var iOcc   = idx('occupation','role','job');\n    var iPhoto = idx('photo','image','img','picture','photo url');\n\n    var out = [];\n    for (var r = 1; r < rows.length; r++) {\n      var cells = rows[r];\n      if (cells.length === 1 &#038;&#038; cells[0].trim() === '') continue; \/\/ skip blank lines\n      out.push({\n        first:      get(cells, iFirst),\n        surname:    get(cells, iSur),\n        date:       get(cells, iDate),\n        occupation: get(cells, iOcc),\n        photo:      get(cells, iPhoto)\n      });\n    }\n    return out;\n  }\n  function get(cells, i) { return (i >= 0 && i < cells.length) ? String(cells[i]).trim() : ''; }\n\n  \/* ---------- Render ---------- *\/\n  function matches(w, q) {\n    q = q.toLowerCase();\n    return (w.first + ' ' + w.surname + ' ' + w.date + ' ' + w.occupation).toLowerCase().indexOf(q) !== -1;\n  }\n\n  function render() {\n    var q = search.value.trim();\n    if (q === '') {\n      welcome.style.display = '';\n      table.style.display = 'none';\n      pager.style.display = 'none';\n      countEl.textContent = '';\n      return;\n    }\n    welcome.style.display = 'none';\n    table.style.display = '';\n\n    filtered = WORKERS.filter(function (w) { return matches(w, q); });\n    countEl.textContent = filtered.length + ' result' + (filtered.length === 1 ? '' : 's');\n\n    var totalPages = Math.max(1, Math.ceil(filtered.length \/ ROWS_PER_PAGE));\n    if (currentPage > totalPages) currentPage = 1;\n    var start = (currentPage - 1) * ROWS_PER_PAGE;\n    var pageRows = filtered.slice(start, start + ROWS_PER_PAGE);\n\n    tbody.innerHTML = '';\n    if (pageRows.length === 0) {\n      tbody.innerHTML = '<tr><td colspan=\"5\" style=\"text-align:center;color:#888;padding:40px;font-style:italic;\">No matching records found.<\/td><\/tr>';\n      pager.style.display = 'none';\n      return;\n    }\n    pageRows.forEach(function (w) {\n      var tr = document.createElement('tr');\n      tr.innerHTML =\n        '<td data-label=\"First Name\">' + esc(w.first) + '<\/td>' +\n        '<td data-label=\"Surname\">' + esc(w.surname) + '<\/td>' +\n        '<td data-label=\"Date\">' + esc(w.date) + '<\/td>' +\n        '<td data-label=\"Occupation\">' + esc(w.occupation) + '<\/td>' +\n        '<td data-label=\"Photo\">' + (w.photo ? '<img decoding=\"async\" class=\"ga-thumb\" src=\"' + esc(w.photo) + '\" alt=\"\">' : '<span class=\"ga-thumb-empty\"><\/span>') + '<\/td>';\n      tr.addEventListener('click', function () { openCard(w); });\n      tbody.appendChild(tr);\n    });\n    renderPager(totalPages);\n  }\n\n  function renderPager(totalPages) {\n    if (totalPages <= 1) { pager.style.display = 'none'; return; }\n    pager.style.display = 'flex';\n    pager.innerHTML = '';\n    pager.appendChild(btn('\u2039', currentPage === 1, function () { currentPage--; render(); }));\n    for (var p = 1; p <= totalPages; p++) {\n      (function (page) {\n        var b = btn(String(page), false, function () { currentPage = page; render(); });\n        if (page === currentPage) b.className = 'active';\n        pager.appendChild(b);\n      })(p);\n    }\n    pager.appendChild(btn('\u203a', currentPage === totalPages, function () { currentPage++; render(); }));\n  }\n  function btn(label, disabled, onClick) {\n    var b = document.createElement('button'); b.textContent = label; b.disabled = disabled;\n    b.addEventListener('click', onClick); return b;\n  }\n\n  function openCard(w) {\n    var html = '';\n    html += '<p class=\"ga-line\"><span class=\"ga-label\">First Name:<\/span> ' + esc(w.first) + '<\/p>';\n    html += '<p class=\"ga-line\"><span class=\"ga-label\">Surname:<\/span> ' + esc(w.surname) + '<\/p>';\n    html += '<p class=\"ga-line\"><span class=\"ga-label\">Date:<\/span> ' + esc(w.date) + '<\/p>';\n    html += '<p class=\"ga-line\"><span class=\"ga-label\">Occupation:<\/span> ' + esc(w.occupation) + '<\/p>';\n    if (w.photo) html += '<img decoding=\"async\" class=\"ga-photo\" src=\"' + esc(w.photo) + '\" alt=\"\">';\n    cardBody.innerHTML = html;\n    overlay.classList.add('open');\n  }\n  function closeCard() { overlay.classList.remove('open'); cardBody.innerHTML = ''; }\n\n  function esc(s) {\n    return String(s == null ? '' : s).replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;').replace(\/\"\/g,'&quot;');\n  }\n\n  search.addEventListener('input', function () { currentPage = 1; render(); });\n  document.getElementById('ga-close').addEventListener('click', closeCard);\n  overlay.addEventListener('click', function (e) { if (e.target === overlay) closeCard(); });\n  document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeCard(); });\n\n  loadCSV();\n})();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Welcome to the Glenarm Castle Digital Archive Search above for past and present staff members. Loading archive\u2026 First Name Surname Date Occupation Photo \u00d7<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-10","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/pages\/10","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10"}],"version-history":[{"count":3,"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/pages\/10\/revisions"}],"predecessor-version":[{"id":18,"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=\/wp\/v2\/pages\/10\/revisions\/18"}],"wp:attachment":[{"href":"https:\/\/kiosk.glenarmcastleheritage.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}