{"id":5940,"date":"2025-09-29T11:16:34","date_gmt":"2025-09-29T05:46:34","guid":{"rendered":"https:\/\/tbspanthers.british-school.org\/?page_id=5940"},"modified":"2025-10-06T10:27:31","modified_gmt":"2025-10-06T04:57:31","slug":"wall-of-fame","status":"publish","type":"page","link":"https:\/\/tbspanthers.british-school.org\/index.php\/wall-of-fame\/","title":{"rendered":""},"content":{"rendered":"\n<!-- Panthers | Wall of Fame intro (WP-ready, scoped styles) -->\n<section id=\"wof-intro\" aria-label=\"Wall of Fame\" style=\"box-sizing:border-box;\">\n  <style>\n    \/* ===== Scoped to #wof-intro only ===== *\/\n    #wof-intro{\n      --ink:#0e2038;         \/* Panthers dark blue *\/\n      --muted:#5b6b84;\n      --gold:#FEB21A;        \/* Panthers gold *\/\n      --bg:#f7f9fc;\n      --line:#e7edf4;\n\n      \/* WIDER + EVEN GUTTERS *\/\n      --wide: 1500px;\n      max-width: none !important;\n      width: min(var(--wide), 94vw) !important;\n      margin-left: calc(50% - (min(var(--wide), 94vw) \/ 2)) !important;\n      margin-right: calc(50% - (min(var(--wide), 94vw) \/ 2)) !important;\n\n      margin-top: 28px;\n      margin-bottom: 28px;\n      padding: 0 16px;\n\n      font-family:Segoe UI,system-ui,-apple-system,Inter,Roboto,Arial,sans-serif;\n    }\n    #wof-intro .frame{\n      background: var(--bg);\n      border: 1px solid var(--line);\n      border-radius: 16px;\n      padding: 22px 22px 20px;\n      position: relative;\n      overflow: hidden;\n    }\n    \/* subtle gold accent bar *\/\n    #wof-intro .frame::before{\n      content:\"\";\n      position:absolute;\n      left:0; top:0;\n      width:100%;\n      height:6px;\n      background: linear-gradient(90deg, var(--gold), #ffd36b);\n    }\n    #wof-intro h1{\n      margin: 8px 0 10px;\n      color: var(--ink);\n      font-size: clamp(28px, 3.2vw, 38px);\n      line-height: 1.15;\n      letter-spacing: .2px;\n    }\n    \/* decorative underline under the title *\/\n    #wof-intro h1::after{\n      content:\"\";\n      display:block;\n      width: 72px;\n      height: 6px;\n      border-radius: 999px;\n      background: var(--gold);\n      margin-top: 10px;\n    }\n    #wof-intro p{\n      margin: 12px 0 6px;\n      color: var(--muted);\n      font-size: clamp(15px, 1.6vw, 17px);\n      line-height: 1.7;\n    }\n    \/* Compact on very small screens *\/\n    @media (max-width:520px){\n      #wof-intro .frame{ padding:18px 16px; border-radius:14px; }\n      #wof-intro h1::after{ width:56px; height:5px; }\n    }\n  <\/style>\n\n  <div class=\"frame\">\n    <h1>Wall of Fame<\/h1>\n    <p>\n      Celebrating our standout students for their dedication, sportsmanship, and achievements across events.\n      Explore highlights from recent competitions, discover the stories behind each win, and get inspired by the effort that made it happen.\n    <\/p>\n  <\/div>\n<\/section>\n\n<!-- ===== Feed container (scoped styles + working IDs) ===== -->\n<style>\n  .wof-container {\n    \/* WIDER + EVEN GUTTERS (matches intro) *\/\n    --wide: 1500px;\n    max-width: none !important;\n    width: min(var(--wide), 94vw) !important;\n    margin-left: calc(50% - (min(var(--wide), 94vw) \/ 2)) !important;\n    margin-right: calc(50% - (min(var(--wide), 94vw) \/ 2)) !important;\n\n    margin-top: 0;\n    margin-bottom: 16px;\n    padding: 0 16px;\n\n    font-family: 'Segoe UI', system-ui, -apple-system, Inter, Roboto, Arial, sans-serif;\n    color: #0f1a2b;\n    box-sizing: border-box;\n  }\n\n  \/* Search *\/\n  .filters-wrapper {\n    margin: 10px 0 0;\n    display: flex;\n    flex-wrap: wrap;\n    gap: 14px;\n    justify-content: center;\n  }\n  .filters-wrapper input[type=\"search\"] {\n    padding: 10px 14px;\n    font-size: 16px;\n    border-radius: 10px;\n    border: 1px solid #d7dbe0;\n    min-width: 320px;\n    outline: none;\n  }\n\n  \/* Grid: 3 \/ row desktop (unchanged) *\/\n  .achiever-container {\n    display: grid;\n    grid-template-columns: repeat(3, minmax(0, 1fr));\n    gap: 24px;\n    margin-top: 18px;\n  }\n  @media (max-width: 1000px) { .achiever-container { grid-template-columns: repeat(2, minmax(0, 1fr)); } }\n  @media (max-width: 620px)  { .achiever-container { grid-template-columns: 1fr; } }\n  .achiever-container.limit-width { display: flex; justify-content: center; flex-wrap: wrap; }\n\n  \/* ===== Animations ===== *\/\n  @keyframes wofFadeUp {\n    from { opacity: 0; transform: translateY(14px) scale(.98); }\n    to   { opacity: 1; transform: translateY(0) scale(1); }\n  }\n\n  \/* Card with multicolour left border *\/\n  .achiever-card {\n    background: #ffffff;\n    border-radius: 16px;\n    overflow: hidden;\n    color: #0f1a2b;\n    display: flex;\n    flex-direction: column;\n    align-items: stretch;\n    box-shadow: 0 8px 20px rgba(0, 0, 0, 0.08);\n    position: relative;\n    text-align: left;\n    width: 100%;\n    border: 1px solid #eef1f6;\n    border-left: 8px solid var(--accent, #8bd3dd);\n\n    \/* Animation on load + smooth hover *\/\n    opacity: 0;\n    animation: wofFadeUp .6s cubic-bezier(.2,.7,.2,1) forwards;\n    transition: transform .18s ease, box-shadow .22s ease;\n    will-change: transform;\n  }\n  .achiever-card:hover {\n    transform: translateY(-4px) scale(1.01);\n    box-shadow: 0 12px 26px rgba(0,0,0,.12);\n  }\n\n  \/* Nice image zoom on hover *\/\n  .achiever-card .post-image {\n    width: 100%;\n    height: 370px;\n    object-fit: cover;\n    object-position: center;\n    display: block;\n    background: #f2f4f8;\n    transition: transform .35s ease;\n    will-change: transform;\n  }\n  .achiever-card:hover .post-image { transform: scale(1.03); }\n\n  \/* Respect reduced motion *\/\n  @media (prefers-reduced-motion: reduce){\n    .achiever-card { animation: none; opacity: 1; transition: none; }\n    .achiever-card .post-image { transition: none; }\n  }\n\n  \/* Cycle 10 soothing colors on the left border *\/\n  .achiever-card:nth-child(10n+1)  { --accent: #8bd3dd; }\n  .achiever-card:nth-child(10n+2)  { --accent: #bde0fe; }\n  .achiever-card:nth-child(10n+3)  { --accent: #a3d9a5; }\n  .achiever-card:nth-child(10n+4)  { --accent: #f9e79f; }\n  .achiever-card:nth-child(10n+5)  { --accent: #ffd6a5; }\n  .achiever-card:nth-child(10n+6)  { --accent: #f7cac9; }\n  .achiever-card:nth-child(10n+7)  { --accent: #cdb4db; }\n  .achiever-card:nth-child(10n+8)  { --accent: #9b9ece; }\n  .achiever-card:nth-child(10n+9)  { --accent: #7aa2f7; }\n  .achiever-card:nth-child(10n+10) { --accent: #c2f0c2; }\n\n  \/* Dark-blue post header with white text *\/\n  .post-header {\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    padding: 14px 16px;\n    background: #0b2a6f;\n    color: #ffffff;\n    position: relative;\n  }\n  .post-avatar {\n    width: 64px !important;\n    height: 64px !important;\n    border-radius: 50%;\n    object-fit: cover;\n    object-position: center;\n    background: #e9eef6;\n    border: 1px solid #e3e8f0;\n    flex: 0 0 auto;\n  }\n  .post-meta { display: flex; flex-direction: column; line-height: 1.25; }\n  .post-username { font-weight: 800; font-size: 16px; color: #ffffff; }\n  .post-sub-cat { font-size: 13px; color: rgba(255,255,255,0.92); font-weight: 600; }\n  .post-activity { font-size: 12.5px; color: rgba(255,255,255,0.92); font-style: italic; }\n\n  \/* Gold Achiever badge *\/\n  .achiever-badge {\n    position: absolute;\n    right: 12px;\n    top: 12px;\n    background: #FFD700;\n    color: #0b2a6f;\n    font-weight: 800;\n    border-radius: 999px;\n    padding: 6px 12px;\n    font-size: 12px;\n    letter-spacing: .2px;\n    box-shadow: 0 2px 8px rgba(0,0,0,.1);\n  }\n\n  \/* Body *\/\n  .post-body { padding: 14px 16px 16px; }\n  .post-caption {\n    font-size: 14.5px;\n    color: #162843;\n    line-height: 1.6;\n    margin: 0 0 10px;\n    word-break: break-word;\n  }\n\n  \/* Footer: date left, socials right *\/\n  .post-footer {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 12px;\n  }\n  .post-date { font-size: 12px; color: #8d9ab2; }\n  .post-socials { display: flex; gap: 12px; }\n  .post-socials i { font-size: 16px; color: #0b2a6f; transition: color .2s; }\n  .post-socials i:hover { color: #163a96; }\n\n  \/* Legacy (kept harmless) *\/\n  .yellow-line, .month-badge { display: none; }\n  .achiever-info, .achiever-desc { display: none; }\n<\/style>\n\n<div class=\"wof-container\">\n  <div class=\"filters-wrapper\">\n    <input id=\"searchStudent\" type=\"search\" placeholder=\"Search student by name\u2026\" aria-label=\"Search student\">\n  <\/div>\n\n  <div id=\"achieverCards\" class=\"achiever-container\">Loading achievers&#8230;<\/div>\n<\/div>\n\n<script>\n  document.addEventListener(\"DOMContentLoaded\", function () {\n    const csvUrl = \"https:\/\/docs.google.com\/spreadsheets\/d\/e\/2PACX-1vRQmnqZg28u6ywwpGsFs7HHcBF5uf_bj9jppYDmuSAqf1NY67RpVXCtbUB0Qkgf2lA6K6zdbJy0Sdfr\/pub?gid=0&single=true&output=csv\";\n\n    let allPlayers = [];\n    const container = document.getElementById(\"achieverCards\");\n    const searchBox = document.getElementById(\"searchStudent\");\n\n    \/* ========== Google Drive helpers (your functions) ========== *\/\n    function extractDriveId_(url) {\n      if (!url) return '';\n      \/\/ \/file\/d\/FILE_ID\/\n      let m = url.match(\/\\\/file\\\/d\\\/([a-zA-Z0-9_-]{20,})\/);\n      if (m) return m[1];\n      \/\/ ...?id=FILE_ID\n      m = url.match(\/[?&]id=([a-zA-Z0-9_-]{20,})\/);\n      if (m) return m[1];\n      \/\/ \/d\/FILE_ID\n      m = url.match(\/\\\/d\\\/([a-zA-Z0-9_-]{20,})\/);\n      if (m) return m[1];\n      \/\/ Last resort: longest plausible token\n      m = url.match(\/([a-zA-Z0-9_-]{25,})\/);\n      return m ? m[1] : '';\n    }\n    \/** Build a fast CDN link for public Drive images (works when file is shared \"Anyone with link\") *\/\n    function buildFastDriveThumb_(fileId, size) {\n      if (!fileId) return '';\n      return \"https:\/\/lh3.googleusercontent.com\/d\/\" + fileId + \"=s\" + size;\n    }\n    function toDriveImageCandidates(url, size){\n      const id = extractDriveId_(url);\n      if (!id) return null;\n      return [\n        buildFastDriveThumb_(id, size),                          \/\/ fast CDN thumb\n        \"https:\/\/drive.google.com\/uc?export=view&id=\" + id,      \/\/ view endpoint\n        \"https:\/\/drive.google.com\/thumbnail?id=\" + id + \"&sz=w\" + size \/\/ thumbnail api\n      ];\n    }\n    function chooseImageSrc(url, size){\n      if (!url) return {src:\"\", fallbacks:[]};\n      \/\/ If it's a Drive link, return candidates; else direct URL\n      const candidates = toDriveImageCandidates(url, size);\n      if (candidates) return {src: candidates[0], fallbacks: candidates.slice(1)};\n      return {src: url, fallbacks: []};\n    }\n    \/\/ Generic image onerror fallback handler\n    window.wofImgFallback = function(img){\n      const listStr = img.getAttribute('data-fallbacks') || '';\n      if (!listStr) {\n        img.onerror = null;\n        img.src = 'https:\/\/tbspanthers.british-school.org\/wp-content\/uploads\/2024\/12\/TBS.png';\n        return;\n      }\n      const arr = listStr.split('||');\n      const next = arr.shift();\n      img.setAttribute('data-fallbacks', arr.join('||'));\n      img.src = next || 'https:\/\/tbspanthers.british-school.org\/wp-content\/uploads\/2024\/12\/TBS.png';\n    };\n\n    \/* ========== Helpers ========== *\/\n    function parseUSDate(mdy) {\n      if (!mdy) return null;\n      const m = mdy.trim().match(\/^(\\d{1,2})\\\/(\\d{1,2})\\\/(\\d{4})$\/);\n      if (!m) return null;\n      const month = parseInt(m[1], 10);\n      const day   = parseInt(m[2], 10);\n      const year  = parseInt(m[3], 10);\n      const d = new Date(year, month - 1, day);\n      if (d.getFullYear() !== year || (d.getMonth()+1) !== month || d.getDate() !== day) return null;\n      return d;\n    }\n    function formatDateNice(mdy) {\n      const d = parseUSDate(mdy);\n      return d\n        ? d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })\n        : (mdy || '');\n    }\n    function esc(s) {\n      return (s ?? '').toString()\n        .replace(\/&\/g,'&amp;').replace(\/<\/g,'&lt;').replace(\/>\/g,'&gt;')\n        .replace(\/\"\/g,'&quot;').replace(\/'\/g,'&#39;');\n    }\n    function escWithBreaks(s){\n      return esc(s).replace(\/\\r?\\n\/g,'<br>');\n    }\n\n    \/* ========== Fetch & parse CSV (A\u2013H) ========== *\/\n    fetch(csvUrl)\n      .then((response) => response.text())\n      .then((csv) => {\n        const rows = csv.trim().split(\"\\n\");\n\n        \/\/ Columns: Date | Category | Activity | Gender | Student | ProfilePhoto | Description | Event Photo\n        allPlayers = rows.slice(1).map(row => {\n          const values = row.split(\",\").map(cell => cell.trim());\n          const date       = values[0] || \"\";\n          const category   = values[1] || \"\";\n          const activity   = values[2] || \"\";\n          const gender     = values[3] || \"\";\n          const student    = values[4] || \"\";\n          const photo      = values[5] || \"\";\n          const eventPhotoRaw = values[values.length - 1] || \"\";\n          const description = values.length > 8 ? values.slice(6, -1).join(\",\") : (values[6] || \"\");\n          return {\n            date,\n            dateNice: formatDateNice(date),\n            category,\n            activity,\n            gender,\n            student,\n            photo,\n            description,\n            eventPhoto: eventPhotoRaw\n          };\n        });\n\n        \/\/ Sort by category ladder\n        const order = [\"U9\", \"U11\", \"U14\", \"U17\", \"U19\"];\n        allPlayers.sort((a, b) => order.indexOf(a.category) - order.indexOf(b.category));\n\n        renderCards(allPlayers);\n      })\n      .catch((error) => {\n        container.textContent = \"Failed to load achievers.\";\n        console.error(\"CSV Fetch Error:\", error);\n      });\n\n    \/* ========== Render ========== *\/\n    function renderCards(list) {\n      const q = (searchBox && searchBox.value ? searchBox.value : \"\").trim().toLowerCase();\n      const filtered = list.filter(p => !q || (p.student || \"\").toLowerCase().indexOf(q) !== -1);\n\n      container.classList.toggle(\"limit-width\", filtered.length <= 2);\n      container.innerHTML = \"\";\n\n      if (!filtered.length) {\n        container.innerHTML = \"<p style='color:#708099;'>No achievers found.<\/p>\";\n        return;\n      }\n\n      filtered.forEach(p => {\n        \/\/ Avatar: allow Drive or direct link; smaller size thumb is fine\n        const avatarPick = chooseImageSrc(p.photo, 512);\n        const avatarSrc = avatarPick.src || \"data:image\/svg+xml;utf8,\" + encodeURIComponent('<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 40 40\"><circle cx=\"20\" cy=\"20\" r=\"20\" fill=\"#e9eef6\"\/><circle cx=\"20\" cy=\"15\" r=\"7\" fill=\"#9aa7bd\"\/><path d=\"M6 34c3.5-6.5 10-10 14-10s10.5 3.5 14 10\" fill=\"#9aa7bd\"\/><\/svg>');\n        const avatarFallbacks = avatarPick.fallbacks.join('||');\n\n        \/\/ Event photo: prefer large thumb; fallbacks chained\n        const heroPick = chooseImageSrc(p.eventPhoto, 2000);\n        const heroSrc = heroPick.src || \"https:\/\/tbspanthers.british-school.org\/wp-content\/uploads\/2024\/12\/TBS.png\";\n        const heroFallbacks = heroPick.fallbacks.join('||');\n\n        const card = document.createElement(\"div\");\n        card.className = \"achiever-card\";\n        card.innerHTML = `\n          <div class=\"post-header\">\n            <img decoding=\"async\" class=\"post-avatar\" src=\"${esc(avatarSrc)}\" alt=\"${esc(p.student)}\"\n                 data-fallbacks=\"${esc(avatarFallbacks)}\" onerror=\"wofImgFallback(this)\">\n            <div class=\"post-meta\">\n              <div class=\"post-username\">${esc(p.student)}<\/div>\n              <div class=\"post-sub-cat\">${esc(p.category)}<\/div>\n              <div class=\"post-activity\">${esc(p.activity)}<\/div>\n            <\/div>\n            <div class=\"achiever-badge\">Achiever<\/div>\n          <\/div>\n\n          <img decoding=\"async\" class=\"post-image\" src=\"${esc(heroSrc)}\" alt=\"${esc(p.activity)} event photo\"\n               data-fallbacks=\"${esc(heroFallbacks)}\" onerror=\"wofImgFallback(this)\">\n\n          <div class=\"post-body\">\n            <p class=\"post-caption\">${escWithBreaks(p.description)}<\/p>\n            <div class=\"post-footer\">\n              <div class=\"post-date\">${esc(p.dateNice)}<\/div>\n              <div class=\"post-socials\" aria-label=\"social links\">\n                <i class=\"fab fa-facebook-f\" title=\"Facebook\"><\/i>\n                <i class=\"fab fa-x-twitter\" title=\"X\"><\/i>\n                <i class=\"fab fa-instagram\" title=\"Instagram\"><\/i>\n              <\/div>\n            <\/div>\n          <\/div>\n        `;\n        container.appendChild(card);\n      });\n    }\n\n    \/\/ Debounced search (keep ID intact)\n    let t;\n    if (searchBox){\n      searchBox.addEventListener('input', function(){\n        clearTimeout(t);\n        t = setTimeout(function(){ renderCards(allPlayers); }, 150);\n      });\n    }\n  });\n<\/script>\n\n<!-- Font Awesome for social icons -->\n<link rel=\"stylesheet\" href=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/font-awesome\/6.5.0\/css\/all.min.css\">\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Wall of Fame Celebrating our standout students for their dedication, sportsmanship, and achievements across events. Explore highlights from recent competitions, discover the stories behind each [&hellip;]<\/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-5940","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/pages\/5940"}],"collection":[{"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/comments?post=5940"}],"version-history":[{"count":15,"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/pages\/5940\/revisions"}],"predecessor-version":[{"id":6073,"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/pages\/5940\/revisions\/6073"}],"wp:attachment":[{"href":"https:\/\/tbspanthers.british-school.org\/index.php\/wp-json\/wp\/v2\/media?parent=5940"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}