/* =============================================
   BRAND COLORS
   CSS Custom Properties (variables) defined on
   the :root element are globally available to
   every rule in this stylesheet.

   WHY use variables here?
   If ESN's brand colours ever change, you update
   one value in this block and the entire page
   resyles automatically — no hunting through
   hundreds of individual rules.
   ============================================= */
:root {
    --brand-blue: #0d2b99;  /* Primary blue — headings, badges, buttons, toggle pill    */
    --brand-red:  #ff2248;  /* Accent red  — website badge outline and hover fill       */
    --brand-sky:  #d6effc;  /* Light blue  — org type badge background, toggle capsule */
}

/* Utility class that applies the brand blue colour to any element's text.
   !important ensures it overrides Bootstrap's own colour utilities
   (e.g. if Bootstrap's .text-primary is also applied to the same element). */
.brand-blue { color: var(--brand-blue) !important; }


/* =============================================
   TOGGLE MAP / LIST VIEW  —  Pill Style Slider
   A pure-CSS sliding toggle. The blue background
   pill moves left/right via transform, no JS needed
   for the animation itself.
   ============================================= */

/* Outer container — the light blue capsule background */
.view-toggle-container {
    background-color: var(--brand-sky); /* Light blue tray the buttons sit inside       */
    overflow: hidden;                   /* Clips the slider pill so it never bleeds outside */
    height: 48px;                       /* Fixed height keeps both buttons the same size */
    border: 1px solid rgba(13, 43, 153, 0.05); /* Very faint blue border for definition  */
}

/* Individual toggle button — Map View and List View */
.toggle-btn {
    border: none !important;        /* Remove Bootstrap's default button border          */
    background: transparent !important; /* Button itself is transparent — the slider behind it provides colour */
    color: #6c757d;                 /* Inactive state: Bootstrap's muted grey            */
    font-weight: 600;               /* Semi-bold keeps text legible at small sizes        */
    padding: 6px 24px;              /* Comfortable click/tap target size                 */
    min-width: 125px;               /* Prevents buttons shrinking on very narrow screens */
    height: 100%;                   /* Fills the full 48px capsule height                */
    z-index: 2;                     /* Sits above the sliding blue pill (z-index: 1)
                                       so the text remains clickable and visible          */
    transition: color 0.25s ease-in-out; /* Smoothly fades text from grey to white
                                            when the active class is applied              */
}

/* Remove the default browser focus ring and Bootstrap's active shadow.
   We replace this with a custom focus-visible style below so keyboard
   users still have a visible indicator — we never simply remove focus
   styles without providing an accessible alternative.                  */
.toggle-btn:focus,
.toggle-btn:active {
    box-shadow: none !important;
    outline: none;
}

/* FIX: Accessible keyboard focus ring using :focus-visible.
   WHY :focus-visible instead of :focus?
   :focus fires on every click too, which adds a visible ring around the
   button whenever a mouse user clicks it — most designers find this ugly.
   :focus-visible only activates when the browser determines the user is
   navigating by keyboard (Tab key) or assistive technology, so mouse
   clicks don't trigger it. This satisfies WCAG 2.1 focus visibility
   requirements without affecting the visual design for mouse users.    */
.toggle-btn:focus-visible {
    box-shadow: 0 0 0 3px rgba(13, 43, 153, 0.4) !important;
    /* A soft blue glow ring around the focused button.
       3px spread keeps it visible against both light and dark backgrounds.
       0.4 opacity makes it softer than a hard solid outline.             */
    outline: none; /* Suppress browser default outline — our box-shadow replaces it */
}

/* Active (selected) button text colour.
   The blue pill slides behind this button; text turns white to stay
   readable against the dark blue background.                            */
.toggle-btn.active-toggle {
    color: #ffffff !important;
    background: transparent !important; /* Transparency is intentional — colour comes from the slider */
}

/* The sliding blue pill that moves behind the active button.
   Uses position:absolute so it can be moved independently of the buttons. */
.toggle-slider {
    background-color: var(--brand-blue); /* Solid brand blue fill                        */
    top: 4px;                            /* 4px inset from container top                 */
    bottom: 4px;                         /* 4px inset from container bottom              */
    left: 4px;                           /* Starts aligned to the left (Map View)        */
    width: calc(50% - 4px);             /* Exactly half the container width minus padding
                                           so it covers one button precisely             */
    z-index: 1;                          /* Below button text (z-index:2) but above the
                                           sky-blue container background                 */
    transition: transform 0.3s cubic-bezier(0.25, 1, 0.5, 1);
    /* cubic-bezier(0.25, 1, 0.5, 1) = a springy ease-out curve:
       fast start, slight overshoot feel at the end — more lively than
       a plain ease-in-out and more appropriate for a UI toggle.         */
}

