{"id":6103,"date":"2026-10-26T12:33:44","date_gmt":"2026-10-26T12:33:44","guid":{"rendered":"https:\/\/sites.npolar.no\/quantarctica\/?page_id=6103"},"modified":"2026-10-29T08:12:12","modified_gmt":"2026-10-29T08:12:12","slug":"data-catalog","status":"publish","type":"page","link":"https:\/\/sites.npolar.no\/quantarctica\/data-catalog\/","title":{"rendered":"Data Catalog"},"content":{"rendered":"\n<div id=\"data-catalog-root\" class=\"qa-catalog-container\">\n  <div class=\"qa-catalog-layout\">\n    <!-- Sidebar \/ Tree Filter -->\n    <aside class=\"qa-catalog-sidebar\" aria-label=\"Dataset Filters\">\n      <div class=\"qa-search-wrap\">\n        <label for=\"catalog-search-input\" class=\"qa-visually-hidden\">Search datasets&#8230;<\/label>\n        <input type=\"search\" id=\"catalog-search-input\" placeholder=\"Search datasets...\" class=\"qa-catalog-search-input\">\n      <\/div>\n\n      <div class=\"qa-catalog-sidebar-header\">\n        <h2 id=\"qa-groups-heading\">Themes<\/h2>\n        <button type=\"button\" id=\"catalog-clear-all-btn\" class=\"qa-catalog-btn-reset\" style=\"display: none;\">Clear\n          All<\/button>\n      <\/div>\n      <div id=\"catalog-tree-container\" role=\"tree\" aria-labelledby=\"qa-groups-heading\">\n        <p aria-live=\"polite\">Loading Themes&#8230;<\/p>\n      <\/div>\n\n<span class=\"qa-note\"><strong> Note:<\/strong> Quantarctica includes more than 640 individual map layers. The Data Catalog groups related layers into datasets, so the number of catalog entries shown here is smaller than the total number of layers.<\/span>\n\n    <\/aside>\n\n    <!-- Main Section -->\n    <main class=\"qa-catalog-main\">\n      <header class=\"qa-catalog-header\">\n        <div class=\"qa-catalog-results-header-row\">\n          <h2 id=\"catalog-results-title\" class=\"qa-catalog-results-title\">All Datasets<\/h2>\n          <span id=\"catalog-results-count\" class=\"qa-catalog-results-badge\" aria-live=\"polite\" aria-atomic=\"true\">Loading datasets&#8230;<\/span>\n        <\/div>\n        <nav id=\"catalog-breadcrumbs\" class=\"qa-catalog-breadcrumbs\" aria-label=\"Breadcrumb\"><\/nav>\n      <\/header>\n\n      <!-- Theme Editors Section -->\n      <section id=\"catalog-editors\" class=\"qa-catalog-editors\" aria-label=\"Theme Editors\"><\/section>\n\n      <section id=\"catalog-grid\" class=\"qa-catalog-grid\" aria-live=\"polite\" aria-busy=\"false\" aria-label=\"Datasets Grid\"><\/section>\n      <nav id=\"catalog-pagination\" class=\"qa-catalog-pagination\" aria-label=\"Pagination Navigation\"><\/nav>\n    <\/main>\n  <\/div>\n<\/div>\n\n<style>\n  .qa-visually-hidden {\n    position: absolute !important;\n    width: 1px !important;\n    height: 1px !important;\n    padding: 0 !important;\n    margin: -1px !important;\n    overflow: hidden !important;\n    clip: rect(0, 0, 0, 0) !important;\n    white-space: nowrap !important;\n    border: 0 !important;\n  }\n.qa-note {\n  display: block;\n  padding: 0.75rem 0rem;\n  font-size: 0.8rem;\n  line-height: 1.45;\n}\n  p {\n    margin: 0 !important;\n  }\n\n  .qa-catalog-container {\n    width: 100%;\n  }\n\n  .qa-catalog-layout {\n    display: flex;\n    flex-direction: column;\n    gap: 1rem;\n  }\n\n  @media (min-width: 900px) {\n    .qa-catalog-layout {\n      flex-direction: row;\n      align-items: flex-start;\n    }\n\n    .qa-catalog-sidebar {\n      width: 320px;\n      flex-shrink: 0;\n    }\n  }\n\n  \/* Sidebar *\/\n  .qa-catalog-sidebar {\n    border: 1px solid #d0d0d0;\n    border-radius: 6px;\n    padding: 1rem;\n    box-sizing: border-box;\n    background: #fff;\n  }\n\n  .qa-search-wrap {\n    margin-bottom: 1rem;\n  }\n\n  .qa-catalog-search-input {\n    padding: 0.5rem 0.75rem;\n    border: 1px solid #767676;\n    border-radius: 4px;\n    font-size: 0.9rem;\n    width: 100%;\n    box-sizing: border-box;\n    transition: all 0.2s ease;\n  }\n\n  .qa-catalog-search-input.has-filter {\n    border-color: #0056b3;\n    background-color: #f4f8ff;\n  }\n\n  .qa-catalog-search-input:focus-visible {\n    outline: 2px solid #0056b3;\n    outline-offset: 1px;\n  }\n\n  .qa-catalog-sidebar-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    border-bottom: 1px solid #e0e0e0;\n    padding-bottom: 0.5rem;\n    margin-bottom: 0.75rem;\n  }\n\n  .qa-catalog-sidebar-header h2 {\n    margin: 0;\n    font-size: 0.95rem;\n    text-transform: uppercase;\n    letter-spacing: 0.05em;\n    color: #111;\n  }\n\n  .qa-catalog-btn-reset {\n    background: transparent;\n    border: none;\n    cursor: pointer;\n    font-size: 0.8rem;\n    padding: 0.15rem 0.4rem;\n    text-decoration: underline;\n    color: #d9381e;\n    font-weight: 600;\n  }\n\n  .qa-catalog-btn-reset:hover {\n    color: #a82510;\n  }\n\n  .qa-catalog-btn-reset:focus-visible {\n    outline: 2px solid #0056b3;\n    outline-offset: 2px;\n    border-radius: 2px;\n  }\n\n  \/* Tree Navigation *\/\n  #catalog-tree-container ul,\n  #catalog-tree-container li {\n    list-style: none;\n    margin: 0;\n    padding: 0;\n    width: 100%;\n  }\n\n  .qa-catalog-tree-row {\n    display: flex;\n    align-items: flex-start;\n    width: 100%;\n    padding: 0.4rem 0.5rem;\n    border: none;\n    background: transparent;\n    border-radius: 4px;\n    cursor: pointer;\n    font-size: 0.875rem;\n    color: #222;\n    text-align: left;\n    box-sizing: border-box;\n    transition: background-color 0.15s;\n  }\n\n  .qa-catalog-tree-row:hover {\n    background-color: #f2f2f2;\n  }\n\n  .qa-catalog-tree-row:focus-visible {\n    outline: 2px solid #0056b3;\n    outline-offset: 2px;\n  }\n\n  .qa-catalog-tree-row.active {\n    font-weight: bold;\n    background-color: #e2ecf8;\n    color: #004488;\n    border-left: 3px solid #0056b3;\n  }\n\n  .qa-catalog-tree-toggle {\n    border: none;\n    background: transparent;\n    cursor: pointer;\n    font-size: 0.75rem;\n    color: #444;\n    padding: 0 0.4rem 0 0;\n    line-height: 1.4;\n    flex-shrink: 0;\n  }\n\n  .qa-catalog-tree-label {\n    flex: 1;\n    display: block;\n    min-width: 0;\n    word-break: break-word;\n    line-height: 1.4;\n  }\n\n  \/* Main Section Header & Breadcrumbs *\/\n  .qa-catalog-main {\n    flex: 1;\n    min-width: 0;\n  }\n\n  .qa-catalog-header {\n    border: 1px solid #d0d0d0;\n    border-radius: 6px;\n    padding: 1rem 1.25rem;\n    margin-bottom: 1.5rem;\n    background: #fff;\n    display: flex;\n    flex-direction: column;\n    gap: 0.5rem;\n  }\n\n  .qa-catalog-breadcrumbs:empty {\n    display: none;\n  }\n\n  .qa-catalog-breadcrumbs {\n    margin-bottom: 0.25rem;\n  }\n\n  .qa-breadcrumb-list {\n    display: flex;\n    gap: 0.35rem;\n    flex-wrap: wrap;\n    align-items: center;\n    list-style: none;\n    margin: 0;\n    padding: 0;\n    font-size: 0.825rem;\n    color: #595959;\n  }\n\n  .qa-breadcrumb-item {\n    background: transparent;\n    border: none;\n    padding: 0;\n    cursor: pointer;\n    color: #0056b3;\n    text-decoration: underline;\n    font-size: inherit;\n  }\n\n  .qa-breadcrumb-item:focus-visible {\n    outline-offset: 2px;\n    border-radius: 2px;\n  }\n\n  .qa-breadcrumb-item.active {\n    font-weight: 600;\n    color: #444;\n    text-decoration: none;\n    cursor: default;\n  }\n\n  .qa-breadcrumb-separator {\n    color: #888;\n    font-size: 0.75rem;\n  }\n\n  .qa-catalog-results-header-row {\n    display: flex;\n    align-items: center;\n    gap: 1rem;\n    flex-wrap: wrap;\n  }\n\n  .qa-catalog-results-title {\n    margin: 0;\n    font-size: 1.25rem;\n    font-weight: 700;\n    color: #111;\n    line-height: 1.2;\n  }\n\n  .qa-catalog-results-badge {\n    font-size: 0.85rem;\n    font-weight: 600;\n    background: #f0f4f8;\n    color: #004488;\n    border: 1px solid #cce0f5;\n    padding: 0.25rem 0.65rem;\n    border-radius: 12px;\n    white-space: nowrap;\n  }\n\n  \/* Theme Editors Styling *\/\n  .qa-catalog-editors {\n    border: 1px solid #d0d0d0;\n    border-radius: 6px;\n    padding: 1.25rem;\n    margin-bottom: 1.5rem;\n    background: #fff;\n    display: flex;\n    flex-direction: column;\n    gap: 1rem;\n  }\n\n  .qa-catalog-editors:empty {\n    display: none;\n    margin-bottom: 0;\n    border: none;\n    padding: 0;\n  }\n\n  .qa-editors-heading {\n    margin: 0 0 0.5rem 0;\n    font-size: 1rem;\n    font-weight: 700;\n    color: #111;\n    text-transform: uppercase;\n    letter-spacing: 0.03em;\n  }\n\n  .qa-editors-list {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 1.5rem;\n  }\n\n  .qa-editor-card {\n    display: flex;\n    flex-direction: column;\n    align-items: flex-start;\n    gap: 0.4rem;\n    max-width: 200px;\n  }\n\n  .qa-editor-img-wrap {\n    width: 120px;\n    height: 120px;\n    border-radius: 50%;\n    overflow: hidden;\n    border: 2px solid #d0d0d0;\n    background: #f4f4f4;\n  }\n\n  .qa-editor-img-wrap img {\n    width: 100%;\n    height: 100%;\n    object-fit: cover;\n    display: block;\n  }\n\n  .qa-editor-name {\n    font-weight: 600;\n    font-size: 0.95rem;\n    color: #111;\n    margin: 0;\n    line-height: 1.3;\n  }\n\n  .qa-editor-affiliation {\n    font-size: 0.825rem;\n    color: #555;\n    margin: 0;\n    line-height: 1.3;\n  }\n\n  \/* Grid & Card Layout Responsive Breakpoints *\/\n  .qa-catalog-grid {\n    display: grid;\n    grid-template-columns: 1fr;\n    gap: 1rem;\n  }\n\n  @media (min-width: 1750px) {\n    .qa-catalog-grid {\n      grid-template-columns: repeat(2, 1fr);\n    }\n  }\n\n  @media (min-width: 2300px) {\n    .qa-catalog-grid {\n      grid-template-columns: repeat(3, 1fr);\n    }\n  }\n\n  .qa-catalog-card {\n    border: 1px solid #d0d0d0;\n    border-radius: 6px;\n    padding: 1.25rem;\n    background: #fff;\n    display: flex;\n    flex-direction: column;\n  }\n\n  .qa-catalog-card-header {\n    margin-bottom: 1rem;\n  }\n\n  .qa-catalog-card-code {\n    display: inline-block;\n    font-size: 0.75rem;\n    font-family: monospace;\n    padding: 0.15rem 0.4rem;\n    background: #f0f0f0;\n    border: 1px solid #ccc;\n    border-radius: 3px;\n    margin-bottom: 0.5rem;\n    color: #222;\n  }\n\n  .qa-catalog-card-title {\n    margin: 0 0 0.5rem 0;\n    font-size: 1.1rem;\n    line-height: 1.35;\n    color: #111;\n  }\n\n  .qa-card-content-wrap {\n    display: flex;\n    flex-direction: column;\n    gap: 1rem;\n    flex: 1;\n  }\n\n  @media (min-width: 768px) {\n    .qa-card-content-wrap {\n      flex-direction: row;\n      align-items: flex-start;\n      gap: 1rem;\n    }\n\n    .qa-dataset-image-col {\n      width: 300px;\n      flex-shrink: 0;\n    }\n\n    .qa-card-right-col {\n      flex: 1;\n      min-width: 0;\n      display: flex;\n      flex-direction: column;\n      gap: 1rem;\n    }\n  }\n\n  .qa-dataset-image img {\n    width: 100%;\n    height: auto;\n    object-fit: contain;\n    border-radius: 4px;\n    border: none !important;\n    display: block;\n  }\n\n  .qa-card-description {\n    color: #222;\n    word-break: break-word;\n    line-height: 1.55;\n  }\n\n  .qa-dataset-metadata {\n    width: 100%;\n    margin-top: 0 !important;\n    padding-top: 0 !important;\n    border-top: none !important;\n    font-size: 0.875rem;\n  }\n\n  \/* Pagination *\/\n  .qa-catalog-pagination {\n    display: flex;\n    justify-content: center;\n    align-items: center;\n    gap: 0.5rem;\n    margin-top: 2rem;\n    flex-wrap: wrap;\n  }\n\n  .qa-catalog-page-btn {\n    padding: 0.4rem 0.8rem;\n    border: 1px solid #767676;\n    background: #fff;\n    border-radius: 4px;\n    cursor: pointer;\n    font-size: 0.85rem;\n    color: #111;\n  }\n\n  .qa-catalog-page-btn:hover:not(:disabled) {\n    background: #f0f0f0;\n  }\n\n  .qa-catalog-page-btn.active {\n    background: #111;\n    color: #fff;\n    border-color: #111;\n    font-weight: bold;\n  }\n\n  .qa-catalog-page-btn:disabled {\n    opacity: 0.5;\n    cursor: not-allowed;\n  }\n<\/style>\n\n<script>\n  (() => {\n    const SITE_URL = 'https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2';\n    const ITEMS_PER_PAGE = 12;\n\n    const THEME_EDITORS_MAP = {\n      \"Atmospheric sciences\": [\n        { name: \"Brice No\u00ebl\", affiliation: \"University of Li\u00e8ge\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/atmospheric-sciences-brice-noel-university-of-liege.png\" },\n        { name: \"Luke Trusel\", affiliation: \"Pennsylvania State University\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/atmospheric-sciences-luke-trusel-pennsylvania-state-university.png\" }\n      ],\n      \"Basemaps\": [{ name: \"Anders Skoglund\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/basemaps-anders-skoglund-norwegian-polar-institute.png\" }],\n      \"Biology\": [\n        { name: \"Elisa Seyboth\", affiliation: \"Universidade Federal do Rio Grande\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/biology-elisa-seyboth-universidade-federal-do-rio-grande.png\" },\n        { name: \"Sebastien Moreau\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/biology-sebastien-moreau-norwegian-polar-institute.jpg\" }\n      ],\n      \"Environmental management\": [{ name: \"Rachel Leihy\", affiliation: \"Arthur Rylah Institute for Environmental Research\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/environmental-management-rachel-leihy-arthur-rylah-institute-for-environmental-research-monash-university.png\" }],\n      \"Geology\": [{ name: \"Antonia Ruppel\", affiliation: \"Federal Institute for Geosciences and Natural Resources\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/geology-antonia-ruppel-federal-institute-for-geosciences-and-natural-resources.png\" }],\n      \"Geophysics\": [\n        { name: \"Guy Paxman\", affiliation: \"Durham University\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/geophysics-guy-paxman-durham-university.png\" },\n        { name: \"Mirko Scheinert\", affiliation: \"TUD Dresden University of Technology\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/geophysics-mirko-scheinert-tud-dresden-university-of-technology.jpg\" }\n      ],\n      \"Glaciology\": [\n        { name: \"Benjamin Hills\", affiliation: \"Colorado School of Mines\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/glaciology-benjamin-hills-colorado-school-of-mines.png\" },\n        { name: \"Catherine Ritz\", affiliation: \"Univ. Grenoble Alpes & CNRS\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/glaciology-catherine-ritz-univ-grenoble-alpes-cnrs.png\" }\n      ],\n      \"Miscellaneous\": [{ name: \"Anders Skoglund\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/miscellaneous-anders-skoglund-norwegian-polar-institute.png\" }],\n      \"Oceanography\": [\n        { name: \"Mattia Poinelli\", affiliation: \"University of California, Los Angeles\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/oceanography-mattia-poinelli-university-of-california-los-angeles.png\" },\n        { name: \"Yoshihiro Nakayama\", affiliation: \"Dartmouth College & Hokkaido University\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/oceanography-yoshihiro-nakayama-dartmouth-college-hokkaido-university.png\" }\n      ],\n      \"Satellite imagery\": [{ name: \"Anders Skoglund\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/satellite-imagery-anders-skoglund-norwegian-polar-institute.png\" }],\n      \"Sea ice\": [{ name: \"Alexander Fraser\", affiliation: \"Australian Antarctic Program Partnership\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/sea-ice-alexander-fraser-australian-antarctic-program-partnership.png\" }],\n      \"Social sciences\": [\n        { name: \"Daniela Cajiao\", affiliation: \"Wageningen University & Research\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/social-sciences-daniela-cajiao-wageningen-university-research.png\" },\n        { name: \"Daniela Liggett\", affiliation: \"University of Canterbury\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/social-sciences-daniela-liggett-university-of-canterbury.png\" }\n      ],\n      \"Terrain models\": [\n        { name: \"Anders Skoglund\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/terrain-models-anders-skoglund-norwegian-polar-institute.png\" },\n        { name: \"Kenichi Matsuoka\", affiliation: \"Norwegian Polar Institute\", imageUrl: \"https:\/\/sites.npolar.no\/quantarctica\/wp-content\/uploads\/sites\/10\/2026\/10\/terrain-models-kenichi-matsuoka-norwegian-polar-institute.jpg\" }\n      ]\n    };\n\n    function fixLazyLoadedImages(htmlContent) {\n      if (!htmlContent) return '';\n      const div = document.createElement('div');\n      div.innerHTML = htmlContent;\n      div.querySelectorAll('img').forEach(img => {\n        const realSrc = img.getAttribute('data-src');\n        if (realSrc) {\n          img.setAttribute('src', realSrc);\n          img.removeAttribute('data-src');\n        }\n      });\n      return div.innerHTML;\n    }\n\n    async function fetchAllCategories() {\n      let page = 1, allCategories = [], totalPages = 1;\n      do {\n        const res = await fetch(`${SITE_URL}\/layer_category?per_page=100&page=${page}&hide_empty=false&orderby=name&order=asc`);\n        if (!res.ok) break;\n        totalPages = parseInt(res.headers.get('X-WP-TotalPages') || '1', 10);\n        allCategories.push(...(await res.json()));\n        page++;\n      } while (page <= totalPages);\n\n      return allCategories;\n    }\n\n    function getCategoryAndChildIds(catId, categories) {\n      const ids = [catId];\n      categories.filter(c => c.parent === catId).forEach(child => {\n        ids.push(...getCategoryAndChildIds(child.id, categories));\n      });\n      return ids;\n    }\n\n    async function fetchLayers(selectedCategory, categories, searchQuery, currentPage) {\n      let url = `${SITE_URL}\/quantarctica_layer?per_page=${ITEMS_PER_PAGE}&page=${currentPage}&orderby=name&order=asc`;\n      if (selectedCategory) {\n        url += `&layer_category=${getCategoryAndChildIds(selectedCategory.id, categories).join(',')}`;\n      }\n      if (searchQuery.trim()) {\n        url += `&search=${encodeURIComponent(searchQuery.trim())}`;\n      }\n\n      const res = await fetch(url);\n      if (!res.ok) throw new Error();\n\n      return {\n        items: await res.json(),\n        totalItems: parseInt(res.headers.get('X-WP-Total') || '0', 10),\n        totalPages: parseInt(res.headers.get('X-WP-TotalPages') || '1', 10)\n      };\n    }\n\n    function buildCategoryTree(categories) {\n      const map = {}, roots = [];\n      categories.forEach(c => map[c.id] = { ...c, children: [] });\n      categories.forEach(c => {\n        if (c.parent && map[c.parent]) map[c.parent].children.push(map[c.id]);\n        else roots.push(map[c.id]);\n      });\n\n      const sortByName = (a, b) => a.name.localeCompare(b.name, undefined, { sensitivity: 'accent', numeric: true });\n      roots.sort(sortByName);\n      Object.values(map).forEach(node => {\n        if (node.children.length > 0) node.children.sort(sortByName);\n      });\n\n      return roots;\n    }\n\n    function getRootCategory(cat, categories) {\n      let curr = cat;\n      while (curr?.parent && curr.parent !== 0) {\n        const parentCat = categories.find(c => c.id === curr.parent);\n        if (!parentCat) break;\n        curr = parentCat;\n      }\n      return curr;\n    }\n\n    function renderEditorsHTML(selectedCategory, categories) {\n      if (!selectedCategory) return '';\n\n      const rootCategory = getRootCategory(selectedCategory, categories);\n      const editors = THEME_EDITORS_MAP[rootCategory.name] || THEME_EDITORS_MAP[rootCategory.slug] || [];\n      if (!editors.length) return '';\n\n      const headingText = editors.length === 1 ? 'Theme editor' : 'Theme editors';\n\n      return `\n        <h3 class=\"qa-editors-heading\">${headingText}<\/h3>\n        <div class=\"qa-editors-list\">\n          ${editors.map(editor => `\n            <div class=\"qa-editor-card\">\n              <div class=\"qa-editor-img-wrap\">\n                <img decoding=\"async\" src=\"${editor.imageUrl}\" alt=\"${editor.name.replace(\/\"\/g, '&quot;')}\" loading=\"lazy\">\n              <\/div>\n              <p class=\"qa-editor-name\">${editor.name}<\/p>\n              <p class=\"qa-editor-affiliation\">${editor.affiliation}<\/p>\n            <\/div>\n          `).join('')}\n        <\/div>`;\n    }\n\n    function renderGridHTML(layers) {\n      if (!layers?.length) return '<p>No matching datasets found.<\/p>';\n      return layers.map(item => {\n        const title = item.title?.rendered || 'Untitled Dataset';\n        const rawContent = fixLazyLoadedImages(item.content?.rendered || item.excerpt?.rendered || '');\n\n        const tempContainer = document.createElement('div');\n        tempContainer.innerHTML = rawContent;\n\n        tempContainer.querySelectorAll('p').forEach(p => {\n          if (!p.textContent.trim() && !p.querySelector('img, iframe')) p.remove();\n        });\n\n        const imgEl = tempContainer.querySelector('.qa-dataset-image');\n        const imgHTML = imgEl ? imgEl.outerHTML : '';\n        if (imgEl) imgEl.remove();\n\n        const metaEl = tempContainer.querySelector('.qa-dataset-metadata');\n        const metaHTML = metaEl ? metaEl.outerHTML : '';\n        if (metaEl) metaEl.remove();\n\n        return `\n        <article class=\"qa-catalog-card\" aria-label=\"${title.replace(\/\"\/g, '&quot;')}\">\n          <div class=\"qa-catalog-card-header\">\n            ${item.meta?.id_code ? `<div class=\"qa-catalog-card-code\">Code: #${item.meta.id_code}<\/div>` : ''}\n            <h3 class=\"qa-catalog-card-title\">${title}<\/h3>\n          <\/div>\n          <div class=\"qa-card-content-wrap\">\n            <div class=\"qa-dataset-image-col\">${imgHTML}<\/div>\n            <div class=\"qa-card-right-col\">\n              <div class=\"qa-card-description\">${tempContainer.innerHTML}<\/div>\n              ${metaHTML}\n            <\/div>\n          <\/div>\n        <\/article>`;\n      }).join('');\n    }\n\n    function renderPagination(currentPage, totalPages) {\n      if (totalPages <= 1) return '';\n\n      let buttons = `<button type=\"button\" class=\"qa-catalog-page-btn\" ${currentPage === 1 ? 'disabled' : ''} data-page=\"${currentPage - 1}\" aria-label=\"Previous Page\">&larr; Prev<\/button>`;\n\n      for (let p = 1; p <= totalPages; p++) {\n        if (p === 1 || p === totalPages || (p >= currentPage - 2 && p <= currentPage + 2)) {\n          const isCurrent = p === currentPage;\n          buttons += `<button type=\"button\" class=\"qa-catalog-page-btn ${isCurrent ? 'active' : ''}\" data-page=\"${p}\" ${isCurrent ? 'aria-current=\"page\"' : ''} aria-label=\"Page ${p}\">${p}<\/button>`;\n        } else if (p === currentPage - 3 || p === currentPage + 3) {\n          buttons += `<span style=\"padding: 0 0.2rem;\" aria-hidden=\"true\">...<\/span>`;\n        }\n      }\n\n      return buttons + `<button type=\"button\" class=\"qa-catalog-page-btn\" ${currentPage === totalPages ? 'disabled' : ''} data-page=\"${currentPage + 1}\" aria-label=\"Next Page\">Next &rarr;<\/button>`;\n    }\n\n    async function initCatalog() {\n      const treeEl = document.getElementById('catalog-tree-container');\n      const gridEl = document.getElementById('catalog-grid');\n      const pagEl = document.getElementById('catalog-pagination');\n      const breadcrumbEl = document.getElementById('catalog-breadcrumbs');\n      const editorsEl = document.getElementById('catalog-editors');\n      const titleEl = document.getElementById('catalog-results-title');\n      const countEl = document.getElementById('catalog-results-count');\n      const searchInput = document.getElementById('catalog-search-input');\n      const clearAllBtn = document.getElementById('catalog-clear-all-btn');\n\n      try {\n        const categories = await fetchAllCategories();\n        const store = { categories, selectedCategory: null, expandedCategories: new Set(), searchQuery: '', currentPage: 1 };\n\n        function updateClearAllVisibility() {\n          if (!clearAllBtn) return;\n          const hasFilter = store.selectedCategory !== null || store.searchQuery.trim() !== '';\n          clearAllBtn.style.display = hasFilter ? 'inline-block' : 'none';\n        }\n\n        function renderTreeNodes(nodes, level = 0) {\n          const ul = document.createElement('ul');\n          ul.setAttribute('role', 'group');\n\n          nodes.forEach(node => {\n            const li = document.createElement('li');\n            li.setAttribute('role', 'none');\n\n            const isSelected = store.selectedCategory?.id === node.id;\n            const hasChildren = node.children?.length > 0;\n            const isExpanded = store.expandedCategories.has(node.id);\n\n            const row = document.createElement('button');\n            row.type = 'button';\n            row.className = `qa-catalog-tree-row ${isSelected ? 'active' : ''}`;\n            row.style.paddingLeft = `${0.5 + (level * 1.25)}rem`;\n            row.setAttribute('role', 'treeitem');\n            row.setAttribute('aria-selected', isSelected ? 'true' : 'false');\n\n            if (hasChildren) {\n              row.setAttribute('aria-expanded', isExpanded ? 'true' : 'false');\n\n              const toggle = document.createElement('button');\n              toggle.type = 'button';\n              toggle.className = 'qa-catalog-tree-toggle';\n              toggle.setAttribute('aria-label', `${isExpanded ? 'Collapse' : 'Expand'} ${node.name}`);\n              toggle.textContent = isExpanded ? '\u25bc' : '\u25ba';\n              toggle.onclick = (e) => {\n                e.stopPropagation();\n                store.expandedCategories.has(node.id) ? store.expandedCategories.delete(node.id) : store.expandedCategories.add(node.id);\n                render(false);\n              };\n              row.appendChild(toggle);\n            }\n\n            const label = document.createElement('span');\n            label.className = 'qa-catalog-tree-label';\n            label.textContent = node.name;\n            row.appendChild(label);\n\n            row.onclick = () => {\n              const currentlySelected = store.selectedCategory?.id === node.id;\n              if (!currentlySelected) {\n                if (hasChildren) store.expandedCategories.add(node.id);\n                updateState({ selectedCategory: node, currentPage: 1 });\n              } else {\n                if (hasChildren) store.expandedCategories.delete(node.id);\n                updateState({ selectedCategory: null, currentPage: 1 });\n              }\n            };\n\n            li.appendChild(row);\n            if (hasChildren && isExpanded) {\n              li.appendChild(renderTreeNodes(node.children, level + 1));\n            }\n            ul.appendChild(li);\n          });\n          return ul;\n        }\n\n        function renderBreadcrumbs(selectedCat) {\n          if (!selectedCat) return '';\n\n          const chain = [];\n          let curr = selectedCat;\n          while (curr) {\n            chain.unshift(curr);\n            curr = store.categories.find(c => c.id === curr.parent);\n          }\n\n          return `\n            <ol class=\"qa-breadcrumb-list\">\n              <li><button type=\"button\" class=\"qa-breadcrumb-item\" data-cat-id=\"\">All<\/button><\/li>\n              ${chain.map((cat, index) => {\n            const isLast = index === chain.length - 1;\n            return `\n                  <li class=\"qa-breadcrumb-separator\" aria-hidden=\"true\">\/<\/li>\n                  <li>\n                    <button type=\"button\" class=\"qa-breadcrumb-item ${isLast ? 'active' : ''}\" ${isLast ? 'aria-current=\"location\"' : `data-cat-id=\"${cat.id}\"`}>\n                      ${cat.name}\n                    <\/button>\n                  <\/li>`;\n          }).join('')}\n            <\/ol>`;\n        }\n\n        async function render(fetchRequired = true) {\n          if (fetchRequired) {\n            gridEl.setAttribute('aria-busy', 'true');\n            gridEl.innerHTML = '<p>Loading datasets...<\/p>';\n            countEl.textContent = 'Searching...';\n          }\n\n          treeEl.innerHTML = '';\n          treeEl.appendChild(renderTreeNodes(buildCategoryTree(store.categories)));\n          breadcrumbEl.innerHTML = renderBreadcrumbs(store.selectedCategory);\n          editorsEl.innerHTML = renderEditorsHTML(store.selectedCategory, store.categories);\n          titleEl.textContent = store.selectedCategory ? store.selectedCategory.name : 'All Datasets';\n\n          updateClearAllVisibility();\n\n          breadcrumbEl.querySelectorAll('.qa-breadcrumb-item[data-cat-id]').forEach(btn => {\n            btn.onclick = () => {\n              const id = btn.dataset.catId;\n              updateState({ selectedCategory: id ? store.categories.find(c => c.id === parseInt(id, 10)) : null, currentPage: 1 });\n            };\n          });\n\n          if (fetchRequired) {\n            try {\n              const { items, totalItems, totalPages } = await fetchLayers(store.selectedCategory, store.categories, store.searchQuery, store.currentPage);\n              gridEl.innerHTML = renderGridHTML(items);\n              pagEl.innerHTML = renderPagination(store.currentPage, totalPages);\n              countEl.textContent = `${totalItems} dataset${totalItems === 1 ? '' : 's'} found`;\n              gridEl.setAttribute('aria-busy', 'false');\n\n              pagEl.querySelectorAll('.qa-catalog-page-btn').forEach(btn => {\n                btn.onclick = () => {\n                  const page = parseInt(btn.dataset.page, 10);\n                  if (page && page !== store.currentPage) updateState({ currentPage: page });\n                };\n              });\n            } catch {\n              gridEl.innerHTML = '<p>Failed to load datasets.<\/p>';\n              countEl.textContent = 'Error loading datasets.';\n              gridEl.setAttribute('aria-busy', 'false');\n            }\n          }\n        }\n\n        function updateState(partialState, fetchRequired = true) {\n          if ('selectedCategory' in partialState) {\n            store.selectedCategory = partialState.selectedCategory;\n            let pId = partialState.selectedCategory?.parent;\n            while (pId) {\n              store.expandedCategories.add(pId);\n              pId = store.categories.find(c => c.id === pId)?.parent;\n            }\n          }\n          if ('searchQuery' in partialState) {\n            store.searchQuery = partialState.searchQuery;\n            store.currentPage = 1;\n          }\n          if ('currentPage' in partialState) store.currentPage = partialState.currentPage;\n          render(fetchRequired);\n        }\n\n        let searchTimeout;\n        searchInput.oninput = (e) => {\n          const val = e.target.value;\n          searchInput.classList.toggle('has-filter', val.trim() !== '');\n\n          clearTimeout(searchTimeout);\n          searchTimeout = setTimeout(() => updateState({ searchQuery: val }), 300);\n        };\n\n        if (clearAllBtn) {\n          clearAllBtn.onclick = () => {\n            store.selectedCategory = null;\n            store.searchQuery = '';\n            searchInput.value = '';\n            searchInput.classList.remove('has-filter');\n            store.expandedCategories.clear();\n            updateState({ selectedCategory: null, searchQuery: '', currentPage: 1 }, true);\n          };\n        }\n\n        render(true);\n      } catch {\n        treeEl.innerHTML = '<p>Failed to load structure.<\/p>';\n        gridEl.innerHTML = '<p>Failed to initialize catalog.<\/p>';\n      }\n    }\n\n    initCatalog();\n  })();\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Search datasets&#8230; Themes Clear All Loading Themes&#8230; Note: Quantarctica includes more than 640 individual map layers. The Data Catalog groups related layers into datasets, so the number of catalog entries shown here is smaller than the total number of layers. All Datasets Loading datasets&#8230;<\/p>\n","protected":false},"author":7,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"fullpage","meta":{"footnotes":""},"class_list":["post-6103","page","type-page","status-publish","hentry"],"mb":[],"mfb_rest_fields":["title"],"_links":{"self":[{"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/pages\/6103","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/comments?post=6103"}],"version-history":[{"count":23,"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/pages\/6103\/revisions"}],"predecessor-version":[{"id":7458,"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/pages\/6103\/revisions\/7458"}],"wp:attachment":[{"href":"https:\/\/sites.npolar.no\/quantarctica\/wp-json\/wp\/v2\/media?parent=6103"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}