{"id":5342,"date":"2025-10-10T02:09:50","date_gmt":"2025-10-10T01:09:50","guid":{"rendered":"https:\/\/paragliding-in-madeira.com\/weather\/?page_id=5342"},"modified":"2026-10-26T16:45:04","modified_gmt":"2026-10-26T16:45:04","slug":"trails-info","status":"publish","type":"page","link":"https:\/\/paragliding-in-madeira.com\/weather\/trails-info\/","title":{"rendered":"Trails Info"},"content":{"rendered":"\n<figure class=\"wp-block-image alignfull size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1999\" height=\"1024\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner.webp\" alt=\"Hiker in nature\" class=\"wp-image-5101\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner.webp 1999w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner-300x154.webp 300w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner-1024x525.webp 1024w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner-768x393.webp 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Hiker-in-nature-by-gpt-wide-banner-1536x787.webp 1536w\" sizes=\"auto, (max-width: 1999px) 100vw, 1999px\" \/><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-0aa01211 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.paragliding-in-madeira.com\/weather\">Home<\/a><\/div>\n\n\n\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/paragliding-in-madeira.com\/weather\/weather-info\/\">Weather info<\/a><\/div>\n<\/div>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\"><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/trails-status\/\" title=\"Trails official status\">Trail status<\/a> | <a href=\"https:\/\/paragliding-in-madeira.com\/weather\/trails-info\/user-feedback\/#user-info\" title=\"\">Hikers Feedback<\/a> | <strong>Info<\/strong><\/p>\n\n\n\n<div style=\"height:6px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"has-text-align-center wp-block-paragraph\" style=\"margin-right:0px;margin-left:0px;padding-right:0px;padding-left:0px;font-size:clamp(17.371px, 1.086rem + ((1vw - 3.2px) * 1.003), 27px);\">Madeira PR trails mobile friendly<br>For reference only, conditions can change<br>Powered by <a href=\"https:\/\/ifcn.madeira.gov.pt\/en\/atividades-de-natureza\/percursos-pedestres-recomendados\/mapa-dos-percursos.html\" target=\"_blank\" rel=\"noopener\" title=\"\">IFCN<\/a> maps<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<div class=\"wp-block-cover alignfull is-light wp-duotone-36345d-abaaaa-1\" style=\"margin-top:0;padding-top:5vw;padding-right:5vw;padding-bottom:5vw;padding-left:5vw;min-height:100vh;aspect-ratio:unset;\"><img loading=\"lazy\" decoding=\"async\" width=\"973\" height=\"1024\" class=\"wp-block-cover__image-background wp-image-12359 size-large\" alt=\"Percursos Mapa bg\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-973x1024.webp\" style=\"object-position:8% 43%\" data-object-fit=\"cover\" data-object-position=\"8% 43%\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-973x1024.webp 973w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-285x300.webp 285w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-768x808.webp 768w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-1459x1536.webp 1459w, https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/Percursos-Mapa-bg-1946x2048.webp 1946w\" sizes=\"auto, (max-width: 973px) 100vw, 973px\" \/><span aria-hidden=\"true\" class=\"wp-block-cover__background has-background-dim-80 has-background-dim\" style=\"background-color:#c2b7a4\"><\/span><div class=\"wp-block-cover__inner-container is-layout-flow wp-block-cover-is-layout-flow\">\n<div style=\"height:10px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image aligncenter full-quality-map\"><a href=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Percursos-Mapa.webp\"><img decoding=\"async\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2025\/10\/Percursos-Mapa.webp\" alt=\"Madeira Hikes Details and Location Map\"\/><\/a><\/figure>\n\n\n\n<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n    const img = document.querySelector('.full-quality-map img');\n    if (!img) return;\n\n    const LENS_SIZE = 180, LENS_ZOOM = 2.5, TOUCH_ZOOM = 5.9;\n\n    const lens = document.createElement('div');\n    lens.style.cssText = 'position:fixed;display:none;width:' + LENS_SIZE + 'px;height:' + LENS_SIZE + 'px;border-radius:50%;box-shadow:0 0 0 3px #fff,0 4px 14px rgba(0,0,0,0.35);pointer-events:none;z-index:9998;background-repeat:no-repeat;';\n    document.body.appendChild(lens);\n\n    \/\/ Shows the lens at (lensX, lensY) magnifying the image point (focusX, focusY)\n    function showLens(lensX, lensY, focusX, focusY, zoom) {\n        const r = img.getBoundingClientRect();\n        lens.style.backgroundImage = 'url(\"' + img.currentSrc + '\")';\n        lens.style.backgroundSize = (r.width * zoom) + 'px ' + (r.height * zoom) + 'px';\n        lens.style.backgroundPosition =\n            (LENS_SIZE \/ 2 - (focusX - r.left) * zoom) + 'px ' +\n            (LENS_SIZE \/ 2 - (focusY - r.top) * zoom) + 'px';\n        lens.style.left = (lensX - LENS_SIZE \/ 2) + 'px';\n        lens.style.top = (lensY - LENS_SIZE \/ 2) + 'px';\n        lens.style.display = 'block';\n    }\n\n    \/\/ Desktop (real hover pointer)\n    if (window.matchMedia('(hover: hover)').matches) {\n        img.style.cursor = 'none';\n        img.addEventListener('mousemove', function(e) {\n            showLens(e.clientX, e.clientY, e.clientX, e.clientY, LENS_ZOOM);\n        });\n        img.addEventListener('mouseleave', function() { lens.style.display = 'none'; });\n        img.addEventListener('click', function() { lens.style.display = 'none'; });\n    }\n\n    \/\/ Mobile: hold a finger still to start the lens\n    let timer = null, active = false, startX = 0, startY = 0;\n\n    function placeTouchLens(t) {\n        const r = img.getBoundingClientRect();\n        const fx = t.clientX;\n        const cx = Math.min(Math.max(fx, LENS_SIZE \/ 2), window.innerWidth - LENS_SIZE \/ 2);\n        const cy = Math.max(LENS_SIZE \/ 2, t.clientY - LENS_SIZE \/ 2 - 20);\n        if (fx < r.left || fx > r.right || cy < r.top || cy > r.bottom) {\n            lens.style.display = 'none';\n            return;\n        }\n        showLens(cx, cy, fx, cy, TOUCH_ZOOM);\n    }\n\n    img.style.webkitTouchCallout = 'none';\n    img.style.webkitUserSelect = 'none';\n    img.style.userSelect = 'none';\n\n    img.addEventListener('touchstart', function(e) {\n        clearTimeout(timer);\n        if (e.touches.length > 1) { active = false; lens.style.display = 'none'; return; }\n        startX = e.touches[0].clientX;\n        startY = e.touches[0].clientY;\n        timer = setTimeout(function() {\n            active = true;\n            placeTouchLens({ clientX: startX, clientY: startY });\n        }, 350);\n    }, { passive: true });\n\n    img.addEventListener('touchmove', function(e) {\n        if (active) {\n            e.preventDefault();\n            placeTouchLens(e.touches[0]);\n            return;\n        }\n        const t = e.touches[0];\n        if (Math.abs(t.clientX - startX) > 10 || Math.abs(t.clientY - startY) > 10) clearTimeout(timer);\n    }, { passive: false });\n\n    function endTouch(e) {\n        clearTimeout(timer);\n        if (active) {\n            active = false;\n            lens.style.display = 'none';\n            if (e.cancelable) e.preventDefault(); \/\/ stops the link from opening after a lens hold\n        }\n    }\n    img.addEventListener('touchend', endTouch, { passive: false });\n    img.addEventListener('touchcancel', endTouch, { passive: false });\n\n    \/\/ Block the phone's long-press menu while the lens is in use\n    img.addEventListener('contextmenu', function(e) { if (active) e.preventDefault(); });\n});\n<\/script>\n\n\n\n<div style=\"height:33px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<!-- Precious trail map and info widget -->\n\n<style>\n\/* GENERAL CONTAINER AND DROPDOWN *\/\n\n#trail-map-info-container {\n    max-width: 900px;\n    margin: auto;\n    padding: 15px 0;\n}\n\n\/* WRAPPER FOR THE MAP AND NAVIGATION ARROWS\nFlexbox for alignment: Arrow | Content | Arrow *\/\n\n.map-wrapper {\n    text-align: center;\n    margin-top: 20px;\n    display: flex;\n    align-items: center;\n    min-height: 250px;\n    position: relative;\n}\n\n\/* Content area for title, map image and subtext *\/\n#map-image-content {\n    flex-grow: 1;           \/* takes up all available space *\/\n    padding: 0 10px;        \/* space between content and arrows *\/\n}\n\n\/* MAP IMAGE STYLING *\/\n\n#clickable-map {\n    max-width: 100%;\n    height: auto;\n    border-radius: 6px;\n    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);\n    cursor: pointer;\n    transition: opacity 0.3s;\n}\n\n\/* Map image on desktop (was: @media screen and (min-width: 1025px) ) *\/\n@media (min-width: 688px) {\n    #clickable-map {\n        max-width: 50%;\n        height: auto;\n    }\n}\n\n#clickable-map:hover {\n    opacity: 0.9;\n}\n\n\/* SLIDESHOW NAVIGATION ARROWS *\/\n\n.slideshow-arrow {\n    font-size: 40px;\n    color: #333;\n    font-weight: bold;\n    cursor: pointer;\n    padding: 0 15px;\n    transition: color 0.2s, opacity 0.2s;\n    user-select: none;\n}\n\n.slideshow-arrow:hover {\n    color: #0073aa;\n}\n\n.slideshow-arrow.disabled {\n    opacity: 0.3;\n    cursor: default;\n    color: #333;\n}\n\n\/* Bottom links wait until a trail is chosen *\/\n#info-links {\n    display: none;\n}\n\n\/* SMOOTH LOAD START (delete this block to undo)*\/\n\n#clickable-map {\n    width: 100%;\n    aspect-ratio: auto 11 \/ 21;\n}\n\n@keyframes mapFade {\n    from {\n        opacity: 0;\n    }\n    to {\n        opacity: 1;\n    }\n}\n\n#map-image-content.map-fade {\n    animation: mapFade 1.9s ease-in-out;\n}\n\n\/* SMOOTH LOAD END *\/\n\n\n\/* TRAIL TITLE LINK STYLING (Part 2) *\/\n\n#trail-title-with-link {\n    font-size: 22px;\n    margin-top: 0;\n    color: #333;\n    line-height: 1.0;\n}\n\n#trail-title-with-link a {\n    color: #115677;         \/* primary brand colour *\/\n    text-decoration: none;\n    border-bottom: 0px solid #0073aa;\n    transition: color 0.2s, border-bottom 0.2s;\n}\n\n#trail-title-with-link a:hover {\n    color: #555;\n    border-bottom: 2px solid #555;    \/* underline on hover *\/\n}\n\n\n\/* MOBILE ARROWS BELOW START (delete everything from here to MOBILE ARROWS BELOW END to undo) *\/\n\n@media (max-width: 687px) {\n    .map-wrapper {\n        flex-wrap: wrap;\n    }\n\n    #map-image-content {\n        order: 1;\n        flex: 0 0 100%;\n        padding: 0 0px;     \/* changed from 0 4px *\/\n    }\n\n    .slideshow-arrow {\n        display: none;\n    }\n}\n\n\/* Removed\norder: 2;\nflex: 0 0 50%;\nbox-sizing: border-box;\ntext-align: center;\npadding: 8px 0 0; *\/\n\n\/* MOBILE ARROWS BELOW END *\/\n\n\n\/* SIMPLE LIGHTBOX (MODAL) *\/\n\n.modal {\n    display: none;\n    position: fixed;\n    z-index: 9999;\n    left: 0;\n    top: 0;\n    width: 100%;\n    height: 100%;\n    overflow: auto;\n    background-color: rgba(0, 0, 0, 0.9);\n}\n\n.modal-content {\n    padding: 10px;\n    border-radius: 8%;\n    margin: 10vh auto 0;\n    display: block;\n    width: auto;\n    height: auto;\n    max-width: min(95%, 900px);\n    max-height: 90vh;\n    animation: zoom 0.9s cubic-bezier(0.16, 1, 0.3, 1) forwards;\n}\n\n@keyframes zoom {\n    from {\n        transform: scale(0.95);\n        opacity: 0;\n    }\n    to {\n        transform: scale(1);\n        opacity: 1;\n    }\n}\n\n.close-modal {\n    position: absolute;\n    top: 15px;\n    right: 35px;\n    color: #f1f1f1;\n    font-size: 40px;\n    font-weight: bold;\n    transition: 0.3s;\n    cursor: pointer;\n}\n\n.close-modal:hover,\n.close-modal:focus {\n    color: #bbb;\n}\n\n\/* SCROLL LOCK FIX (the brilliant part) *\/\n\nbody.no-scroll,\nhtml.no-scroll {\n    overflow: hidden !important;\n}\n\n.modal {\n    overscroll-behavior: contain;\n}\n\n\/* DETAILS WINDOW START (delete everything from here to DETAILS WINDOW END to undo) *\/\n\n\/* 1. COLOURS AND SIZES: change these first *\/\n\n.details-window {\n    --details-card-background: #ffffff;\n    --details-text: #222222;\n    --details-soft-text: #666666;\n    --details-link: #0073aa;\n    --details-coords-background: #e8f3fa;\n    --details-line: #e3e3e3;\n    --details-button-background: #f1f1f1;\n    --details-button-active: #0073aa;\n    --details-accent: #2f7d4f;                  \/* thin line on top of the card (official route) *\/\n    --details-badge-background: #e3f1e8;        \/* small badge with the direction (official route) *\/\n    --details-badge-text: #25613b;\n}\n\n\/* the alternative \/ reverse route uses its own colours, so it is clear at a glance *\/\n.details-window.is-reverse {\n    --details-accent: #8a6e43;\n    --details-badge-background: #e8e3d2;\n    --details-badge-text: #86652a;\n}\n\n\/* 2. THE DARK BACKGROUND AND THE CARD *\/\n\n.details-window {\n    display: none;    \/* the script shows it when you tap Details *\/\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    z-index: 100000;   \/* above the WordPress admin bar (you see that bar, visitors do not) *\/\n    background: rgba(0, 0, 0, 0.75);\n    align-items: center;\n    justify-content: center;\n    padding: 10px;\n    overscroll-behavior: contain;\n}\n\n.details-card {\n    position: relative;\n    width: 100%;\n    max-width: 430px;\n    max-height: calc(100vh - 39px);\n    max-height: calc(100dvh - 39px);\n    overflow-y: auto;\n    background: var(--details-card-background);\n    color: var(--details-text);\n    border-radius: 14px;\n    border-top: 5px solid var(--details-accent);\n    padding: 11px 14px 14px;\n    font-size: 15px;\n    line-height: 1.3;\n    text-align: left;\n    -webkit-user-select: none;       \/* text selection is off on purpose *\/\n    user-select: none;\n}\n\n\/* 3. MAIN NAVIGATION (top row): Details, Description, ?, i and close *\/\n\n.details-nav {\n    position: relative;\n    display: flex;\n    gap: 4px;\n    align-items: center;\n    justify-content: center;\n    margin-bottom: 9px;\n    border-bottom: 1px solid var(--details-line);\n}\n\n.details-tab {\n    margin-bottom: -1px;\n    padding: 6px 10px 7px;\n    border: 0;\n    border-bottom: 2px solid transparent;\n    background: transparent;\n    font-size: 13px;\n    font-family: inherit;\n    color: var(--details-soft-text);\n    cursor: pointer;\n}\n\n.details-tab.is-active {\n    color: var(--details-text);\n    font-weight: 700;\n    border-bottom-color: var(--details-accent);\n}\n\n\/* the small round ? and i *\/\n.details-tab-round {\n    width: 26px;\n    height: 26px;\n    margin: 0 2px 4px;\n    padding: 0;\n    border: 1.5px solid var(--details-link);\n    border-radius: 50%;\n    color: var(--details-link);\n    font-weight: 700;\n}\n\n.details-tab-round.is-active {\n    background: var(--details-link);\n    color: #ffffff;\n    border-color: var(--details-link);\n}\n\n.details-close {\n    position: absolute;\n    right: 0;\n    top: -3px;\n    width: 28px;\n    height: 28px;\n    margin: 0;\n    padding: 0;\n    border: 0;\n    border-radius: 50%;\n    cursor: pointer;\n    background: #eceae4b2;\n    color: black;\n    font-size: 25px;\n    line-height: 1;\n}\n\n\/* 4. ROUTE BUTTONS (Official route \/ Reverse route) *\/\n\n.details-buttons {\n    display: flex;\n    gap: 6px;\n    align-items: center;\n    margin-bottom: 9px;\n}\n\n.details-button {\n    flex: 1;\n    border: 0;\n    border-radius: 999px;\n    padding: 7px 6px;\n    font-size: 13px;\n    font-family: inherit;\n    cursor: pointer;\n    background: var(--details-button-background);\n    color: var(--details-text);\n}\n\n.details-button.is-active {\n    background: var(--details-button-active);\n    color: #ffffff;\n    font-weight: 600;\n}\n\n\/* 5. DIRECTION BADGE, TITLE, INTRO *\/\n\n.details-badge {\n    display: inline-block;\n    margin-bottom: 5px;\n    padding: 2px 9px;\n    border-radius: 999px;\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.4px;\n    text-transform: uppercase;\n    background: var(--details-badge-background);\n    color: var(--details-badge-text);\n}\n\n.details-title {\n    font-size: 18px;\n    font-weight: 700;\n    line-height: 1.2;\n    color: var(--details-text);\n}\n\n.details-intro {\n    margin: 3px 0 8px;\n    font-size: 13px;\n    font-style: italic;\n    color: var(--details-soft-text);\n}\n\n\/* 6. THE SMALL BOXES (distance, duration, heights) *\/\n\n.details-specs {\n    display: flex;\n    flex-wrap: wrap;\n    gap: 6px;\n    margin-bottom: 8px;\n}\n\n.details-spec {\n    flex: 1 1 56px;\n    min-width: 0;\n    padding: 5px 8px;\n    border-radius: 10px;\n    background: var(--details-button-background);\n}\n\n.details-spec-label {\n    display: block;\n    font-size: 10.5px;\n    color: var(--details-soft-text);\n}\n\n.details-spec-value {\n    font-size: 14px;\n}\n\n.details-spec-note {\n    display: block;\n    font-size: 10.5px;\n    color: var(--details-soft-text);\n}\n\n\/* 7. THE PLACES (parking, start, end) *\/\n\n.details-place {\n    padding: 5px 0;\n    border-top: 1px solid var(--details-line);\n}\n\n.details-place-top {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n}\n\n.details-place-icon {\n    width: 22px;\n    text-align: center;\n    font-size: 17px;\n}\n\n.details-place-name {\n    flex: 1;\n    min-width: 0;\n    font-size: 14px;\n    font-weight: 700;\n}\n\n.details-place-kind {\n    font-size: 11px;\n    white-space: nowrap;\n    color: var(--details-soft-text);\n}\n\n.details-coords {\n    display: inline-block;\n    margin: 3px 0 0 30px;\n    padding: 3px 10px;\n    border-radius: 999px;\n    font-size: 13px;\n    font-weight: 600;\n    text-decoration: none;\n    background: var(--details-coords-background);\n    color: var(--details-link);\n}\n\n.details-note {\n    margin: 3px 0 0 30px;\n    font-size: 12.5px;\n    color: var(--details-soft-text);\n}\n\n\/* 8. THE HEIGHT GRAPH *\/\n\n.details-graph {\n    margin-top: 8px;\n    padding-top: 8px;\n    border-top: 1px solid var(--details-line);\n}\n\n.details-graph img {\n    display: block;\n    width: 100%;\n    border-radius: 6px;\n    cursor: default;\n    background: #ffffff;\n    margin-bottom: -5px;\n}\n\n\/* 9. PICTURES IN THE ? AND i TABS *\/\n\n.details-picture {\n    display: block;\n    width: 100%;\n    border-radius: 6px;\n}\n\n\/* 10. MESSAGE (loading, or nothing here yet) *\/\n\n.details-message {\n    padding: 26px 6px 22px;\n    text-align: center;\n    color: var(--details-soft-text);\n}\n\n\/* 11. HELPER: the script uses this to hide anything that has no information *\/\n\n.details-window .is-hidden {\n    display: none !important;\n}\n\n\/* 12. DESCRIPTION TAB *\/\n\n\/* the row with the two flags and the title *\/\n.details-description-head {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    gap: 10px;\n    margin-bottom: 6px;\n}\n\n.details-description-title {\n    flex: 1;\n    text-align: center;\n    font-size: 14px;\n    font-weight: 700;\n    line-height: 1.2;\n    color: var(--details-text);\n}\n\n\/* the flag buttons: the chosen language is bright, the other one fades *\/\n.details-flag {\n    flex: 0 0 auto;\n    padding: 3px;\n    border: 0;\n    border-radius: 4px;\n    background: transparent;\n    cursor: pointer;\n    line-height: 0;\n    opacity: 0.35;\n    transition: opacity 0.25s;\n}\n\n.details-flag.is-active {\n    opacity: 1;\n}\n\n.details-flag svg {\n    display: block;\n    border-radius: 2px;\n    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);\n}\n\n\/* which trail this description belongs to *\/\n.details-description-trail {\n    margin-bottom: 8px;\n    text-align: center;\n    font-size: 12px;\n    color: var(--details-soft-text);\n}\n\n\/* the paragraphs *\/\n.details-description-text p {\n    margin: 0 0 9px;\n    font-size: 13.5px;\n    line-height: 1.5;\n    color: var(--details-text);\n    white-space: pre-line;\n}\n\n\/* a gentle fade when the language changes *\/\n.details-description-text.is-fading-in {\n    animation: detailsFadeIn 0.35s ease;\n}\n\n@keyframes detailsFadeIn {\n    from { opacity: 0; }\n    to   { opacity: 1; }\n}\n\n\/* DETAILS WINDOW END *\/\n\n<\/style>\n\n<!-- TRAIL SELECTOR, MAP AND LINKS UNDER THE MAP -->\n\n<div id=\"trail-map-info-container\">\n\n    <label for=\"trail_select_maps\" style=\"display: block; margin-bottom: 10px; font-weight: bold;\">\n        Select a Recommended Trail:\n    <\/label>\n\n    <select id=\"trail_select_maps\" style=\"width: 100%; padding: 10px; border-radius: 4px; border: 1px solid #ccc; margin-bottom: 20px;\">\n        <option value=\"\">&#8212; Choose a Trail &#8212;<\/option>\n    <\/select>\n\n    <div id=\"trail-map-details\" class=\"map-wrapper\">\n\n        <p id=\"default-message\" style=\"width: 100%; text-align: center; font-size: 17px; font-weight: 700;\">\n            And unlock map advanced features..\n            <br>\n        <\/p>\n\n        <figure class=\"wp-block-image size-full\">\n            <picture>\n                <source media=\"(hover: hover)\" srcset=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/ancient_scroll-desktop.webp\">\n                <img decoding=\"async\" src=\"https:\/\/paragliding-in-madeira.com\/weather\/wp-content\/uploads\/2026\/10\/ancient_scroll-mobile.webp\" alt=\"Ancient scroll\" class=\"wp-image-12366\" style=\"border-radius: 12px;\">\n            <\/picture>\n        <\/figure>\n\t\t\n    <\/div>\n\n    <div id=\"info-links\" style=\"text-align: center; margin-top: 15px;\">\n\n        <a href=\"#\" id=\"open-details-link\" style=\"text-decoration: none; font-weight: 600; color: #3e63a8;\">\n            Details\n        <\/a> |\n      <a href=\"#\" id=\"open-description-link\" style=\"text-decoration: none; font-weight: 600; color: #3e63a8;\">\n            Description\n        <\/a>\n\n        <a href=\"#\" id=\"open-legend-link\" title=\"Legend\" style=\"display: inline-block; text-align: center; text-decoration: none; vertical-align: middle; padding-left: 5px;\">\n\n            <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" width=\"33\" height=\"33\" fill=\"none\" stroke=\"#3b82f6\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#f0f9ff\"><\/circle>\n                <path d=\"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3\"><\/path>\n                <line x1=\"12\" y1=\"17\" x2=\"12.01\" y2=\"17\"><\/line>\n            <\/svg>\n        <\/a>\n\n        <a href=\"#\" id=\"open-ancient-scroll-link\" title=\"How to use the map\" style=\"display: inline-block; text-align: center; text-decoration: none; vertical-align: middle; padding-left: 5px;\">\n\n            <svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 24 24\" width=\"33\" height=\"33\" fill=\"none\" stroke=\"#3b82f6\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n                <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"#f0f9ff\"><\/circle>\n                <line x1=\"12\" y1=\"16\" x2=\"12\" y2=\"12\"><\/line>\n                <line x1=\"12\" y1=\"8\" x2=\"12.01\" y2=\"8\"><\/line>\n            <\/svg>\n        <\/a>\n    <\/div>\n<\/div>\n\n<!-- SIMPLE LIGHTBOX (image only) -->\n\n<div id=\"trail-info-modal\" class=\"modal\">\n    <span class=\"close-modal\">\u00d7<\/span>\n    <img class=\"modal-content\" id=\"modal-image-content\" alt=\"Trail Aditional Info\">\n<\/div>\n\n<!-- DETAILS WINDOW HTML START (delete everything from here to DETAILS WINDOW HTML END to undo) -->\n<!--\n  This is the window that opens when you tap \"Details\". Edit it like any other HTML:\n  move things, change words, icons or emojis, add or remove boxes.\n  The script only FILLS this window with words from the trail's details file.\n  It finds each part by its data-... name (for example data-place=\"start\").\n  Keep those names; everything else is yours.\n\n  The window has four tabs, in the top row: Details, Description, ? (legend) and i (how to use the map).\n  Each tab shows its own \"panel\" further down (data-panel=\"details\", \"description\", \"legend\", \"howto\").\n\n  To add a small box (for example total climb): copy one <div class=\"details-spec\" ...> block,\n  change data-spec=\"max\" to data-spec=\"climb\" and the label, then add \"climb\" to the trail file.\n  To add another place row: copy one <div class=\"details-place\" ...> block and rename it the same way.\n-->\n<div id=\"details-window\" class=\"details-window\">\n  <div class=\"details-card\">\n\n    <!-- MAIN NAVIGATION: the tabs and the close button -->\n    <div class=\"details-nav\">\n      <button type=\"button\" class=\"details-tab\" data-tab=\"details\">Details<\/button>\n      <button type=\"button\" class=\"details-tab\" data-tab=\"description\">Description<\/button>\n      <button type=\"button\" class=\"details-tab details-tab-round\" data-tab=\"legend\" aria-label=\"Legend\">?<\/button>\n      <button type=\"button\" class=\"details-tab details-tab-round\" data-tab=\"howto\" aria-label=\"How to use the map\">i<\/button>\n      <button type=\"button\" class=\"details-close\" data-close=\"\" aria-label=\"Close\">\u00d7<\/button>\n    <\/div>\n\n    <!-- ============ TAB 1: DETAILS ============ -->\n    <div class=\"details-panel\" data-panel=\"details\">\n\n      <!-- Route buttons. The words are yours to change here. They hide when a trail has only one route. -->\n      <div class=\"details-buttons\">\n        <button type=\"button\" class=\"details-button\" data-route=\"official\">&#x1f9ed; Official route<\/button>\n        <button type=\"button\" class=\"details-button\" data-route=\"reverse\">&#x1f501; Reverse route<\/button>\n      <\/div>\n\n      <!-- Shown only while loading, or when a trail has no details yet -->\n      <div class=\"details-message is-hidden\" data-message=\"\"><\/div>\n\n      <!-- Everything below is the card itself -->\n      <div class=\"details-content\">\n\n        <!-- The direction (for example Areeiro \u2192 Ruivo). It comes from the trail file. -->\n        <span class=\"details-badge\" data-badge=\"\"><\/span>\n\n        <!-- Title: the icon changes with the route -->\n        <div class=\"details-title\">\n          <span data-icon=\"\" data-official=\"&#x1f3d4;&#xfe0f;\" data-reverse=\"&#x1f501;\"><\/span>\n          <span data-title=\"\"><\/span>\n        <\/div>\n\n        <!-- One line about what you are looking at -->\n        <div class=\"details-intro\" data-intro=\"\"><\/div>\n\n        <!-- The small boxes. A box with no information is hidden automatically. -->\n        <div class=\"details-specs\">\n\n          <div class=\"details-spec\" data-spec=\"distance\">\n            <span class=\"details-spec-label\">Distance<\/span>\n            <b class=\"details-spec-value\"><\/b>\n            <small class=\"details-spec-note\"><\/small>\n          <\/div>\n\n          <div class=\"details-spec\" data-spec=\"duration\">\n            <span class=\"details-spec-label\">Duration<\/span>\n            <b class=\"details-spec-value\"><\/b>\n            <small class=\"details-spec-note\"><\/small>\n          <\/div>\n\n          <div class=\"details-spec\" data-spec=\"max\">\n            <span class=\"details-spec-label\">Max height<\/span>\n            <b class=\"details-spec-value\"><\/b>\n            <small class=\"details-spec-note\"><\/small>\n          <\/div>\n\n          <div class=\"details-spec\" data-spec=\"min\">\n            <span class=\"details-spec-label\">Min height<\/span>\n            <b class=\"details-spec-value\"><\/b>\n            <small class=\"details-spec-note\"><\/small>\n          <\/div>\n\n        <\/div>\n\n        <!-- Place 1: parking -->\n        <div class=\"details-place\" data-place=\"parking\">\n\n          <div class=\"details-place-top\">\n            <span class=\"details-place-icon\">&#x1f697;<\/span>\n            <span class=\"details-place-kind\">Parking<\/span>\n            <span class=\"details-place-name\"><\/span>\n          <\/div>\n\n          <a class=\"details-coords\" target=\"_blank\" rel=\"noopener\"><\/a>\n\n          <div class=\"details-note\">\n            <span>&#x1f4dd;<\/span>\n            <span class=\"details-note-text\"><\/span>\n          <\/div>\t\t  \n        <\/div>\n\n\n        <!-- Place 2: start -->\n        <div class=\"details-place\" data-place=\"start\">\n\n          <div class=\"details-place-top\">\n            <span class=\"details-place-icon\">&#x1f97e;<\/span>\n            <span class=\"details-place-kind\">Start<\/span>\n            <span class=\"details-place-name\"><\/span>\n          <\/div>\n\n          <a class=\"details-coords\" target=\"_blank\" rel=\"noopener\"><\/a>\n\n          <div class=\"details-note\">\n            <span>&#x1f4dd;<\/span>\n            <span class=\"details-note-text\"><\/span>\n          <\/div>\n        <\/div>\n\n        <!-- Place 3: end -->\n        <div class=\"details-place\" data-place=\"end\">\n\n          <div class=\"details-place-top\">\n            <span class=\"details-place-icon\">&#x1f3d4;&#xfe0f;<\/span>\n            <span class=\"details-place-kind\">Finish<\/span>\n            <span class=\"details-place-name\"><\/span>\n          <\/div>\n\n          <a class=\"details-coords\" target=\"_blank\" rel=\"noopener\"><\/a>\n\n          <div class=\"details-note\">\n            <span>&#x1f4dd;<\/span>\n            <span class=\"details-note-text\"><\/span>\n          <\/div>\n        <\/div>\n\n        <!-- Height graph. It hides itself if the picture is not uploaded yet. -->\n        <div class=\"details-graph\" data-graph=\"\">\n          <img alt=\"Height graph\">\n        <\/div>\n\n      <\/div>\n    <\/div>\n\n        <!-- ============ TAB 2: DESCRIPTION (the words come from the trail's description file, for example pr1-description.json) ============ -->\n    <div class=\"details-panel is-hidden\" data-panel=\"description\">\n\n      <!-- Shown while loading, or when a trail has no description yet -->\n      <div class=\"details-message is-hidden\" data-description-message=\"\"><\/div>\n\n      <div class=\"details-description is-hidden\" data-description-content=\"\">\n\n        <!-- Title row: Portuguese flag on the left, English flag on the right.\n             The chosen flag is bright and the other one fades. The flags are tiny drawings (SVG);\n             you can replace either one with <img decoding=\"async\" src=\"your-flag.webp\"> if you prefer.\n             The title has one version for each language: change them here. -->\n        <div class=\"details-description-head\">\n\n          <button type=\"button\" class=\"details-flag\" data-language=\"pt\" aria-label=\"Portugu\u00eas\">\n            <svg viewBox=\"0 0 30 20\" width=\"26\" height=\"17\">\n              <rect width=\"12\" height=\"20\" fill=\"#006600\"><\/rect>\n              <rect x=\"12\" width=\"18\" height=\"20\" fill=\"#ff0000\"><\/rect>\n              <circle cx=\"12\" cy=\"10\" r=\"4.2\" fill=\"#ffcc00\"><\/circle>\n              <rect x=\"10.2\" y=\"7.6\" width=\"3.6\" height=\"4.6\" rx=\"1\" fill=\"#ff0000\"><\/rect>\n              <rect x=\"11.1\" y=\"8.6\" width=\"1.8\" height=\"2.4\" fill=\"#ffffff\"><\/rect>\n            <\/svg>\n          <\/button>\n\n          <div class=\"details-description-title\" data-description-title=\"\" data-en=\"Official IFCN trail description\" data-pt=\"Descri\u00e7\u00e3o oficial do percurso IFCN\"><\/div>\n\n          <button type=\"button\" class=\"details-flag\" data-language=\"en\" aria-label=\"English\">\n            <svg viewBox=\"0 0 60 30\" width=\"34\" height=\"17\">\n              <clipPath id=\"flag-uk-a\"><path d=\"M0,0 v30 h60 v-30 z\"><\/path><\/clipPath>\n              <clipPath id=\"flag-uk-b\"><path d=\"M30,15 h30 v15 z v15 h-30 z h-30 v-15 z v-15 h30 z\"><\/path><\/clipPath>\n              <g clip-path=\"url(#flag-uk-a)\">\n                <path d=\"M0,0 v30 h60 v-30 z\" fill=\"#012169\"><\/path>\n                <path d=\"M0,0 L60,30 M60,0 L0,30\" stroke=\"#ffffff\" stroke-width=\"6\"><\/path>\n                <path d=\"M0,0 L60,30 M60,0 L0,30\" clip-path=\"url(#flag-uk-b)\" stroke=\"#c8102e\" stroke-width=\"4\"><\/path>\n                <path d=\"M30,0 v30 M0,15 h60\" stroke=\"#ffffff\" stroke-width=\"10\"><\/path>\n                <path d=\"M30,0 v30 M0,15 h60\" stroke=\"#c8102e\" stroke-width=\"6\"><\/path>\n              <\/g>\n            <\/svg>\n          <\/button>\n\n        <\/div>\n\n     <!-- Which trail this description belongs to -->\n        <div class=\"details-description-trail\" data-description-trail=\"\"><\/div>\n\n<!-- The paragraphs. They are filled in from the description file. -->\n        <div class=\"details-description-text\" data-description-text=\"\"><\/div>\n\n      <\/div>\n    <\/div>\n    <!-- ============ TAB 3: ? LEGEND (the picture is set in the script settings) ============ -->\n    <div class=\"details-panel is-hidden\" data-panel=\"legend\">\n      <img class=\"details-picture\" alt=\"Map legend\">\n    <\/div>\n\n    <!-- ============ TAB 4: i HOW TO USE THE MAP (the picture is set in the script settings) ============ -->\n    <div class=\"details-panel is-hidden\" data-panel=\"howto\">\n      <img class=\"details-picture\" alt=\"How to use the map\">\n    <\/div>\n\n  <\/div>\n<\/div>\n<!-- DETAILS WINDOW HTML END -->\n\n<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n\n    \/\/ CONFIGURATION CONSTANTS\n    const BASE_MAP_PATH = '\/weather\/wp-content\/uploads\/trail-info-maps\/';\n    const LEGEND_IMAGE_URL = '\/weather\/wp-content\/uploads\/2026\/10\/Legend.webp';\n    const SCROLL_IMAGE_URL = '\/weather\/wp-content\/uploads\/2026\/10\/ancient_scroll-'\n        + (window.matchMedia('(hover: hover)').matches ? 'desktop' : 'mobile')\n        + '.webp';\n    const PLACEHOLDER_IMG_URL = BASE_MAP_PATH + 'placeholder.jpg';\n\n    \/\/ DOM ELEMENTS\n    const $select = document.getElementById('trail_select_maps');\n    const $detailsArea = document.getElementById('trail-map-details');\n    const $modal = document.getElementById('trail-info-modal');\n\n    \/\/ Move modal to body so no parent container interferes with full screen coverage\n    document.body.appendChild($modal);\n\n    const $modalImage = document.getElementById('modal-image-content');\n    const $legendLink = document.getElementById('open-legend-link');\n\n\n    \/\/ For Scroll Lock\n    const htmlElement = document.documentElement;\n    const bodyElement = document.body;\n    let scrollPosition = 0;\n\n    \/\/ trails already shown during this visit\n    const seenTrails = {};\n\n    \/\/ TRAIL LIST DATA (hardcoded)\n    const TRAILS_GROUPED = {\n\n        'Madeira (Recommended PRs)': {\n            'PR1':   'PR1 - Vereda do Areeiro',\n            'PR1.1': 'PR1.1 - Vereda da Ilha',\n            'PR1.2': 'PR1.2 - Vereda do Pico Ruivo',\n            'PR1.3': 'PR1.3 - Vereda da Encumeada',\n            'PR2':   'PR2 - Vereda do Urzal',\n            'PR3':   'PR3 - Vereda do Burro',\n            'PR3.1': 'PR3.1 - Caminho Real do Monte',\n            'PR4':   'PR4 - Levada do Barreiro',\n            'PR5':   'PR5 - Vereda das Funduras',\n            'PR6':   'PR6 - Levada das 25 Fontes',\n            'PR6.1': 'PR6.1 - Levada do Risco',\n            'PR6.2': 'PR6.2 - Levada do Alecrim',\n            'PR6.3': 'PR6.3 - Vereda da Lagoa do Vento',\n            'PR6.4': 'PR6.4 - Levada Velha do Raba\u00e7al',\n            'PR6.5': 'PR6.5 - Vereda do Pico Fernandes',\n            'PR6.6': 'PR6.6 - Vereda do T\u00fanel do Cavalo',\n            'PR6.7': 'PR6.7 - Vereda da C\u00e2mara de Carga do Raba\u00e7al',\n            'PR6.8': 'PR6.8 - Levada do Paul II \u2013 Um Caminho para todos',\n            'PR7':   'PR7 - Levada do Moinho',\n            'PR8':   'PR8 - Vereda da Ponta de S\u00e3o Louren\u00e7o',\n            'PR9':   'PR9 - Levada do Caldeir\u00e3o Verde',\n            'PR9.1': 'PR9.1 - Levada do Caldeir\u00e3o Verde - Um Caminho para todos',\n            'PR10':  'PR10 - Levada do Furado',\n            'PR11':  'PR11 - Vereda dos Balc\u00f5es',\n            'PR12':  'PR12 - Caminho Real da Encumeada',\n            'PR13':  'PR13 - Vereda do Fanal',\n            'PR13.1':'PR13.1 - Vereda da Palha Carga',\n            'PR14':  'PR14 - Levada dos Cedros',\n            'PR15':  'PR15 - Vereda da Ribeira da Janela',\n            'PR16':  'PR16 - Levada Faj\u00e3 do Rodrigues',\n            'PR17':  'PR17 - Caminho do Pin\u00e1culo e Folhadal',\n            'PR18':  'PR18 - Levada do Rei',\n            'PR19':  'PR19 - Caminho Real do Paul do Mar',\n            'PR20':  'PR20 - Vereda do Jardim do Mar',\n            'PR21':  'PR21 - Caminho do Norte',\n            'PR22':  'PR22 - Vereda do Ch\u00e3o dos Louros',\n            'PR23':  'PR23 - Levada da Azenha',\n            'PR27':  'PR27 - Glaciar de Planalto',\n            'PR28':  'PR28 - Levada da Rocha Vermelha',\n        },\n\n        'Porto Santo': {\n            'PS_PR1': 'PR (PS) 1 - Vereda do Pico Branco e Terra Ch\u00e3',\n            'PS_PR2': 'PR (PS) 2 - Vereda do Pico do Castelo',\n            'PS_PR3': 'PR (PS) 3 - Levada do Pico Castelo',\n        }\n\n    };\n\n    \/\/ PART 2: GOOGLE MAPS LINKS (verified using starting point coordinates)\n    const TRAIL_MAP_LINKS = {\n        'PR1':   'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.734936,-16.928672',\n        'PR1.1': 'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.805950,-16.911656',\n        'PR1.2': 'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.764811,-16.921133',\n        'PR1.3': 'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.754250,-17.018639',\n        'PR6':   'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.7601,-17.1517',\n        'PR8':   'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.747371,-16.711853',\n        'PR9':   'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.78347,-16.94056',\n        'PR11':  'https:\/\/www.google.com\/maps\/search\/?api=1&query=32.74235,-16.88371',\n        \/\/ Add more permanent map links here as you verify them\n    };\n\n    \/\/ Flattened, ordered list of trail codes for slideshow (Part 1)\n    const ORDERED_TRAIL_KEYS = [].concat(\n        Object.keys(TRAILS_GROUPED['Madeira (Recommended PRs)']),\n        Object.keys(TRAILS_GROUPED['Porto Santo'])\n    );\n\n    \/\/ SCROLL LOCK \/ UNLOCK FUNCTIONS (fixes the page jump)\n    function lockScroll() {\n        const gap = window.innerWidth - htmlElement.clientWidth;\n\n        if (gap > 0) {\n            bodyElement.style.paddingRight = gap + 'px';\n        }\n\n        htmlElement.classList.add('no-scroll');\n        bodyElement.classList.add('no-scroll');\n    }\n\n    function unlockScroll() {\n        htmlElement.classList.remove('no-scroll');\n        bodyElement.classList.remove('no-scroll');\n        bodyElement.style.paddingRight = '';\n    }\n\n    \/\/ Helper to get full trail details\n    function getTrailInfo(trailCode) {\n        for (const island in TRAILS_GROUPED) {\n\n            if (TRAILS_GROUPED[island].hasOwnProperty(trailCode)) {\n\n                return {\n                    code: trailCode,\n                    fullName: TRAILS_GROUPED[island][trailCode],\n                    mapLink: TRAIL_MAP_LINKS[trailCode] || null,\n                    group: island\n                };\n            }\n        }\n\n        return null;\n    }\n\n    \/\/ CORE LOGIC: render the map and links on the main page\n    function loadTrailMap(trailCode) {\n\n        const trailInfo = getTrailInfo(trailCode);\n        const currentIndex = ORDERED_TRAIL_KEYS.indexOf(trailCode);\n\n        if (!trailInfo) {\n\n            \/\/ Should not happen if called correctly, but good for safety\n            $detailsArea.innerHTML = '<p id=\"default-message\" style=\"width: 100%; text-align: center;\">Please select a valid trail<\/p>';\n            $select.value = '';\n            return;\n        }\n\n        const imgName = trailCode.toLowerCase().replace(\/[^a-z0-9]\/g, '-');\n\n        \/\/ Tries WebP first; the built-in onerror handler on the image tag will find jpeg if needed\n        \/\/ NEW - INFO-lightbox jpg no longer in use, replaced with proper modal details\n        const simpleMapUrl = BASE_MAP_PATH + imgName + '.webp';\n        const infoPosterUrl = BASE_MAP_PATH + imgName + '-info-lightbox.jpg';\n\n        \/\/ 1. Update the HTML structure with the new map and arrows\n        let titleLinkHtml = `<h3 id=\"trail-title-with-link\" style=\"color: #333;\">${trailInfo.fullName}<\/h3>`;\n\n        if (trailInfo.mapLink) {\n\n            \/\/ Link the entire H3 to the Google Map URL\n            titleLinkHtml = `\n                <h3 id=\"trail-title-with-link\">\n                    <a href=\"${trailInfo.mapLink}\" target=\"_blank\" title=\"Open ${trailInfo.fullName} location on Google Maps\">\n                        ${trailInfo.fullName}\n                        <span style=\"font-size: 0.7em; vertical-align: top; padding-left: 5px;\">&#128205;<\/span>\n                    <\/a>\n                <\/h3>\n            `;\n        }\n\n        $detailsArea.innerHTML = `\n            <span id=\"prev-btn\" class=\"slideshow-arrow ${currentIndex <= 0 ? 'disabled' : ''}\">\u276e<\/span>\n\n            <div id=\"map-image-content\">\n\n                ${titleLinkHtml}\n\n                <img decoding=\"async\"\n                    id=\"clickable-map\"\n                    src=\"${simpleMapUrl}\"\n                    alt=\"Map for ${trailInfo.fullName}\"\n                    title=\" \"\n                    data-full-info=\"${infoPosterUrl}\"\n                    data-trail-code=\"${trailCode}\"\n                    onerror=\"if(!this.dataset.fallback){this.dataset.fallback='true';this.src=this.src.replace('.webp','.jpg');}else{this.onerror=null;this.src='${PLACEHOLDER_IMG_URL}';}\">\n\n                <!-- Removed caption below the map to make the links closer <p style=\"font-style: italic; font-size: 0.9em; margin-top: 10px;\">Tap & hold\/hover the map for magnifying magic<\/p> -->\n\n            <\/div>\n\n            <span id=\"next-btn\" class=\"slideshow-arrow ${currentIndex >= ORDERED_TRAIL_KEYS.length - 1 ? 'disabled' : ''}\">\u276f<\/span>\n        `;\n\n        \/\/ 2. Update the dropdown selection\n        $select.value = trailCode;\n\n        \/\/ First time a trail is shown it fades in gently; trails already seen appear instantly\n        if (!seenTrails[trailCode]) {\n            seenTrails[trailCode] = true;\n            document.getElementById('map-image-content').classList.add('map-fade');\n        }\n\n        \/\/ links appear once a trail is chosen\n        document.getElementById('info-links').style.display = 'block';\n\n        \/\/ 3. Attach new event listeners to the *newly created* elements\n\n        \/\/ Map Click (Open Details inside modal lightbox)\n        document.getElementById('clickable-map').addEventListener('click', function() {\n            openDetails(this.dataset.trailCode);\n        });\n\n        \/\/ Slideshow Navigation Handlers (Part 1 - Map Browsing)\n        const newPrevBtn = document.getElementById('prev-btn');\n        const newNextBtn = document.getElementById('next-btn');\n\n        if (!newPrevBtn.classList.contains('disabled')) {\n            newPrevBtn.addEventListener('click', function() {\n                navigateTrail(-1);\n            });\n        }\n\n        if (!newNextBtn.classList.contains('disabled')) {\n            newNextBtn.addEventListener('click', function() {\n                navigateTrail(1);\n            });\n        }\n    }\n\n    \/\/ SLIDESHOW NAVIGATION LOGIC\n    function navigateTrail(direction) {\n\n        const currentTrail = $select.value;\n        let currentIndex = ORDERED_TRAIL_KEYS.indexOf(currentTrail);\n\n        if (currentIndex === -1) {\n            \/\/ Start from the first trail if nothing is selected\n            currentIndex = direction > 0 ? -1 : 0;\n        }\n\n        let newIndex = currentIndex + direction;\n\n        if (newIndex >= 0 && newIndex < ORDERED_TRAIL_KEYS.length) {\n            const newTrailCode = ORDERED_TRAIL_KEYS[newIndex];\n            loadTrailMap(newTrailCode);\n        }\n    }\n\n    \/\/ LIGHTBOX FUNCTIONS (for the full info poster)\n    function openLightbox(fullImageUrl) {\n        $modalImage.src = fullImageUrl;\n        $modal.style.display = 'block';\n        lockScroll();\n    }\n\n    function closeLightbox() {\n        $modal.style.display = 'none';\n        unlockScroll();\n    }\n\n    \/\/ 1. POPULATE DROPDOWN (initial setup)\n    for (const island in TRAILS_GROUPED) {\n\n        const optgroup = document.createElement('optgroup');\n        optgroup.label = island;\n\n        for (const pr in TRAILS_GROUPED[island]) {\n\n            const option = document.createElement('option');\n            option.value = pr;\n            option.textContent = TRAILS_GROUPED[island][pr];\n            option.dataset.fullName = TRAILS_GROUPED[island][pr];\n\n            optgroup.appendChild(option);\n        }\n\n        $select.appendChild(optgroup);\n    }\n\n    \/\/ NEW the opening view, kept for reset\n    const INITIAL_DETAILS_HTML = $detailsArea.innerHTML;\n\n    \/\/ 2. EVENT LISTENER FOR DROPDOWN CHANGE (loads map)\n    $select.addEventListener('change', function() {\n\n        const selectedTrail = this.value;\n\n        if (selectedTrail) {\n            loadTrailMap(selectedTrail);\n        } else {\n            $detailsArea.innerHTML = INITIAL_DETAILS_HTML;\n            document.getElementById('info-links').style.display = 'none';\n        }\n    });\n\n    \/\/ 3. EVENT LISTENER FOR LEGEND LINK\n    $legendLink.addEventListener('click', function(e) {\n        e.preventDefault();\n        openLightbox(LEGEND_IMAGE_URL);\n    });\n\n    \/\/ Features info ICON START (delete this block and the (i) link to undo)\n    document.getElementById('open-ancient-scroll-link').addEventListener('click', function(e) {\n        e.preventDefault();\n        openLightbox(SCROLL_IMAGE_URL);\n    });\n    \/\/ Features info ICON END\n\n    \/\/ 4. CLOSE MODAL HANDLERS\n    document.querySelector('.close-modal').addEventListener('click', closeLightbox);\n\n    $modal.addEventListener('touchmove', function(e) {\n        if (e.target === $modal) {\n            e.preventDefault();\n        }\n\n    }, { passive: false });\n\n    \/\/ Close modal when clicking the dark background\n    $modal.addEventListener('click', function(e) {\n\n        if (e.target === this) {\n            closeLightbox();\n        }\n    });\n\n    \/\/ MAGNIFIER START (delete everything from here to MAGNIFIER END to undo)\n    const LENS_SIZE = 180;\n    const LENS_ZOOM = 2.5;\n\n    const lens = document.createElement('div');\n\n    lens.style.cssText =\n        'position:fixed;' +\n        'display:none;' +\n        'width:' + LENS_SIZE + 'px;' +\n        'height:' + LENS_SIZE + 'px;' +\n        'border-radius:50%;' +\n        'box-shadow:0 0 0 3px #fff,0 4px 14px rgba(0,0,0,0.35);' +\n        'pointer-events:none;' +\n        'z-index:9998;' +\n        'background-color:#ffffff;' +\n        'background-repeat:no-repeat;';\n\n    document.body.appendChild(lens);\n\n    function moveLens(img, clientX, clientY) {\n\n        const r = img.getBoundingClientRect();\n        const x = clientX - r.left;\n        const y = clientY - r.top;\n\n        lens.style.backgroundImage = 'url(\"' + img.currentSrc + '\")';\n        lens.style.backgroundSize = (r.width * LENS_ZOOM) + 'px ' + (r.height * LENS_ZOOM) + 'px';\n        lens.style.backgroundPosition = (LENS_SIZE \/ 2 - x * LENS_ZOOM) + 'px ' + (LENS_SIZE \/ 2 - y * LENS_ZOOM) + 'px';\n        lens.style.left = (clientX - LENS_SIZE \/ 2) + 'px';\n        lens.style.top = (clientY - LENS_SIZE \/ 2) + 'px';\n\n        img.style.cursor = 'none';\n        lens.style.display = 'block';\n    }\n\n    \/\/ Desktop only (devices with a real hover pointer)\n    if (window.matchMedia('(hover: hover)').matches) {\n\n        $detailsArea.addEventListener('mousemove', function(e) {\n            if (e.target.id !== 'clickable-map') {\n                lens.style.display = 'none';\n                return;\n            }\n\n            moveLens(e.target, e.clientX, e.clientY);\n        });\n\n        $detailsArea.addEventListener('mouseleave', function() {\n            lens.style.display = 'none';\n        });\n\n        $detailsArea.addEventListener('click', function() {\n            lens.style.display = 'none';\n        });\n    }\n\n    \/\/ Mobile: hold a finger still on the map to start the lens\n    let lensTimer = null;\n    let lensActive = false;\n    let lensImg = null;\n    let lensStartX = 0;\n    let lensStartY = 0;\n\n    function placeTouchLens(t) {\n\n        \/\/ mobile magnification, desktop keeps LENS_ZOOM\n        const TOUCH_ZOOM = 3.4;\n\n        const r = lensImg.getBoundingClientRect();\n\n        \/\/ what the glass shows follows the finger to the screen edge\n        const fx = t.clientX;\n\n        \/\/ the glass itself stays on screen\n        const cx = Math.min(\n            Math.max(fx, LENS_SIZE \/ 2),\n            window.innerWidth - LENS_SIZE \/ 2\n        );\n\n        const cy = Math.max(LENS_SIZE \/ 2, t.clientY - LENS_SIZE \/ 2 - 20);\n\n        if (fx < r.left || fx > r.right || cy < r.top || cy > r.bottom) {\n            lens.style.display = 'none';\n            return;\n        }\n\n        moveLens(lensImg, cx, cy);\n\n        lens.style.backgroundSize = (r.width * TOUCH_ZOOM) + 'px ' + (r.height * TOUCH_ZOOM) + 'px';\n\n        lens.style.backgroundPosition =\n            (LENS_SIZE \/ 2 - (fx - r.left) * TOUCH_ZOOM) + 'px ' +\n            (LENS_SIZE \/ 2 - (cy - r.top) * TOUCH_ZOOM) + 'px';\n    }\n\n    $detailsArea.addEventListener('touchstart', function(e) {\n\n        clearTimeout(lensTimer);\n\n        if (e.touches.length > 1) {\n            lensActive = false;\n            lens.style.display = 'none';\n            return;\n        }\n\n        if (e.target.id !== 'clickable-map') {\n            return;\n        }\n\n        lensImg = e.target;\n        lensStartX = e.touches[0].clientX;\n        lensStartY = e.touches[0].clientY;\n\n        lensImg.style.webkitTouchCallout = 'none';\n        lensImg.style.webkitUserSelect = 'none';\n        lensImg.style.userSelect = 'none';\n\n        lensTimer = setTimeout(function() {\n\n            lensActive = true;\n\n            \/\/ keeps swipe-to-change-trail quiet while the lens is in use\n            touchStartX = null;\n            placeTouchLens({ clientX: lensStartX, clientY: lensStartY });\n        }, 350);\n    }, { passive: true });\n\n    $detailsArea.addEventListener('touchmove', function(e) {\n\n        if (lensActive) {\n            e.preventDefault();\n            placeTouchLens(e.touches[0]);\n            return;\n        }\n\n        const t = e.touches[0];\n        if (Math.abs(t.clientX - lensStartX) > 10 || Math.abs(t.clientY - lensStartY) > 10) {\n            clearTimeout(lensTimer);\n        }\n    }, { passive: false });\n\n    function endTouchLens(e) {\n\n        clearTimeout(lensTimer);\n\n        if (lensActive) {\n            lensActive = false;\n            lens.style.display = 'none';\n\n            if (e.cancelable) {\n                e.preventDefault();\n            }\n        }\n    }\n\n    $detailsArea.addEventListener('touchend', endTouchLens, { passive: false });\n    $detailsArea.addEventListener('touchcancel', endTouchLens, { passive: false });\n\n    \/\/ Stops the phone's long-press menu while the lens is in use\n    $detailsArea.addEventListener('contextmenu', function(e) {\n\n        if (lensActive) {\n            e.preventDefault();\n        }\n    });\n\n    \/\/ MAGNIFIER END\n\n    \/\/ LIGHTBOX MAGNIFIER START (delete everything from here to LIGHTBOX MAGNIFIER END to undo)\n\n    \/\/ lens sits above the lightbox\n    lens.style.zIndex = '100001';\n\n    \/\/ gentler zoom for legend and scroll\n    const MODAL_ZOOM_DESKTOP = 1.8;\n    const MODAL_ZOOM_TOUCH = 2.2;\n\n    function modalLens(gx, gy, fx, zoom) {\n\n        const r = $modalImage.getBoundingClientRect();\n        const nw = $modalImage.naturalWidth;\n        const nh = $modalImage.naturalHeight;\n\n        if (!nw) {\n            return;\n        }\n\n        const s = Math.min(r.width \/ nw, r.height \/ nh);\n        const w = nw * s;\n        const h = nh * s;\n\n        const left = r.left + (r.width - w) \/ 2;\n        const top = r.top + (r.height - h) \/ 2;\n\n        if (fx < left || fx > left + w || gy < top || gy > top + h) {\n            lens.style.display = 'none';\n            return;\n        }\n\n        lens.style.backgroundImage = 'url(\"' + $modalImage.currentSrc + '\")';\n        lens.style.backgroundSize = (w * zoom) + 'px ' + (h * zoom) + 'px';\n        lens.style.backgroundPosition =\n            (LENS_SIZE \/ 2 - (fx - left) * zoom) + 'px ' +\n            (LENS_SIZE \/ 2 - (gy - top) * zoom) + 'px';\n        lens.style.left = (gx - LENS_SIZE \/ 2) + 'px';\n        lens.style.top = (gy - LENS_SIZE \/ 2) + 'px';\n        lens.style.display = 'block';\n    }\n\n    \/\/ Desktop: hover\n    if (window.matchMedia('(hover: hover)').matches) {\n\n        $modalImage.addEventListener('mousemove', function(e) {\n            $modalImage.style.cursor = 'none';\n            modalLens(e.clientX, e.clientY, e.clientX, MODAL_ZOOM_DESKTOP);\n        });\n\n        $modalImage.addEventListener('mouseleave', function() {\n            lens.style.display = 'none';\n        });\n    }\n\n    document.addEventListener('keydown', function(e) {\n\n        if (e.key === 'Escape') {\n            lens.style.display = 'none';\n        }\n    });\n\n    \/\/ Mobile: press and hold\n    let mTimer = null;\n    let mActive = false;\n    let mX = 0;\n    let mY = 0;\n\n    function mTouchLens(t) {\n\n        const gx = Math.min(\n            Math.max(t.clientX, LENS_SIZE \/ 2),\n            window.innerWidth - LENS_SIZE \/ 2\n        );\n\n        const gy = Math.max(LENS_SIZE \/ 2, t.clientY - LENS_SIZE \/ 2 - 20);\n\n        modalLens(gx, gy, t.clientX, MODAL_ZOOM_TOUCH);\n    }\n\n    $modalImage.addEventListener('touchstart', function(e) {\n        clearTimeout(mTimer);\n\n        if (e.touches.length > 1) {\n            mActive = false;\n            lens.style.display = 'none';\n            return;\n        }\n\n        mX = e.touches[0].clientX;\n        mY = e.touches[0].clientY;\n\n        $modalImage.style.webkitTouchCallout = 'none';\n        $modalImage.style.webkitUserSelect = 'none';\n        $modalImage.style.userSelect = 'none';\n\n        mTimer = setTimeout(function() {\n\n            mActive = true;\n            mTouchLens({ clientX: mX, clientY: mY });\n\n        }, 350);\n\n    }, { passive: true });\n\n    $modalImage.addEventListener('touchmove', function(e) {\n        if (mActive) {\n            e.preventDefault();\n            mTouchLens(e.touches[0]);\n            return;\n        }\n\n        const t = e.touches[0];\n        if (Math.abs(t.clientX - mX) > 10 || Math.abs(t.clientY - mY) > 10) {\n            clearTimeout(mTimer);\n        }\n    }, { passive: false });\n\n    function endModalLens(e) {\n\n        clearTimeout(mTimer);\n\n        if (mActive) {\n            mActive = false;\n            lens.style.display = 'none';\n\n            if (e.cancelable) {\n                e.preventDefault();\n            }\n        }\n    }\n\n    $modalImage.addEventListener('touchend', endModalLens, { passive: false });\n    $modalImage.addEventListener('touchcancel', endModalLens, { passive: false });\n\n    $modalImage.addEventListener('contextmenu', function(e) {\n\n        if (mActive) {\n            e.preventDefault();\n        }\n    });\n\n    \/\/ LIGHTBOX MAGNIFIER END\n\n    \/\/ SCROLL MAGNIFIER START (delete everything from here to SCROLL MAGNIFIER END to undo)\n\n    \/\/ gentle zoom for the scroll on the page\n    const SCROLL_ZOOM_DESKTOP = 2;\n    const SCROLL_ZOOM_TOUCH = 2.5;\n\n    function scrollLens(img, gx, gy, fx, zoom) {\n        const r = img.getBoundingClientRect();\n        if (fx < r.left || fx > r.right || gy < r.top || gy > r.bottom) {\n            lens.style.display = 'none';\n            return;\n        }\n\n        lens.style.backgroundImage = 'url(\"' + img.currentSrc + '\")';\n        lens.style.backgroundSize = (r.width * zoom) + 'px ' + (r.height * zoom) + 'px';\n        lens.style.backgroundPosition =\n            (LENS_SIZE \/ 2 - (fx - r.left) * zoom) + 'px ' +\n            (LENS_SIZE \/ 2 - (gy - r.top) * zoom) + 'px';\n        lens.style.left = (gx - LENS_SIZE \/ 2) + 'px';\n        lens.style.top = (gy - LENS_SIZE \/ 2) + 'px';\n        lens.style.display = 'block';\n    }\n\n    \/\/ Desktop: hover\n    if (window.matchMedia('(hover: hover)').matches) {\n        $detailsArea.addEventListener('mousemove', function(e) {\n            if (e.target.alt !== 'Ancient scroll') {\n                return;\n            }\n\n            e.target.style.cursor = 'none';\n            scrollLens(e.target, e.clientX, e.clientY, e.clientX, SCROLL_ZOOM_DESKTOP);\n        });\n    }\n\n    \/\/ Mobile: press and hold\n    let sTimer = null;\n    let sActive = false;\n    let sImg = null;\n    let sX = 0;\n    let sY = 0;\n\n    function sTouchLens(t) {\n\n        const gx = Math.min(\n            Math.max(t.clientX, LENS_SIZE \/ 2),\n            window.innerWidth - LENS_SIZE \/ 2\n        );\n\n        const gy = Math.max(LENS_SIZE \/ 2, t.clientY - LENS_SIZE \/ 2 - 20);\n        scrollLens(sImg, gx, gy, t.clientX, SCROLL_ZOOM_TOUCH);\n    }\n\n    $detailsArea.addEventListener('touchstart', function(e) {\n        clearTimeout(sTimer);\n\n        if (e.touches.length > 1) {\n            sActive = false;\n            lens.style.display = 'none';\n            return;\n        }\n\n        if (e.target.alt !== 'Ancient scroll') {\n            return;\n        }\n\n        sImg = e.target;\n        sX = e.touches[0].clientX;\n        sY = e.touches[0].clientY;\n\n        sImg.style.webkitTouchCallout = 'none';\n        sImg.style.webkitUserSelect = 'none';\n        sImg.style.userSelect = 'none';\n\n        sTimer = setTimeout(function() {\n            sActive = true;\n            sTouchLens({ clientX: sX, clientY: sY });\n        }, 350);\n    }, { passive: true });\n\n    $detailsArea.addEventListener('touchmove', function(e) {\n        if (sActive) {\n            e.preventDefault();\n            sTouchLens(e.touches[0]);\n            return;\n        }\n\n        const t = e.touches[0];\n\n        if (Math.abs(t.clientX - sX) > 10 || Math.abs(t.clientY - sY) > 10) {\n            clearTimeout(sTimer);\n        }\n    }, { passive: false });\n\n    function endScrollLens(e) {\n        clearTimeout(sTimer);\n\n        if (sActive) {\n            sActive = false;\n            lens.style.display = 'none';\n\n            if (e.cancelable) {\n                e.preventDefault();\n            }\n        }\n    }\n\n    $detailsArea.addEventListener('touchend', endScrollLens, { passive: false });\n    $detailsArea.addEventListener('touchcancel', endScrollLens, { passive: false });\n\n    $detailsArea.addEventListener('contextmenu', function(e) {\n\n        if (sActive) {\n            e.preventDefault();\n        }\n    });\n\n    \/\/ SCROLL MAGNIFIER END\n\n    \/\/ DETAILS WINDOW SCRIPT START (delete everything from here to DETAILS WINDOW SCRIPT END, and the Details link, to undo)\n\n    \/\/ This part only FILLS the card (in the HTML above) with the words from the trail's details file.\n    \/\/ The style is in the CSS and the structure is in the HTML, so you rarely need to change anything here.\n\n    \/\/ ---- Settings you may want to change ----\n    \/\/ change to version '2', '3' etc, after editing a details file, so phones load the new one\n    const DETAILS_V = '1.1';\n\n    \/\/ pr1  +  -details.json  =  pr1-details.json (in the same folder as the maps)\n    const DETAILS_ENDING = '-details.json';\n\n\/\/ pr1 + -height.webp = pr1-height.webp\n    const GRAPH_ENDING = '-height.webp';\n    const GRAPH_ENDING_REVERSE = '-height-reverse.webp';\n\n    \/\/ the coordinates are added at the end\n    const MAPS_URL = 'https:\/\/www.google.com\/maps\/search\/?api=1&query=';\n\n    \/\/ ---- The card ----\n    const $details = document.getElementById('details-window');\n\n    \/\/ same trick as the lightbox, so no parent can get in the way\n    document.body.appendChild($details);\n\n    \/\/ each details file is loaded once per visit\n    const DETAILS_CACHE = {};\n    let detailsCode = '';\n    let detailsOpen = false;\n\n    \/\/ keep the page locked while this window is open, even after the lightbox (graph) closes\n    const baseUnlockScroll = unlockScroll;\n    unlockScroll = function() {\n        if (detailsOpen) {\n            return;\n        }\n\n        baseUnlockScroll();\n    };\n\n    \/\/ show or hide a part\/section\n    function showPart(el, yes) {\n        if (el) {\n            el.classList.toggle('is-hidden', !yes);\n        }\n    }\n\n    \/\/ put words inside a part\/section (and hide the section's box when there are no words)\n    function fillText(parent, selector, text, boxSelector) {\n        const el = parent.querySelector(selector);\n\n        if (el) {\n            el.textContent = text ? text : '';\n        }\n\n        if (boxSelector) {\n            showPart(parent.querySelector(boxSelector), text ? true : false);\n        }\n    }\n\n    function slugOf(code) {\n        return code.toLowerCase().replace(\/[^a-z0-9]\/g, '-');\n    }\n\n    \/\/ fill the card with one route ('official' or 'reverse')\n    function showRoute(routeName) {\n\n        const trail = DETAILS_CACHE[detailsCode];\n        const route = trail[routeName] ? trail[routeName] : trail.official;\n        const isReverse = trail[routeName] ? (routeName === 'reverse') : false;\n        const hasBoth = trail.official ? (trail.reverse ? true : false) : false;\n        const info = getTrailInfo(detailsCode);\n\n        $details.classList.toggle('is-reverse', isReverse);\n\n        showPart($details.querySelector('.details-content'), true);\n        showPart($details.querySelector('[data-message]'), false);\n\n        \/\/ route buttons (only when the trail has both routes)\n        $details.querySelectorAll('[data-route]').forEach(function(button) {\n\n            const name = button.getAttribute('data-route');\n\n            showPart(button, hasBoth);\n            fillText(button, 'span', trail[name] ? trail[name].button : '');\n            button.classList.toggle('is-active', trail[name] === route);\n        });\n\n        \/\/ badge, icon, title, intro\n        const badge = $details.querySelector('[data-badge]');\n        const direction = route.direction || route.badge || route.button || '';\n\n        badge.textContent = direction;\n        showPart(badge, direction ? true : false);\n\n        const icon = $details.querySelector('[data-icon]');\n        icon.textContent = icon.getAttribute(isReverse ? 'data-reverse' : 'data-official');\n\n        $details.querySelector('[data-title]').textContent =\n            route.title ? route.title : (info ? info.fullName : detailsCode);\n\n        const intro = $details.querySelector('[data-intro]');\n        intro.textContent = route.intro ? route.intro : '';\n        showPart(intro, route.intro ? true : false);\n\n        \/\/ the small boxes\n        const specs = route.specs ? route.specs : {};\n\n        $details.querySelectorAll('[data-spec]').forEach(function(box) {\n\n            const spec = specs[box.getAttribute('data-spec')];\n\n            showPart(box, spec ? true : false);\n\n            if (spec) {\n                fillText(box, '.details-spec-value', spec.value);\n                fillText(box, '.details-spec-note', spec.note, '.details-spec-note');\n            }\n        });\n\n        showPart(\n            $details.querySelector('.details-specs'),\n            $details.querySelectorAll('[data-spec]:not(.is-hidden)').length > 0\n        );\n\n        \/\/ the places\n        $details.querySelectorAll('[data-place]').forEach(function(box) {\n\n            const place = route[box.getAttribute('data-place')];\n\n            showPart(box, place ? true : false);\n\n            if (place) {\n                fillText(box, '.details-place-name', place.name);\n\n                const coords = box.querySelector('.details-coords');\n\n                coords.textContent = place.coords ? place.coords : '';\n\n                coords.href = place.link\n                    ? place.link\n                    : (place.coords ? MAPS_URL + place.coords.replace(\/\\s\/g, '') : '#');\n\n                showPart(coords, place.coords ? true : false);\n\n                fillText(box, '.details-note-text', place.note, '.details-note');\n            }\n        });\n\n        \/\/ height graph (hides itself if the picture is not there yet)\n        const graphBox = $details.querySelector('[data-graph]');\n        const graph = graphBox.querySelector('img');\n\n        let src = route.graph\n            ? route.graph\n            : slugOf(detailsCode) + (isReverse ? GRAPH_ENDING_REVERSE : GRAPH_ENDING);\n\n        if (src.indexOf('\/') === -1) {\n            src = BASE_MAP_PATH + src;\n        }\n\n        showPart(graphBox, true);\n\n        graph.onerror = function() {\n            showPart(graphBox, false);\n        };\n\n        graph.src = src;\n\n        $details.querySelector('.details-card').scrollTop = 0;\n    }\n\n    \/\/ show a short message instead of the card (loading, or no details yet)\n    function showMessage(text) {\n        $details.classList.remove('is-reverse');\n\n        showPart($details.querySelector('.details-content'), false);\n        showPart($details.querySelector('[data-message]'), true);\n\n        $details.querySelector('[data-message]').textContent = text;\n\n        $details.querySelectorAll('[data-route]').forEach(function(button) {\n            showPart(button, false);\n        });\n    }\n\n    function showTab(name) {\n        $details.querySelectorAll('[data-tab]').forEach(function(b) {\n            b.classList.toggle('is-active', b.getAttribute('data-tab') === name);\n        });\n\n        $details.querySelectorAll('[data-panel]').forEach(function(p) {\n            showPart(p, p.getAttribute('data-panel') === name);\n        });\n\n       if (name === 'description') {\n            loadDescription();\n        }\n\n        if (name === 'legend') {\n            $details.querySelector('[data-panel=\"legend\"] img').src = LEGEND_IMAGE_URL;\n        }\n\n        if (name === 'howto') {\n            $details.querySelector('[data-panel=\"howto\"] img').src = SCROLL_IMAGE_URL;\n        }\n\n        $details.querySelector('.details-card').scrollTop = 0;\n    }\n\n    function openDetails(code, tab) {\n        detailsCode = code;\n        detailsOpen = true;\n\n        $details.style.display = 'flex';\n\n        lockScroll();\n\n        showTab(tab || 'details');\n\n        if (DETAILS_CACHE[code]) {\n            showRoute('official');\n            return;\n        }\n\n        showMessage('Opening details\u2026');\n\n        fetch(BASE_MAP_PATH + slugOf(code) + DETAILS_ENDING + '?v=' + DETAILS_V)\n\n            .then(function(res) {\n                if (!res.ok) {\n                    throw new Error('missing');\n                }\n\n                return res.json();\n            })\n\n            .then(function(data) {\n                DETAILS_CACHE[code] = data;\n                showRoute('official');\n            })\n\n            .catch(function() {\n                showMessage('Details for this trail are on their way \\uD83C\\uDF3F');\n            });\n    }\n\n\n    function closeDetails() {\n        detailsOpen = false;\n\n        $details.style.display = 'none';\n\n        baseUnlockScroll();\n    }\n\n    \/\/ taps inside the window: route buttons, close, dark background, tabs\n    $details.addEventListener('click', function(e) {\n\n        const t = e.target;\n\n        if (t === $details) {\n            closeDetails();\n            return;\n        }\n\n        if (t.closest('[data-close]')) {\n            closeDetails();\n            return;\n        }\n\n        const button = t.closest('[data-route]');\n\n        if (button) {\n            showRoute(button.getAttribute('data-route'));\n            return;\n        }\n\n        const flag = t.closest('[data-language]');\n\n        if (flag) {\n            descriptionLanguage = flag.getAttribute('data-language');\n\n            if (DESCRIPTION_CACHE[detailsCode]) {\n                showDescription();\n            }\n\n            return;\n        }\n\n        const tab = t.closest('[data-tab]');\n\n        if (tab) {\n            showTab(tab.getAttribute('data-tab'));\n            return;\n        }\n\n    });\n\n    \/\/ REMOVED FROM ABOVE }); to stop the graph from opening a modal inside a modal\n    \/\/  if (t.closest('[data-graph]')) { openLightbox(t.closest('[data-graph]').querySelector('img').src); }\n\n    \/\/ the Details link under the map\n    document.getElementById('open-details-link').addEventListener('click', function(e) {\n        e.preventDefault();\n\n        if ($select.value) {\n            openDetails($select.value);\n        }\n    });\n\n    \/\/ the Description link under the map\n    document.getElementById('open-description-link').addEventListener('click', function(e) {\n\n        e.preventDefault();\n\n        if ($select.value) {\n            openDetails($select.value, 'description');\n        }\n    });\n\n    \/\/ keys: Esc closes this window, the arrows do not change the trail behind it\n    document.addEventListener('keydown', function(e) {\n        if (!detailsOpen) {\n            return;\n        }\n\n        if ($modal.style.display === 'block') {\n            return;\n        }\n\n        if (e.key === 'Escape') {\n            closeDetails();\n            e.stopPropagation();\n            return;\n        }\n\n        if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {\n            e.stopPropagation();\n        }\n\n        }, true);\n\n\/\/ DESCRIPTION TAB START (delete everything from here to DESCRIPTION TAB END to undo)\n\n    \/\/ Fills the Description tab from the trail's description file, for example pr1-description.json\n    \/\/ (in the same folder as the maps). The look is in the CSS, the structure is in the HTML.\n\n    \/\/ ---- Settings you may want to change ----\n    \/\/ change to '2', '3' etc, after editing a description file, so phones load the new one\n    const DESCRIPTION_V = '1';\n\n    \/\/ pr1  +  -description.json  =  pr1-description.json\n    const DESCRIPTION_ENDING = '-description.json';\n\n    \/\/ the language shown first: 'en' or 'pt'\n    const DESCRIPTION_DEFAULT_LANGUAGE = 'en';\n\n    \/\/ ---- The Description tab ----\n    \/\/ each description file is loaded once per visit\n    const DESCRIPTION_CACHE = {};\n    let descriptionLanguage = DESCRIPTION_DEFAULT_LANGUAGE;\n\n    \/\/ show a short message instead of the description (loading, or no description yet)\n    function showDescriptionMessage(text) {\n        const panel = $details.querySelector('[data-panel=\"description\"]');\n\n        showPart(panel.querySelector('[data-description-content]'), false);\n        showPart(panel.querySelector('[data-description-message]'), true);\n\n        panel.querySelector('[data-description-message]').textContent = text;\n    }\n\n    \/\/ fill the tab with the description, in the chosen language\n    function showDescription() {\n\n        const data = DESCRIPTION_CACHE[detailsCode];\n        const panel = $details.querySelector('[data-panel=\"description\"]');\n        const info = getTrailInfo(detailsCode);\n\n        \/\/ if one language is missing, the other one is shown instead\n        const otherLanguage = (descriptionLanguage === 'en') ? 'pt' : 'en';\n        const paragraphs = data[descriptionLanguage]\n            ? data[descriptionLanguage]\n            : (data[otherLanguage] ? data[otherLanguage] : []);\n\n        showPart(panel.querySelector('[data-description-content]'), true);\n        showPart(panel.querySelector('[data-description-message]'), false);\n\n        \/\/ the flags: the chosen one is bright, the other one fades\n        panel.querySelectorAll('[data-language]').forEach(function(flag) {\n            flag.classList.toggle('is-active', flag.getAttribute('data-language') === descriptionLanguage);\n        });\n\n        \/\/ the title (its words are in the HTML; a description file can bring its own)\n        const title = panel.querySelector('[data-description-title]');\n        const ownTitle = data.title ? data.title[descriptionLanguage] : '';\n\n        title.textContent = ownTitle ? ownTitle : title.getAttribute('data-' + descriptionLanguage);\n\n        \/\/ which trail this is\n        fillText(panel, '[data-description-trail]', info ? info.fullName : detailsCode);\n\n        \/\/ the paragraphs\n        const text = panel.querySelector('[data-description-text]');\n\n        text.innerHTML = '';\n\n        paragraphs.forEach(function(paragraph) {\n            const p = document.createElement('p');\n            p.textContent = paragraph;\n            text.appendChild(p);\n        });\n\n        \/\/ a gentle fade each time the words change\n        text.classList.remove('is-fading-in');\n        void text.offsetWidth;\n        text.classList.add('is-fading-in');\n    }\n\n    \/\/ get the trail's description file (once per visit), then show it\n    function loadDescription() {\n\n        const code = detailsCode;\n\n        if (DESCRIPTION_CACHE[code]) {\n            showDescription();\n            return;\n        }\n\n        showDescriptionMessage('Opening description\u2026');\n\n        fetch(BASE_MAP_PATH + slugOf(code) + DESCRIPTION_ENDING + '?v=' + DESCRIPTION_V)\n\n            .then(function(res) {\n                if (!res.ok) {\n                    throw new Error('missing');\n                }\n\n                return res.json();\n            })\n\n            .then(function(data) {\n                DESCRIPTION_CACHE[code] = data;\n\n                if (code === detailsCode) {\n                    showDescription();\n                }\n            })\n\n            .catch(function() {\n                if (code === detailsCode) {\n                    showDescriptionMessage('The description for this trail is on its way \\uD83C\\uDF3F');\n                }\n            });\n    }\n\n    \/\/ DESCRIPTION TAB END\n\n    \/\/ DETAILS IMAGES MAGNIFIER START (delete everything from here to DETAILS IMAGES MAGNIFIER END to undo)\n\n    \/\/ the images that get the lens: legend, how-to-use and the height graph\n    const DETAILS_IMAGES = '.details-picture, .details-graph img';\n\n    const DETAILS_ZOOM_DESKTOP = 2;\n    const DETAILS_ZOOM_TOUCH = 2.5;\n\n    \/\/ Desktop: hover\n    if (window.matchMedia('(hover: hover)').matches) {\n\n        $details.addEventListener('mousemove', function(e) {\n\n            if (!e.target.matches(DETAILS_IMAGES)) {\n                lens.style.display = 'none';\n                return;\n            }\n\n            e.target.style.cursor = 'none';\n\n            scrollLens(e.target, e.clientX, e.clientY, e.clientX, DETAILS_ZOOM_DESKTOP);\n        });\n\n        $details.addEventListener('mouseleave', function() {\n            lens.style.display = 'none';\n        });\n    }\n\n    \/\/ Mobile: press and hold\n    let dTimer = null;\n    let dActive = false;\n    let dImg = null;\n    let dX = 0;\n    let dY = 0;\n\n    function dTouchLens(t) {\n\n        const gx = Math.min(\n            Math.max(t.clientX, LENS_SIZE \/ 2),\n            window.innerWidth - LENS_SIZE \/ 2\n        );\n\n        const gy = Math.max(LENS_SIZE \/ 2, t.clientY - LENS_SIZE \/ 2 - 20);\n\n        scrollLens(dImg, gx, gy, t.clientX, DETAILS_ZOOM_TOUCH);\n    }\n\n    $details.addEventListener('touchstart', function(e) {\n\n        clearTimeout(dTimer);\n\n        if (e.touches.length > 1) {\n            dActive = false;\n            lens.style.display = 'none';\n            return;\n        }\n\n        if (!e.target.matches(DETAILS_IMAGES)) {\n            return;\n        }\n\n        dImg = e.target;\n        dX = e.touches[0].clientX;\n        dY = e.touches[0].clientY;\n\n        dImg.style.webkitTouchCallout = 'none';\n        dImg.style.webkitUserSelect = 'none';\n        dImg.style.userSelect = 'none';\n\n        dTimer = setTimeout(function() {\n            dActive = true;\n            dTouchLens({ clientX: dX, clientY: dY });\n        }, 350);\n\n    }, { passive: true });\n\n    $details.addEventListener('touchmove', function(e) {\n\n        if (dActive) {\n            e.preventDefault();\n            dTouchLens(e.touches[0]);\n            return;\n        }\n\n        const t = e.touches[0];\n\n        if (Math.abs(t.clientX - dX) > 10 || Math.abs(t.clientY - dY) > 10) {\n            clearTimeout(dTimer);\n        }\n\n    }, { passive: false });\n\n    function endDetailsLens(e) {\n\n        clearTimeout(dTimer);\n\n        if (dActive) {\n            dActive = false;\n            lens.style.display = 'none';\n\n            if (e.cancelable) {\n                e.preventDefault();\n            }\n        }\n    }\n\n    $details.addEventListener('touchend', endDetailsLens, { passive: false });\n    $details.addEventListener('touchcancel', endDetailsLens, { passive: false });\n\n    \/\/ Stops the phone's long-press menu while the lens is in use\n    $details.addEventListener('contextmenu', function(e) {\n\n        if (dActive) {\n            e.preventDefault();\n        }\n    });\n\n    \/\/ DETAILS IMAGES MAGNIFIER END\n\n    \/\/ DETAILS WINDOW SCRIPT END\n\n    \/\/ KEYBOARD: arrow keys switch trail (skipped when the lightbox or a field is in use)\n    document.addEventListener('keydown', function(e) {\n        if ($modal.style.display === 'block' || !$select.value) {\n            return;\n        }\n\n        if (['SELECT', 'INPUT', 'TEXTAREA'].indexOf(e.target.tagName) !== -1) {\n            return;\n        }\n\n        if (e.key === 'ArrowLeft') {\n            e.preventDefault();\n            navigateTrail(-1);\n        }\n\n        if (e.key === 'ArrowRight') {\n            e.preventDefault();\n            navigateTrail(1);\n        }\n    });\n\n    \/\/ SWIPE left or right on the map area switches trail\n    let touchStartX = null;\n    let touchStartY = 0;\n\n    $detailsArea.addEventListener('touchstart', function(e) {\n\n        touchStartX = e.touches.length > 1 ? null : e.changedTouches[0].clientX;\n        touchStartY = e.changedTouches[0].clientY;\n\n    }, { passive: true });\n\n    $detailsArea.addEventListener('touchend', function(e) {\n\n        if (touchStartX === null || !$select.value) {\n            return;\n        }\n\n        const dx = e.changedTouches[0].clientX - touchStartX;\n        const dy = e.changedTouches[0].clientY - touchStartY;\n\n        if (Math.abs(dx) > Math.max(50, Math.abs(dy) * 1.5)) {\n            navigateTrail(dx < 0 ? 1 : -1);\n        }\n\n    }, { passive: true });\n\n    \/\/ Close modal when pressing the ESC key  \n    document.addEventListener('keydown', function(e) {\n\n        if (e.key === 'Escape' ? $modal.style.display === 'block' : false) {\n            closeLightbox();\n        }\n    });\n\n});\n<\/script>\n\n\n\n<div style=\"height:3px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n<\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>Trail status | Hikers Feedback | Info Madeira PR trails mobile friendlyFor reference only, conditions can changePowered by IFCN maps<\/p>\n","protected":false},"author":4,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"slim_seo":{"title":"Madeira PR trails info","description":"Some Information on the official PR Madeira Trails, map and description, powered by IFCN."},"footnotes":""},"class_list":["post-5342","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5342","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/comments?post=5342"}],"version-history":[{"count":189,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5342\/revisions"}],"predecessor-version":[{"id":12494,"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/pages\/5342\/revisions\/12494"}],"wp:attachment":[{"href":"https:\/\/paragliding-in-madeira.com\/weather\/wp-json\/wp\/v2\/media?parent=5342"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}