/* When the list view is active, JS adds .list-active to the container.
   This rule slides the pill rightward by exactly its own width (100%)
   to sit behind the List View button instead of the Map View button.   */
.view-toggle-container.list-active .toggle-slider {
    transform: translateX(100%);
    /* translateX(100%) moves the element right by 100% of its own width.
       Since the pill is exactly 50% of the container, this perfectly
       aligns it under the right-hand button without any hardcoded pixels. */
}


/* =============================================
   LAYOUT — View Modes & Responsiveness

   Strategy: Mobile First.
   Base styles below target phones and small tablets.
   The @media (min-width: 769px) block at the bottom
   then progressively enhances the layout for desktop.
   This approach means smaller devices download less CSS.
   ============================================= */

/* Base grid container — flex column on mobile so panels stack vertically */
.directory-grid {
    display: flex;
    flex-direction: column; /* Stack side-panel above map-panel on mobile */
    height: auto;           /* Height is determined by content on mobile   */
    overflow: hidden;       /* Clips any content that escapes the container */
    border-radius: 1rem;    /* Rounded corners on the outer card            */
}

/* ---- MOBILE: Map View ---- */
/* Map sits on top, member list below */
.view-mode-map .side-panel {
    order: 2;                       /* Flexbox order: list renders second (below map) */
    border-top: 2px solid #eee;     /* Visual separator between map and list          */
}
.view-mode-map .map-panel {
    order: 1;                       /* Map renders first (above list) on mobile        */
}
.view-mode-map #map {
    height: 350px !important;       /* Fixed map height on mobile — tall enough to be
                                       usable but leaves room for the list below        */
}
.view-mode-map .scroll-box {
    height: 500px;                  /* Constrains list height so it doesn't push the
                                       page to an unusable length on small screens      */
}

/* ---- MOBILE: List View ---- */
/* Map is completely hidden; list fills the full panel width */
.view-mode-list .map-panel {
    display: none;                  /* Hides the entire map panel in list-only mode    */
}
.view-mode-list .side-panel {
    width: 100%;                    /* List expands to full container width             */
    order: 1;
}
.view-mode-list .scroll-box {
    height: 700px;                  /* Taller scroll area when map is hidden —
                                       more room to browse members                      */
}

/* ---- DESKTOP ENHANCEMENTS (769px and wider) ---- */
@media (min-width: 769px) {

    /* Fixed height container on desktop — prevents the panel from
       growing infinitely tall as member cards load                   */
    .directory-grid {
        height: 800px;
    }

    /* Side-by-side two-column grid on desktop map view.
       !important overrides the flex layout set in the mobile base rules. */
    .view-mode-map {
        display: grid !important;
        grid-template-columns: 400px 1fr !important;
        /* 400px fixed width for the member list — wide enough for card content.
           1fr gives all remaining space to the map.                          */
    }

    /* Restore natural order on desktop — list on left, map on right */
    .view-mode-map .side-panel {
        order: 1;
        border-top: none; /* Remove the mobile separator — not needed in side-by-side layout */
    }
    .view-mode-map .map-panel {
        order: 2;
    }
    .view-mode-map #map {
        height: 100% !important; /* Map fills the entire right column height on desktop */
    }
    .view-mode-map .scroll-box {
        height: 800px; /* Matches the container height so the list scrolls within it */
    }

    /* Desktop list view: single column block layout.
       !important overrides the flex/grid set by the mobile rules.   */
    .view-mode-list {
        display: block !important;
    }
    .view-mode-list .scroll-box {
        height: 800px; /* Matches container height for consistent scrolling behaviour */
    }
}


/* =============================================
   SIDE PANEL — Left column housing the member list
   ============================================= */
.side-panel {
    position: relative;     /* Required for the Back to Top button's absolute positioning
                               to be calculated relative to this panel, not the page     */
    display: flex;
    flex-direction: column; /* Stacks scroll-box vertically inside the panel             */
    background: #fff;
    height: 100%;           /* Fills the full height of its grid cell on desktop         */
}

/* Scrollable inner container — wraps all member cards */
.scroll-box {
    overflow-y: auto;           /* Enables vertical scrolling when cards exceed the height */
    flex-grow: 1;               /* Expands to fill all remaining space inside .side-panel  */
    scroll-behavior: smooth;    /* Animates the jump when scrollTopPanel() is called       */
}


/* =============================================
   MEMBER CARD — Each individual member row
   ============================================= */
.member-card {
    display: flex;                      /* Logo and text info sit side by side           */
    padding: 1.2rem;                    /* Comfortable internal spacing                  */
    border-bottom: 1px solid #eee;      /* Hairline separator between cards              */
    gap: 12px;                          /* Space between logo column and text column     */
    cursor: pointer;                    /* Hand cursor signals the card is clickable     */
    transition: background 0.15s;       /* Smooth background colour change on hover      */
}

/* Subtle highlight on hover to confirm the card is interactive */
.member-card:hover { background: #f8f9ff; }

/* Organisation logo image */
.member-logo {
    width: 45px;
    height: 45px;
    object-fit: contain;    /* Scales the logo to fit within 45×45 without cropping
                               or distorting — important for logos with varied aspect ratios */
    border-radius: 4px;     /* Slight rounding softens hard-edged square logos            */
    flex-shrink: 0;         /* Prevents the logo from being squeezed when the member name
                               is very long — it always stays 45px wide                    */
}

/* Text content column — stacks name, location, badge, and actions vertically */
.member-info {
    display: flex;
    flex-direction: column;
    gap: 4px;               /* Small, consistent spacing between each text row            */
}

/* Member name text */
.member-name { font-size: 0.95rem; }

/* "📍 City, Country" location line */
.member-loc { font-size: 0.8rem; }

/* Row of action buttons beneath the member details */
.card-actions {
    display: flex;
    gap: 8px;               /* Space between Website and Locate buttons                  */
    margin-top: 4px;
    flex-wrap: wrap;        /* Wraps to a second line on very narrow cards               */
}


/* =============================================
   BADGES — Organisation type tag + action buttons
   ============================================= */

/* Organisation type pill (e.g. "UNIVERSITY", "NGO") */
.badge-org {
    background: var(--brand-sky);   /* Light blue background from brand palette          */
    color: var(--brand-blue);       /* Dark blue text for strong contrast on light bg    */
    font-size: 0.65rem;
    padding: 4px 8px;
    border-radius: 4px;
    font-weight: bold;
    display: inline-block;
    width: fit-content;             /* Only as wide as the text — doesn't stretch to fill
                                       the card width                                     */
    text-transform: uppercase;      /* Forces consistent ALL-CAPS style regardless of how
                                       the type is capitalised in the CSV data            */
}

/* Shared base styles for Website and Locate buttons.
   Both use an outlined style — transparent background with a
   coloured border that fills on hover.                           */
.badge-website,
.badge-locate {
    padding: 4px 12px;
    font-size: 0.7rem;
    font-weight: bold;
    border-radius: 4px;
    border: 1.5px solid;        /* Border colour is set per-button below                 */
    text-decoration: none;      /* Removes underline from the <a> Website link           */
    text-align: center;
    cursor: pointer;
    background: transparent;    /* Outlined style — no fill until hover                  */
    transition: background 0.15s, color 0.15s; /* Smooth fill-in on hover               */
}

/* Website button — red outline, fills red on hover */
.badge-website { color: var(--brand-red); border-color: var(--brand-red); }
.badge-website:hover { background: var(--brand-red); color: white; }

/* Locate button — blue outline, fills blue on hover */
.badge-locate { color: var(--brand-blue); border-color: var(--brand-blue); }
.badge-locate:hover { background: var(--brand-blue); color: white; }


/* =============================================
   MAP PANEL — Right column containing the Leaflet map
   ============================================= */
#map {
    height: 100%;   /* Fills the full height of .map-panel, which fills its grid cell */
    width: 100%;    /* Fills the full width of its container                           */
    z-index: 1;     /* Keeps the map tiles below page overlays like dropdowns or modals.
                       Leaflet internally uses higher z-indexes for its own controls,
                       but this base value anchors it below our UI chrome.             */
}


/* =============================================
   BACK TO TOP BUTTON
   Floats in the bottom-right corner of the side panel.
   Hidden by default — MapCode.js reveals it after the
   user scrolls more than 400px down the member list.
   ============================================= */
#scrollToTopBtn {
    position: absolute;     /* Positioned relative to .side-panel (which has position:relative)
                               so it floats over the list without affecting document flow       */
    bottom: 20px;           /* 20px from the bottom of the panel                              */
    right: 20px;            /* 20px from the right edge of the panel                          */
    display: none;          /* Hidden on page load — JS sets display:block after scrolling    */
    background: var(--brand-blue);
    color: white;
    border: none;
    padding: 10px 15px;
    border-radius: 50px;    /* Fully pill-shaped button                                       */
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); /* Soft drop shadow lifts the button visually
                                                    above the scrolling content beneath it    */
    z-index: 1000;          /* Must be high enough to float above member cards and the
                               Leaflet map's own internal z-index layers                      */
    cursor: pointer;
}


/* =============================================
   NO RESULTS MESSAGE
   Shown inside the scroll-box when all member cards
   are filtered out and nothing matches the search.
   JS toggles display between 'none' and 'block'.
   ============================================= */
#noResultsMsg {
    text-align: center;
    padding: 30px 20px;
    color: #999;
    font-style: italic;
    display: none;          /* Hidden by default — only shown when visibleCount === 0 in runFilters() */
}


/* =============================================
   LOADING STATE — Spinner shown while member
   data is being fetched from the serverless function.
   Injected and removed dynamically by MapCode.js.
   ============================================= */
.map-loader-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;        /* Centres spinner and text horizontally */
    justify-content: center;    /* Centres content vertically within the panel */
    padding: 60px 20px;
    gap: 16px;                  /* Space between spinner circle and loading text */
}

/* Animated spinning circle */
.map-loader-spinner {
    width: 40px;
    height: 40px;
    border: 4px solid var(--brand-sky);         /* Light blue track (the full circle outline) */
    border-top-color: var(--brand-blue);         /* Dark blue segment that spins              */
    border-radius: 50%;                          /* Makes it a perfect circle                 */
    animation: spin 0.8s linear infinite;        /* Continuous rotation                       */
}

/* Keyframe: rotates the spinner element 360° continuously */
@keyframes spin {
    to { transform: rotate(360deg); }
}

/* "Loading members…" text beneath the spinner */
.map-loader-text {
    color: #999;
    font-style: italic;
    font-size: 0.9rem;
    margin: 0;
}


/* =============================================
   ERROR STATE — Shown when the serverless function
   fails to return data. Injected by showErrorMessage()
   in MapCode.js, replacing the member list content.
   ============================================= */
.map-error-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;        /* Centres all error content horizontally */
    justify-content: center;
    padding: 40px 24px;
    gap: 12px;
    text-align: center;
}

/* Large tool emoji that immediately signals something went wrong */
.map-error-icon {
    font-size: 2.5rem;
    line-height: 1;
}

/* "We're currently refreshing the map" heading */
.map-error-title {
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--brand-blue);
    margin: 0;
}

/* Explanatory message beneath the heading */
.map-error-body {
    font-size: 0.875rem;
    color: #666;
    max-width: 280px;           /* Constrains line length for readability in narrow panels */
    margin: 0;
    line-height: 1.5;
}

/* "Try again" retry button */
.map-error-retry {
    margin-top: 8px;
    padding: 8px 24px;
    background: var(--brand-blue);
    color: white;
    border: none;
    border-radius: 50px;        /* Pill shape consistent with other buttons on the page   */
    font-weight: 600;
    font-size: 0.875rem;
    cursor: pointer;
    transition: opacity 0.15s;  /* Subtle fade on hover instead of a colour change        */
}

/* Slightly fades the button on hover to confirm it is interactive */
.map-error-retry:hover {
    opacity: 0.85;
}


/* =============================================
   ACCESSIBILITY — prefers-reduced-motion

   Some users configure their operating system to
   minimise animations (epilepsy, vestibular disorders,
   personal preference). This media query detects that
   setting and disables all CSS transitions and animations.

   WHY is this important?
   WCAG 2.1 Success Criterion 2.3.3 (AAA) and common
   best practice require that motion can be suppressed.
   Ignoring this setting can cause genuine physical
   discomfort for some users.

   transition: none removes smooth hover/toggle effects.
   animation: none stops the loading spinner rotating.
   Both fall back gracefully — UI still works, just
   without movement.
   ============================================= */
@media (prefers-reduced-motion: reduce) {
    .toggle-slider,         /* Sliding blue pill in the Map/List toggle        */
    .toggle-btn,            /* Colour fade on the toggle button text           */
    .member-card,           /* Background highlight fade on hover              */
    .badge-website,         /* Fill animation on hover                         */
    .badge-locate,          /* Fill animation on hover                         */
    .map-error-retry {      /* Opacity fade on hover                           */
        transition: none !important;
        /* Instantly applies state changes instead of animating them.
           !important overrides any inline or component-level transitions. */
    }

    .map-loader-spinner {
        animation: none !important;
        /* Stops the loading spinner rotating for users who find
           spinning elements uncomfortable or distracting.
           The spinner element remains visible as a static circle. */
    }
}