/**
 * SchoolCompare.co.uk - Frontend Application
 * Interactive UK Primary School Performance Comparison
 */

const API_BASE = "";

// =============================================================================
// STATE MANAGEMENT
// =============================================================================

const state = {
  schools: [],
  selectedSchools: [],
  currentSchoolData: null,
  filters: null, // Cached filter data
  metrics: null, // Cached metric definitions
  pagination: { page: 1, pageSize: 50, total: 0, totalPages: 0 },
  isShowingFeatured: true, // Whether showing featured schools vs search results
  searchMode: "name", // "name" or "location"
  locationSearch: {
    active: false,
    postcode: null,
    radius: 5,
    coords: null, // Lat/lng of search location
  },
  resultsView: "list", // "list" or "map"
  loading: {
    schools: false,
    filters: false,
    rankings: false,
    comparison: false,
    modal: false,
  },
};

// Charts
let comparisonChart = null;
let schoolDetailChart = null;
let modalMap = null;
let resultsMapInstance = null;
let resultsMapMarkers = new Map(); // Store markers by school URN

// Chart colors
const CHART_COLORS = [
  "#e07256", // coral
  "#2d7d7d", // teal
  "#c9a227", // gold
  "#7b68a6", // purple
  "#3498db", // blue
  "#27ae60", // green
  "#e74c3c", // red
  "#9b59b6", // violet
];

// Term definitions for tooltips
const TERM_DEFINITIONS = {
  rwm_expected: {
    title: "RWM Expected Standard",
    description:
      "The percentage of pupils meeting the expected standard in Reading, Writing and Maths combined at the end of Key Stage 2 (Year 6).",
    note: "National average: 61%",
  },
  rwm_higher: {
    title: "RWM Higher Standard",
    description:
      "The percentage of pupils exceeding the expected standard and reaching the higher standard in Reading, Writing and Maths combined.",
    note: "National average: 8%",
  },
  gps_expected: {
    title: "GPS Expected Standard",
    description:
      "The percentage of pupils meeting the expected standard in Grammar, Punctuation and Spelling at the end of Key Stage 2.",
    note: "National average: 72%",
  },
  science_expected: {
    title: "Science Expected Standard",
    description:
      "The percentage of pupils meeting the expected standard in Science, assessed by teacher judgement at the end of Key Stage 2.",
    note: "National average: 80%",
  },
  reading_progress: {
    title: "Reading Progress Score",
    description:
      "A value-added measure showing how much progress pupils made in Reading between KS1 and KS2, compared to pupils with similar starting points nationally.",
    note: "A score of 0 is average. Positive = above-average progress.",
  },
  writing_progress: {
    title: "Writing Progress Score",
    description:
      "A value-added measure showing how much progress pupils made in Writing between KS1 and KS2, compared to pupils with similar starting points nationally.",
    note: "A score of 0 is average. Positive = above-average progress.",
  },
  maths_progress: {
    title: "Maths Progress Score",
    description:
      "A value-added measure showing how much progress pupils made in Maths between KS1 and KS2, compared to pupils with similar starting points nationally.",
    note: "A score of 0 is average. Positive = above-average progress.",
  },
  disadvantaged_pct: {
    title: "% Disadvantaged",
    description:
      "The percentage of pupils eligible for free school meals or who have been at any point in the last six years, or are looked-after children.",
    note: "Affects school funding through the Pupil Premium.",
  },
  eal_pct: {
    title: "% EAL",
    description:
      "The percentage of pupils whose first language is known or believed to be other than English. These pupils may need additional language support.",
    note: null,
  },
  sen_support_pct: {
    title: "% SEN Support",
    description:
      "The percentage of pupils receiving Special Educational Needs Support. These pupils need extra help but do not have an Education, Health and Care Plan.",
    note: "Does not include pupils with EHCPs.",
  },
  total_pupils: {
    title: "Total Pupils",
    description: "The total number of pupils enrolled at the school.",
    note: null,
  },
  reading_absence: {
    title: "Reading Test Absence",
    description:
      "The percentage of pupils who were absent from or unable to access the Reading test on test day.",
    note: "Includes pupils who were ill, absent, or had circumstances preventing access.",
  },
  gps_absence: {
    title: "GPS Test Absence",
    description:
      "The percentage of pupils who were absent from or unable to access the Grammar, Punctuation and Spelling test.",
    note: "Includes pupils who were ill, absent, or had circumstances preventing access.",
  },
  maths_absence: {
    title: "Maths Test Absence",
    description:
      "The percentage of pupils who were absent from or unable to access the Maths test on test day.",
    note: "Includes pupils who were ill, absent, or had circumstances preventing access.",
  },
  writing_absence: {
    title: "Writing Absence/Disapplied",
    description:
      "The percentage of pupils who were absent from or disapplied from the Writing teacher assessment.",
    note: "Disapplied means formally removed from assessment, usually due to significant special needs.",
  },
  science_absence: {
    title: "Science Absence/Disapplied",
    description:
      "The percentage of pupils who were absent from or disapplied from the Science teacher assessment.",
    note: "Disapplied means formally removed from assessment, usually due to significant special needs.",
  },
};

// Warning definitions for alerts/notices
const WARNING_DEFINITIONS = {
  progress_scores_unavailable: {
    title: "Progress Scores Unavailable",
    description:
      "The DfE will not publish primary school progress measures for 2023-24 or 2024-25, as KS1 SATs were cancelled in 2020 and 2021.",
  },
};

/**
 * Creates an info trigger button for a term tooltip
 * @param {string} termKey - Key from TERM_DEFINITIONS
 * @returns {string} HTML string for the info trigger
 */
function createInfoTrigger(termKey) {
  const definition = TERM_DEFINITIONS[termKey];
  if (!definition) return "";

  const label = `What is ${definition.title}?`;

  return `<button class="info-trigger" type="button" data-term="${termKey}" aria-label="${label}" aria-expanded="false"><svg class="info-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><circle cx="8" cy="8" r="6.5"/><path d="M8 7v4"/><circle cx="8" cy="5" r="0.5" fill="currentColor" stroke="none"/></svg></button>`;
}

/**
 * Creates a warning trigger button for warning tooltips
 * @param {string} warningKey - Key from WARNING_DEFINITIONS
 * @returns {string} HTML string for the warning trigger
 */
function createWarningTrigger(warningKey) {
  const definition = WARNING_DEFINITIONS[warningKey];
  if (!definition) return "";

  const label = definition.title;

  return `<button class="warning-trigger" type="button" data-warning="${warningKey}" aria-label="${label}" aria-expanded="false"><svg class="warning-icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><path d="M8 1.5L14.5 13H1.5L8 1.5z"/><path d="M8 6v3"/><circle cx="8" cy="11" r="0.5" fill="currentColor" stroke="none"/></svg></button>`;
}

// Map instances (stored to allow cleanup)
const schoolMaps = new Map();

// Helper to get chart aspect ratio based on screen size
function getChartAspectRatio() {
  if (window.innerWidth <= 480) return 0.9; // Very small screens - taller chart
  if (window.innerWidth <= 768) return 1.1; // Mobile - taller chart
  return 2;
}

// Helper to check if we're on mobile
function isMobile() {
  return window.innerWidth <= 768;
}

// =============================================================================
// DOM ELEMENTS
// =============================================================================

const elements = {
  // Search mode toggle
  searchModeToggle: document.querySelector(".search-mode-toggle"),
  searchModeBtns: document.querySelectorAll(".search-mode-btn"),
  nameSearchPanel: document.getElementById("name-search-panel"),
  locationSearchPanel: document.getElementById("location-search-panel"),
  // Name search
  schoolSearch: document.getElementById("school-search"),
  localAuthorityFilter: document.getElementById("local-authority-filter"),
  typeFilter: document.getElementById("type-filter"),
  // Location search
  postcodeSearch: document.getElementById("postcode-search"),
  radiusSelect: document.getElementById("radius-select"),
  locationSearchBtn: document.getElementById("location-search-btn"),
  typeFilterLocation: document.getElementById("type-filter-location"),
  // Results view
  viewToggle: document.getElementById("view-toggle"),
  viewToggleBtns: document.querySelectorAll(".view-toggle-btn"),
  resultsContainer: document.getElementById("results-container"),
  resultsMap: document.getElementById("results-map"),
  // Schools grid
  schoolsGrid: document.getElementById("schools-grid"),
  compareSearch: document.getElementById("compare-search"),
  compareResults: document.getElementById("compare-results"),
  selectedSchools: document.getElementById("selected-schools"),
  chartsSection: document.getElementById("charts-section"),
  metricSelect: document.getElementById("metric-select"),
  comparisonChart: document.getElementById("comparison-chart"),
  comparisonTable: document.getElementById("comparison-table"),
  tableHeader: document.getElementById("table-header"),
  tableBody: document.getElementById("table-body"),
  rankingArea: document.getElementById("ranking-area"),
  rankingMetric: document.getElementById("ranking-metric"),
  rankingYear: document.getElementById("ranking-year"),
  rankingsList: document.getElementById("rankings-list"),
  modal: document.getElementById("school-modal"),
  modalClose: document.getElementById("modal-close"),
  modalSchoolName: document.getElementById("modal-school-name"),
  modalMeta: document.getElementById("modal-meta"),
  modalDetails: document.getElementById("modal-details"),
  modalStats: document.getElementById("modal-stats"),
  modalMapContainer: document.getElementById("modal-map-container"),
  modalMap: document.getElementById("modal-map"),
  schoolDetailChart: document.getElementById("school-detail-chart"),
  addToCompare: document.getElementById("add-to-compare"),
};

// =============================================================================
// API & CACHING
// =============================================================================

const apiCache = new Map();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

async function fetchAPI(endpoint, options = {}) {
  const { useCache = true, showLoading = null } = options;
  const cacheKey = endpoint;

  // Check cache
  if (useCache && apiCache.has(cacheKey)) {
    const cached = apiCache.get(cacheKey);
    if (Date.now() - cached.timestamp < CACHE_TTL) {
      return cached.data;
    }
    apiCache.delete(cacheKey);
  }

  // Set loading state
  if (showLoading) {
    state.loading[showLoading] = true;
    updateLoadingUI(showLoading);
  }

  try {
    const response = await fetch(`${API_BASE}${endpoint}`);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    const data = await response.json();

    // Cache the response
    if (useCache) {
      apiCache.set(cacheKey, { data, timestamp: Date.now() });
    }

    return data;
  } catch (error) {
    console.error(`API Error (${endpoint}):`, error);
    return null;
  } finally {
    if (showLoading) {
      state.loading[showLoading] = false;
      updateLoadingUI(showLoading);
    }
  }
}

function updateLoadingUI(section) {
  // Update UI based on loading state
  switch (section) {
    case "schools":
      if (state.loading.schools) {
        elements.schoolsGrid.innerHTML = renderLoadingSkeleton(6);
      }
      break;
    case "rankings":
      if (state.loading.rankings) {
        elements.rankingsList.innerHTML = renderLoadingSkeleton(5, "ranking");
      }
      break;
    case "modal":
      if (state.loading.modal) {
        elements.modalStats.innerHTML =
          '<div class="loading"><div class="loading-spinner"></div><p>Loading school data...</p></div>';
      }
      break;
  }
}

function renderLoadingSkeleton(count, type = "card") {
  if (type === "ranking") {
    return Array(count)
      .fill(0)
      .map(
        () => `
            <div class="ranking-item skeleton">
                <div class="skeleton-circle"></div>
                <div class="skeleton-content">
                    <div class="skeleton-line" style="width: 60%"></div>
                    <div class="skeleton-line short" style="width: 30%"></div>
                </div>
                <div class="skeleton-score"></div>
            </div>
        `,
      )
      .join("");
  }
  return Array(count)
    .fill(0)
    .map(
      () => `
        <div class="school-card skeleton">
            <div class="skeleton-line" style="width: 70%"></div>
            <div class="skeleton-line short" style="width: 40%"></div>
            <div class="skeleton-line" style="width: 90%"></div>
        </div>
    `,
    )
    .join("");
}

// =============================================================================
// ROUTING
// =============================================================================

const routes = {
  "/": "home",
  "/compare": "compare",
  "/rankings": "rankings",
};

const pageTitles = {
  home: "SchoolCompare | Compare Primary School Performance",
  compare: "Compare Schools | SchoolCompare",
  rankings: "School Rankings | SchoolCompare",
};

function navigateTo(path) {
  // Update URL without reload
  window.history.pushState({}, "", path);
  handleRoute();
}

function handleRoute() {
  let path = window.location.pathname;

  // Normalize path - treat /index.html, empty, or just "/" as home
  if (path === "" || path === "/index.html" || path.endsWith("/index.html")) {
    path = "/";
  }

  const view = routes[path] || "home";

  // Update page title for SEO
  document.title = pageTitles[view] || pageTitles.home;

  // Update navigation
  document.querySelectorAll(".nav-link").forEach((link) => {
    link.classList.toggle("active", link.dataset.view === view);
  });

  // Update view
  document
    .querySelectorAll(".view")
    .forEach((v) => v.classList.remove("active"));
  const viewElement = document.getElementById(`${view}-view`);
  if (viewElement) {
    viewElement.classList.add("active");
  }
}

// =============================================================================
// INITIALIZATION
// =============================================================================

document.addEventListener("DOMContentLoaded", init);

async function init() {
  // Load selected schools from localStorage first
  loadSelectedSchoolsFromStorage();

  try {
    // Load filters and metrics in parallel (single request for filters)
    const [filtersData, metricsData] = await Promise.all([
      fetchAPI("/api/filters", { showLoading: "filters" }),
      fetchAPI("/api/metrics"),
    ]);

    // Cache and apply filters
    if (filtersData) {
      state.filters = filtersData;
      populateFilters(filtersData);
    }

    // Cache metrics
    if (metricsData) {
      state.metrics = metricsData.metrics;
    }

    // Load initial data
    await loadSchools();
    await loadRankings();
  } catch (err) {
    console.error("Error during initialization:", err);
  }

  // Always set up event listeners and routing, even if data loading fails
  setupEventListeners();

  // Initialize tooltip manager
  tooltipManager = new TooltipManager();

  // Render any previously selected schools
  renderSelectedSchools();

  // Handle initial route
  handleRoute();

  // Handle browser back/forward
  window.addEventListener("popstate", handleRoute);
}

function populateFilters(data) {
  // Populate local authority filter (home page)
  data.local_authorities.forEach((la) => {
    const option = document.createElement("option");
    option.value = la;
    option.textContent = la;
    elements.localAuthorityFilter.appendChild(option);
  });

  // Populate school type filter (both name and location panels)
  data.school_types.forEach((type) => {
    const option = document.createElement("option");
    option.value = type;
    option.textContent = type;
    elements.typeFilter.appendChild(option);

    // Also add to location panel's type filter
    const optionLocation = document.createElement("option");
    optionLocation.value = type;
    optionLocation.textContent = type;
    elements.typeFilterLocation.appendChild(optionLocation);
  });

  // Populate ranking area dropdown
  data.local_authorities.forEach((la) => {
    const option = document.createElement("option");
    option.value = la;
    option.textContent = la;
    elements.rankingArea.appendChild(option);
  });

  // Populate ranking year dropdown
  elements.rankingYear.innerHTML = "";
  data.years
    .sort((a, b) => b - a)
    .forEach((year) => {
      const option = document.createElement("option");
      option.value = year;
      option.textContent = `${year}`;
      elements.rankingYear.appendChild(option);
    });
}

// =============================================================================
// DATA LOADING
// =============================================================================

async function loadSchools() {
  const params = new URLSearchParams();

  if (state.searchMode === "name") {
    // Name search mode
    const search = elements.schoolSearch.value.trim();
    const localAuthority = elements.localAuthorityFilter.value;
    const type = elements.typeFilter.value;

    // If no search query (or less than 2 chars) and no filters, show featured schools
    if (search.length < 2 && !localAuthority && !type) {
      await loadFeaturedSchools();
      return;
    }

    if (search.length >= 2) params.append("search", search);
    if (localAuthority) params.append("local_authority", localAuthority);
    if (type) params.append("school_type", type);
  } else {
    // Location search mode
    const { active: locationActive, postcode, radius } = state.locationSearch;
    const type = elements.typeFilterLocation.value;

    // If no location search active, show featured schools
    if (!locationActive) {
      await loadFeaturedSchools();
      return;
    }

    params.append("postcode", postcode);
    params.append("radius", radius);
    if (type) params.append("school_type", type);
  }

  params.append("page", state.pagination.page);
  params.append("page_size", state.pagination.pageSize);

  const queryString = params.toString();
  const endpoint = `/api/schools?${queryString}`;

  // Don't cache search results (they change based on input)
  const data = await fetchAPI(endpoint, {
    useCache: false,
    showLoading: "schools",
  });

  if (!data) {
    showEmptyState(elements.schoolsGrid, "Unable to load schools");
    return;
  }

  state.schools = data.schools;
  state.pagination.total = data.total;
  state.pagination.totalPages = data.total_pages;
  state.isShowingFeatured = false;

  // Store search coordinates if available
  if (data.search_location && data.search_location.lat && data.search_location.lng) {
    state.locationSearch.coords = {
      lat: data.search_location.lat,
      lng: data.search_location.lng,
    };
  }

  // Show location info banner if location search is active
  updateLocationInfoBanner(data.search_location);

  // Render appropriate view based on current state
  if (state.resultsView === "map" && state.locationSearch.active) {
    renderCompactSchoolList(state.schools);
    initializeResultsMap(state.schools);
  } else {
    renderSchools(state.schools);
  }

  // Update view toggle visibility
  updateViewToggle();
}

async function loadFeaturedSchools() {
  // Clear location info when showing featured
  updateLocationInfoBanner(null);

  // Load a sample of schools and pick 3 random ones
  const data = await fetchAPI("/api/schools?page_size=100", {
    showLoading: "schools",
  });

  if (!data || !data.schools.length) {
    showEmptyState(elements.schoolsGrid, "No schools available");
    return;
  }

  // Shuffle and pick 3 random schools
  const shuffled = data.schools.sort(() => Math.random() - 0.5);
  state.schools = shuffled.slice(0, 3);
  state.isShowingFeatured = true;

  renderFeaturedSchools(state.schools);

  // Hide view toggle for featured schools
  updateViewToggle();
}

function updateLocationInfoBanner(searchLocation) {
  // Remove existing banner if any
  const existingBanner = document.querySelector(".location-info");
  if (existingBanner) {
    existingBanner.remove();
  }

  if (!searchLocation) {
    return;
  }

  // Create location info banner
  const banner = document.createElement("div");
  banner.className = "location-info";
  banner.innerHTML = `
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
            <circle cx="12" cy="10" r="3"/>
        </svg>
        <span>Showing schools within ${searchLocation.radius} miles of <strong>${searchLocation.postcode.toUpperCase()}</strong></span>
    `;

  // Insert banner before the results container (above map view)
  elements.resultsContainer.parentNode.insertBefore(banner, elements.resultsContainer);
}

async function searchByLocation() {
  const postcode = elements.postcodeSearch.value.trim();
  const radius = parseFloat(elements.radiusSelect.value);

  if (!postcode) {
    alert("Please enter a postcode");
    return;
  }

  // Validate UK postcode format (basic check)
  const postcodeRegex = /^[A-Z]{1,2}[0-9][A-Z0-9]?\s*[0-9][A-Z]{2}$/i;
  if (!postcodeRegex.test(postcode)) {
    alert("Please enter a valid UK postcode (e.g. SW18 4TF)");
    return;
  }

  // Update state
  state.locationSearch = {
    active: true,
    postcode: postcode,
    radius: radius,
  };
  state.pagination.page = 1;

  // Load schools with location filter
  await loadSchools();
}

function renderFeaturedSchools(schools) {
  elements.schoolsGrid.innerHTML = `
        <div class="featured-header">
            <h3>Featured Schools</h3>
            <p>Start typing to search schools across England</p>
        </div>
        ${schools
          .map((school) => {
            // Religious denomination tag (only show if meaningful)
            const faithTag = school.religious_denomination &&
              !["None", "Does not apply", ""].includes(school.religious_denomination)
                ? `<span class="school-tag faith">${escapeHtml(school.religious_denomination)}</span>`
                : "";

            // Age range display
            const ageRange = school.age_range
              ? `<span class="age-range">Ages ${escapeHtml(school.age_range)}</span>`
              : "";

            // Map container (only if coordinates available)
            const hasCoords = school.latitude && school.longitude;
            const mapContainer = hasCoords
              ? `<div class="school-map" data-lat="${school.latitude}" data-lng="${school.longitude}" data-name="${escapeHtml(school.school_name)}"></div>`
              : "";

            return `
            <div class="school-card featured" data-urn="${school.urn}">
                <h3 class="school-name">${escapeHtml(school.school_name)}</h3>
                <div class="school-meta">
                    <span class="school-tag">${escapeHtml(school.local_authority || "")}</span>
                    <span class="school-tag type">${escapeHtml(school.school_type || "")}</span>
                    ${faithTag}
                </div>
                <div class="school-address">${escapeHtml(school.address || "")}</div>
                ${ageRange ? `<div class="school-details">${ageRange}</div>` : ""}
                <div class="school-stats">
                    <div class="stat">
                        <div class="stat-value">
                            ${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
                            ${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)}
                        </div>
                        <div class="stat-label"><span class="stat-label-with-info">RWM Expected${createInfoTrigger("rwm_expected")}</span></div>
                    </div>
                    <div class="stat">
                        <div class="stat-value">${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}</div>
                        <div class="stat-label"><span class="stat-label-with-info">RWM Higher${createInfoTrigger("rwm_higher")}</span></div>
                    </div>
                    <div class="stat">
                        <div class="stat-value">${school.total_pupils || "-"}</div>
                        <div class="stat-label"><span class="stat-label-with-info">Pupils${createInfoTrigger("total_pupils")}</span></div>
                    </div>
                </div>
                ${mapContainer}
            </div>
        `;
          })
          .join("")}
    `;

  // Initialize maps
  initializeSchoolMaps(elements.schoolsGrid);

  // Add click handlers
  elements.schoolsGrid.querySelectorAll(".school-card").forEach((card) => {
    card.addEventListener("click", (e) => {
      // Don't trigger if clicking on map or info trigger
      if (e.target.closest(".school-map") || e.target.closest(".info-trigger")) return;
      const urn = parseInt(card.dataset.urn);
      openSchoolModal(urn);
    });
  });
}

async function loadSchoolDetails(urn) {
  const data = await fetchAPI(`/api/schools/${urn}`, { showLoading: "modal" });
  return data;
}

async function loadComparison() {
  if (state.selectedSchools.length === 0) return null;

  const urns = state.selectedSchools.map((s) => s.urn).join(",");
  const data = await fetchAPI(`/api/compare?urns=${urns}`, {
    useCache: false,
    showLoading: "comparison",
  });
  return data;
}

async function loadRankings() {
  const area = elements.rankingArea.value;
  const metric = elements.rankingMetric.value;
  const year = elements.rankingYear.value;

  let endpoint = `/api/rankings?metric=${metric}&limit=20`;
  if (year) endpoint += `&year=${year}`;
  if (area) endpoint += `&local_authority=${encodeURIComponent(area)}`;

  const data = await fetchAPI(endpoint, {
    useCache: false,
    showLoading: "rankings",
  });

  if (!data) {
    showEmptyState(elements.rankingsList, "Unable to load rankings");
    return;
  }

  renderRankings(data.rankings, metric);
}

// =============================================================================
// METRIC HELPERS
// =============================================================================

function getMetricLabel(key, short = false) {
  if (state.metrics) {
    const metric = state.metrics.find((m) => m.key === key);
    if (metric) {
      return short ? metric.short_name : metric.name;
    }
  }
  // Fallback labels
  return key.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
}

function formatMetricValue(value, metric) {
  if (value === null || value === undefined) return "-";

  const metricDef = state.metrics?.find((m) => m.key === metric);
  const type =
    metricDef?.type || (metric.includes("pct") ? "percentage" : "score");

  if (metric.includes("progress")) {
    return (value >= 0 ? "+" : "") + value.toFixed(1);
  }
  if (type === "percentage" || metric.includes("pct")) {
    return value.toFixed(0) + "%";
  }
  return value.toFixed(1);
}

/**
 * Calculate trend indicator based on current and previous year values
 * Returns HTML for trend arrow with class
 */
function getTrendIndicator(current, previous) {
  if (current === null || current === undefined ||
      previous === null || previous === undefined) {
    return "";
  }

  const diff = current - previous;
  const threshold = 2; // Minimum % change to show trend

  if (diff >= threshold) {
    return `<span class="trend-indicator trend-up" title="Up ${diff.toFixed(0)}% from last year">&#9650;</span>`;
  } else if (diff <= -threshold) {
    return `<span class="trend-indicator trend-down" title="Down ${Math.abs(diff).toFixed(0)}% from last year">&#9660;</span>`;
  } else {
    return `<span class="trend-indicator trend-stable" title="Stable (${diff >= 0 ? '+' : ''}${diff.toFixed(0)}%)">&#9644;</span>`;
  }
}

// =============================================================================
// MAP FUNCTIONS
// =============================================================================

/**
 * Initialize Leaflet maps for all school cards in a container
 */
function initializeSchoolMaps(container) {
  // Check if Leaflet is loaded
  if (typeof L === "undefined") {
    console.warn("Leaflet not loaded yet, skipping map initialization");
    return;
  }

  const mapElements = container.querySelectorAll(".school-map");
  if (mapElements.length === 0) return;

  // Clean up existing maps first
  mapElements.forEach((mapEl) => {
    const existingMap = schoolMaps.get(mapEl);
    if (existingMap) {
      try {
        existingMap.remove();
      } catch (e) {
        // Ignore cleanup errors
      }
      schoolMaps.delete(mapEl);
    }
  });

  // Initialize new maps with a small delay to ensure DOM is ready
  setTimeout(() => {
    mapElements.forEach((mapEl) => {
      try {
        // Skip if already initialized
        if (schoolMaps.has(mapEl)) return;

        const lat = parseFloat(mapEl.dataset.lat);
        const lng = parseFloat(mapEl.dataset.lng);
        const schoolName = mapEl.dataset.name;

        if (isNaN(lat) || isNaN(lng)) return;

        // Ensure element has dimensions
        if (mapEl.offsetWidth === 0 || mapEl.offsetHeight === 0) {
          console.warn("Map container has no dimensions, skipping");
          return;
        }

        // Create map
        const map = L.map(mapEl, {
          center: [lat, lng],
          zoom: 15,
          zoomControl: false,
          attributionControl: false,
          dragging: true,
          scrollWheelZoom: false,
        });

        // Add tile layer (OpenStreetMap)
        L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
          maxZoom: 19,
        }).addTo(map);

        // Add marker
        const marker = L.marker([lat, lng]).addTo(map);
        marker.bindTooltip(schoolName, { permanent: false, direction: "top" });

        // Store map reference
        schoolMaps.set(mapEl, map);

        // Handle click to open fullscreen
        mapEl.addEventListener("click", (e) => {
          e.stopPropagation();
          openMapModal(lat, lng, schoolName);
        });
      } catch (err) {
        console.error("Error initializing map:", err);
      }
    });
  }, 100);
}

/**
 * Open fullscreen map modal
 */
function openMapModal(lat, lng, schoolName) {
  // Create modal overlay
  const overlay = document.createElement("div");
  overlay.className = "map-modal-overlay";
  overlay.innerHTML = `
    <div class="map-modal">
      <div class="map-modal-header">
        <h3>${escapeHtml(schoolName)}</h3>
        <button class="map-modal-close" aria-label="Close map">&times;</button>
      </div>
      <div class="map-modal-content" id="fullscreen-map"></div>
    </div>
  `;

  document.body.appendChild(overlay);
  document.body.style.overflow = "hidden";

  // Initialize fullscreen map
  const mapContainer = document.getElementById("fullscreen-map");
  const fullMap = L.map(mapContainer, {
    center: [lat, lng],
    zoom: 16,
  });

  L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
    maxZoom: 19,
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
  }).addTo(fullMap);

  const marker = L.marker([lat, lng]).addTo(fullMap);
  marker.bindPopup(`<strong>${escapeHtml(schoolName)}</strong>`).openPopup();

  // Close handlers
  const closeModal = () => {
    fullMap.remove();
    overlay.remove();
    document.body.style.overflow = "";
  };

  overlay.querySelector(".map-modal-close").addEventListener("click", closeModal);
  overlay.addEventListener("click", (e) => {
    if (e.target === overlay) closeModal();
  });

  // Close on Escape key
  const escHandler = (e) => {
    if (e.key === "Escape") {
      closeModal();
      document.removeEventListener("keydown", escHandler);
    }
  };
  document.addEventListener("keydown", escHandler);
}

/**
 * Initialize the results map for location search
 */
function initializeResultsMap(schools) {
  // Check if Leaflet is loaded
  if (typeof L === "undefined") {
    console.warn("Leaflet not loaded, skipping results map initialization");
    return;
  }

  // Destroy existing map if any
  if (resultsMapInstance) {
    try {
      resultsMapInstance.remove();
    } catch (e) {
      // Ignore cleanup errors
    }
    resultsMapInstance = null;
  }

  // Need search coords to center the map
  if (!state.locationSearch.coords) {
    console.warn("No search coordinates available for results map");
    return;
  }

  const { lat, lng } = state.locationSearch.coords;

  // Ensure container has dimensions
  setTimeout(() => {
    const mapContainer = elements.resultsMap;
    if (!mapContainer || mapContainer.offsetWidth === 0 || mapContainer.offsetHeight === 0) {
      console.warn("Results map container has no dimensions");
      return;
    }

    // Create map centered on search location
    resultsMapInstance = L.map(mapContainer, {
      center: [lat, lng],
      zoom: 14,
      scrollWheelZoom: true,
    });

    // Add tile layer
    L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
      maxZoom: 19,
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
    }).addTo(resultsMapInstance);

    // Create custom icon for search location (blue)
    const searchIcon = L.divIcon({
      className: "search-location-marker",
      html: `<svg viewBox="0 0 24 24" fill="#3498db" stroke="#2980b9" stroke-width="1" width="32" height="32">
        <circle cx="12" cy="12" r="8"/>
      </svg>`,
      iconSize: [32, 32],
      iconAnchor: [16, 16],
    });

    // Add search location marker
    L.marker([lat, lng], { icon: searchIcon })
      .addTo(resultsMapInstance)
      .bindPopup(`<strong>Search location</strong><br>${state.locationSearch.postcode}`);

    // Clear existing markers
    resultsMapMarkers.clear();

    // Add school markers
    const bounds = L.latLngBounds([[lat, lng]]);
    schools.forEach((school) => {
      if (school.latitude && school.longitude) {
        const marker = L.marker([school.latitude, school.longitude])
          .addTo(resultsMapInstance)
          .bindPopup(`
            <strong>${escapeHtml(school.school_name)}</strong><br>
            ${school.distance !== undefined ? school.distance.toFixed(1) + " miles away" : ""}
          `);

        // Store marker reference
        resultsMapMarkers.set(school.urn, {
          marker,
          lat: school.latitude,
          lng: school.longitude,
        });

        // Click handler to highlight card
        marker.on("click", () => {
          highlightSchoolCard(school.urn, false); // Don't center map, already at marker
        });

        bounds.extend([school.latitude, school.longitude]);
      }
    });

    // Fit bounds to show all markers with padding
    if (schools.length > 0) {
      resultsMapInstance.fitBounds(bounds, { padding: [30, 30] });
    }
  }, 100);
}

/**
 * Highlight a school card and scroll it into view
 * @param {number} urn - School URN
 * @param {boolean} centerMap - Whether to center the map on the school (default: true)
 */
function highlightSchoolCard(urn, centerMap = true) {
  // Remove highlight from all cards and compact items
  document.querySelectorAll(".school-card, .school-list-item").forEach((card) => {
    card.classList.remove("highlighted");
  });

  // Add highlight to selected card/item
  const card = document.querySelector(`.school-card[data-urn="${urn}"], .school-list-item[data-urn="${urn}"]`);
  if (card) {
    card.classList.add("highlighted");
    card.scrollIntoView({ behavior: "smooth", block: "center" });
  }

  // Center map on the school and open popup
  if (centerMap && resultsMapInstance && resultsMapMarkers.has(urn)) {
    const { marker, lat, lng } = resultsMapMarkers.get(urn);
    resultsMapInstance.setView([lat, lng], 15, { animate: true });
    marker.openPopup();
  }
}

/**
 * Destroy the results map instance
 */
function destroyResultsMap() {
  if (resultsMapInstance) {
    try {
      resultsMapInstance.remove();
    } catch (e) {
      // Ignore cleanup errors
    }
    resultsMapInstance = null;
  }
  resultsMapMarkers.clear();
}

/**
 * Update the view toggle visibility and state
 */
function updateViewToggle() {
  // Only show toggle for location search results
  if (state.locationSearch.active && state.schools.length > 0) {
    elements.viewToggle.style.display = "flex";
  } else {
    elements.viewToggle.style.display = "none";
    // Reset to list view when hiding toggle
    if (state.resultsView === "map") {
      setResultsView("list");
    }
  }
}

/**
 * Set the results view mode (list or map)
 */
function setResultsView(view) {
  state.resultsView = view;

  // Update toggle button states
  elements.viewToggleBtns.forEach((btn) => {
    btn.classList.toggle("active", btn.dataset.view === view);
  });

  // Update container class and render appropriate view
  if (view === "map") {
    elements.resultsContainer.classList.add("map-view");
    // Render compact list for map view
    if (state.schools.length > 0) {
      renderCompactSchoolList(state.schools);
    }
    // Initialize map if location search is active
    if (state.locationSearch.active) {
      initializeResultsMap(state.schools);
    }
  } else {
    elements.resultsContainer.classList.remove("map-view");
    destroyResultsMap();
    // Re-render full cards for list view
    if (state.schools.length > 0 && state.locationSearch.active) {
      renderSchools(state.schools);
    }
  }
}

/**
 * Render compact school list items for map view
 */
function renderCompactSchoolList(schools) {
  const html = schools
    .map((school) => {
      const distanceBadge =
        school.distance !== undefined && school.distance !== null
          ? `<span class="distance-badge">${school.distance.toFixed(1)} mi</span>`
          : "";

      const isInCompare = state.selectedSchools.some((s) => s.urn === school.urn);
      const compareButtonText = isInCompare ? "Remove" : "Compare";
      const compareButtonClass = isInCompare ? "btn-compare active" : "btn-compare";

      return `
        <div class="school-list-item" data-urn="${school.urn}">
          <div class="school-list-item-content">
            <div class="school-list-item-header">
              <h4 class="school-list-item-name">${escapeHtml(school.school_name)}</h4>
              ${distanceBadge}
            </div>
            <div class="school-list-item-meta">
              <span>${escapeHtml(school.school_type || "")}</span>
              <span>${escapeHtml(school.local_authority || "")}</span>
            </div>
            <div class="school-list-item-stats">
              <span class="school-list-item-stat">
                <strong>${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}</strong> RWM
              </span>
              <span class="school-list-item-stat">
                <strong>${school.total_pupils || "-"}</strong> pupils
              </span>
            </div>
          </div>
          <div class="school-list-item-actions">
            <button class="btn ${compareButtonClass}" data-urn="${school.urn}">${compareButtonText}</button>
            <button class="btn btn-secondary school-list-item-details" data-urn="${school.urn}">Details</button>
          </div>
        </div>
      `;
    })
    .join("");

  elements.schoolsGrid.innerHTML = html;

  // Add click handlers for list items (to highlight on map)
  elements.schoolsGrid.querySelectorAll(".school-list-item").forEach((item) => {
    item.addEventListener("click", (e) => {
      // Don't trigger if clicking buttons
      if (e.target.closest(".school-list-item-actions")) return;
      const urn = parseInt(item.dataset.urn, 10);
      highlightSchoolCard(urn, true);
    });
  });

  // Add click handlers for details buttons
  elements.schoolsGrid.querySelectorAll(".school-list-item-details").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      e.stopPropagation();
      const urn = parseInt(btn.dataset.urn, 10);
      openSchoolModal(urn);
    });
  });

  // Add click handlers for compare buttons
  elements.schoolsGrid.querySelectorAll(".btn-compare").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      e.stopPropagation();
      const urn = parseInt(btn.dataset.urn, 10);
      const school = schools.find((s) => s.urn === urn);
      if (!school) return;

      const isInCompare = state.selectedSchools.some((s) => s.urn === urn);
      if (isInCompare) {
        removeFromComparison(urn);
        btn.textContent = "Compare";
        btn.classList.remove("active");
      } else {
        addToComparison(school);
        btn.textContent = "Remove";
        btn.classList.add("active");
      }
    });
  });
}

// =============================================================================
// RENDER FUNCTIONS
// =============================================================================

function renderSchools(schools) {
  if (schools.length === 0) {
    const message = state.locationSearch.active
      ? `No schools found within ${state.locationSearch.radius} miles of ${state.locationSearch.postcode}`
      : "No primary schools found matching your criteria";
    showEmptyState(elements.schoolsGrid, message);
    return;
  }

  let html = schools
    .map((school) => {
      const distanceBadge =
        school.distance !== undefined && school.distance !== null
          ? `<span class="distance-badge">${school.distance.toFixed(1)} mi</span>`
          : "";

      // Religious denomination tag (only show if meaningful)
      const faithTag = school.religious_denomination &&
        !["None", "Does not apply", ""].includes(school.religious_denomination)
          ? `<span class="school-tag faith">${escapeHtml(school.religious_denomination)}</span>`
          : "";

      // Age range display
      const ageRange = school.age_range
        ? `<span class="age-range">Ages ${escapeHtml(school.age_range)}</span>`
        : "";

      // Map container (only if coordinates available)
      const hasCoords = school.latitude && school.longitude;
      const mapContainer = hasCoords
        ? `<div class="school-map" data-lat="${school.latitude}" data-lng="${school.longitude}" data-name="${escapeHtml(school.school_name)}"></div>`
        : "";

      return `
            <div class="school-card" data-urn="${school.urn}">
                <h3 class="school-name">${escapeHtml(school.school_name)}${distanceBadge}</h3>
                <div class="school-meta">
                    <span class="school-tag">${escapeHtml(school.local_authority || "")}</span>
                    <span class="school-tag type">${escapeHtml(school.school_type || "")}</span>
                    ${faithTag}
                </div>
                <div class="school-address">${escapeHtml(school.address || "")}</div>
                ${ageRange ? `<div class="school-details">${ageRange}</div>` : ""}
                <div class="school-stats">
                    <div class="stat">
                        <div class="stat-value">
                            ${formatMetricValue(school.rwm_expected_pct, "rwm_expected_pct")}
                            ${getTrendIndicator(school.rwm_expected_pct, school.prev_rwm_expected_pct)}
                        </div>
                        <div class="stat-label"><span class="stat-label-with-info">RWM Expected${createInfoTrigger("rwm_expected")}</span></div>
                    </div>
                    <div class="stat">
                        <div class="stat-value">${formatMetricValue(school.rwm_high_pct, "rwm_high_pct")}</div>
                        <div class="stat-label"><span class="stat-label-with-info">RWM Higher${createInfoTrigger("rwm_higher")}</span></div>
                    </div>
                    <div class="stat">
                        <div class="stat-value">${school.total_pupils || "-"}</div>
                        <div class="stat-label"><span class="stat-label-with-info">Pupils${createInfoTrigger("total_pupils")}</span></div>
                    </div>
                </div>
                ${mapContainer}
            </div>
        `;
    })
    .join("");

  // Add pagination info
  if (state.pagination.totalPages > 1) {
    html += `
            <div class="pagination-info">
                <span>Showing ${schools.length} of ${state.pagination.total} schools</span>
                ${
                  state.pagination.page < state.pagination.totalPages
                    ? `<button class="btn-load-more" onclick="loadMoreSchools()">Load More</button>`
                    : ""
                }
            </div>
        `;
  }

  elements.schoolsGrid.innerHTML = html;

  // Initialize maps
  initializeSchoolMaps(elements.schoolsGrid);

  // Add click handlers
  elements.schoolsGrid.querySelectorAll(".school-card").forEach((card) => {
    card.addEventListener("click", (e) => {
      // Don't trigger if clicking on map or info trigger
      if (e.target.closest(".school-map") || e.target.closest(".info-trigger")) return;
      const urn = parseInt(card.dataset.urn);
      openSchoolModal(urn);
    });
  });
}

async function loadMoreSchools() {
  state.pagination.page++;
  const params = new URLSearchParams();

  if (state.searchMode === "name") {
    const search = elements.schoolSearch.value.trim();
    if (search) params.append("search", search);

    const localAuthority = elements.localAuthorityFilter.value;
    if (localAuthority) params.append("local_authority", localAuthority);

    const type = elements.typeFilter.value;
    if (type) params.append("school_type", type);
  } else {
    const { postcode, radius } = state.locationSearch;
    if (postcode) {
      params.append("postcode", postcode);
      params.append("radius", radius);
    }

    const type = elements.typeFilterLocation.value;
    if (type) params.append("school_type", type);
  }

  params.append("page", state.pagination.page);
  params.append("page_size", state.pagination.pageSize);

  const data = await fetchAPI(`/api/schools?${params.toString()}`, {
    useCache: false,
  });

  if (data && data.schools.length > 0) {
    state.schools = [...state.schools, ...data.schools];
    renderSchools(state.schools);
  }
}

function renderRankings(rankings, metric) {
  if (rankings.length === 0) {
    showEmptyState(
      elements.rankingsList,
      "No ranking data available for this year/metric",
    );
    return;
  }

  elements.rankingsList.innerHTML = rankings
    .map((school, index) => {
      const value = school[metric];
      if (value === null || value === undefined) return "";

      return `
            <div class="ranking-item" data-urn="${school.urn}">
                <div class="ranking-position ${index < 3 ? "top-3" : ""}">${index + 1}</div>
                <div class="ranking-info">
                    <div class="ranking-name">${escapeHtml(school.school_name)}</div>
                    <div class="ranking-location">${escapeHtml(school.local_authority || "")}</div>
                </div>
                <div class="ranking-score">
                    <div class="ranking-score-value">${formatMetricValue(value, metric)}</div>
                    <div class="ranking-score-label">${getMetricLabel(metric, true)}</div>
                </div>
            </div>
        `;
    })
    .filter(Boolean)
    .join("");

  // Add click handlers
  elements.rankingsList.querySelectorAll(".ranking-item").forEach((item) => {
    item.addEventListener("click", () => {
      const urn = parseInt(item.dataset.urn);
      openSchoolModal(urn);
    });
  });
}

function renderSelectedSchools() {
  if (state.selectedSchools.length === 0) {
    elements.selectedSchools.innerHTML = `
            <div class="empty-selection">
                <div class="empty-icon">
                    <svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.5">
                        <rect x="6" y="10" width="36" height="28" rx="2"/>
                        <path d="M6 18h36"/>
                        <circle cx="14" cy="14" r="2" fill="currentColor"/>
                        <circle cx="22" cy="14" r="2" fill="currentColor"/>
                    </svg>
                </div>
                <p>Search and add schools to compare</p>
            </div>
        `;
    elements.chartsSection.style.display = "none";
    return;
  }

  elements.selectedSchools.innerHTML = state.selectedSchools
    .map(
      (school, index) => `
        <div class="selected-school-tag" style="border-left: 3px solid ${CHART_COLORS[index % CHART_COLORS.length]}">
            <span>${escapeHtml(school.school_name)}</span>
            <button class="remove" data-urn="${school.urn}" title="Remove">
                <svg viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2">
                    <path d="M12 4L4 12M4 4l8 8"/>
                </svg>
            </button>
        </div>
    `,
    )
    .join("");

  // Add remove handlers
  elements.selectedSchools.querySelectorAll(".remove").forEach((btn) => {
    btn.addEventListener("click", (e) => {
      e.stopPropagation();
      const urn = parseInt(btn.dataset.urn);
      removeFromComparison(urn);
    });
  });

  elements.chartsSection.style.display = "block";
  updateComparisonChart();
}

async function updateComparisonChart() {
  if (state.selectedSchools.length === 0) return;

  const data = await loadComparison();
  if (!data) return;

  const metric = elements.metricSelect.value;

  // Prepare chart data
  const datasets = [];
  const allYears = new Set();

  state.selectedSchools.forEach((school, index) => {
    const schoolData = data.comparison[school.urn];
    if (!schoolData) return;

    const yearlyData = schoolData.yearly_data;
    yearlyData.forEach((d) => allYears.add(d.year));

    const sortedData = yearlyData.sort((a, b) => a.year - b.year);

    datasets.push({
      label: schoolData.school_info.school_name,
      data: sortedData.map((d) => ({ x: d.year, y: d[metric] })),
      borderColor: CHART_COLORS[index % CHART_COLORS.length],
      backgroundColor: CHART_COLORS[index % CHART_COLORS.length] + "20",
      borderWidth: 3,
      pointRadius: 5,
      pointHoverRadius: 7,
      tension: 0.3,
      fill: false,
    });
  });

  const years = Array.from(allYears).sort();

  // Destroy existing chart
  if (comparisonChart) {
    comparisonChart.destroy();
  }

  // Create new chart
  const ctx = elements.comparisonChart.getContext("2d");
  comparisonChart = new Chart(ctx, {
    type: "line",
    data: {
      labels: years,
      datasets: datasets,
    },
    options: {
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: getChartAspectRatio(),
      plugins: {
        legend: {
          position: "bottom",
          labels: {
            font: { family: "'DM Sans', sans-serif", size: 12 },
            padding: 20,
            usePointStyle: true,
          },
        },
        title: {
          display: true,
          text: getMetricLabel(metric),
          font: { family: "'Playfair Display', serif", size: 18, weight: 600 },
          padding: { bottom: 20 },
        },
        tooltip: {
          backgroundColor: "#1a1612",
          titleFont: { family: "'DM Sans', sans-serif" },
          bodyFont: { family: "'DM Sans', sans-serif" },
          padding: 12,
          cornerRadius: 8,
        },
      },
      scales: {
        x: {
          title: {
            display: true,
            text: "Academic Year",
            font: { family: "'DM Sans', sans-serif", weight: 500 },
          },
          grid: { display: false },
        },
        y: {
          title: {
            display: true,
            text: getMetricLabel(metric),
            font: { family: "'DM Sans', sans-serif", weight: 500 },
          },
          grid: { color: "#e5dfd5" },
        },
      },
      interaction: {
        intersect: false,
        mode: "index",
      },
    },
  });

  // Update comparison table
  updateComparisonTable(data.comparison, metric, years);
}

function updateComparisonTable(comparison, metric, years) {
  const lastYear = years[years.length - 1];
  const prevYear = years.length > 1 ? years[years.length - 2] : null;

  // Build header with explicit year ranges
  let headerHtml = "<th>School</th>";
  years.forEach((year) => {
    headerHtml += `<th>${year}</th>`;
  });
  if (prevYear) {
    headerHtml += `<th title="Change from ${prevYear} to ${lastYear}">Δ 1yr</th>`;
  }
  if (years.length > 2) {
    headerHtml += `<th title="Standard deviation of scores (lower = more consistent)">Variability</th>`;
  }
  elements.tableHeader.innerHTML = headerHtml;

  // Build body
  let bodyHtml = "";
  state.selectedSchools.forEach((school, index) => {
    const schoolData = comparison[school.urn];
    if (!schoolData) return;

    const yearlyMap = {};
    schoolData.yearly_data.forEach((d) => {
      yearlyMap[d.year] = d[metric];
    });

    const lastValue = yearlyMap[lastYear];
    const prevValue = prevYear ? yearlyMap[prevYear] : null;

    // Calculate 1-year change
    const oneYearChange =
      prevValue != null && lastValue != null ? lastValue - prevValue : null;
    const oneYearChangeStr =
      oneYearChange !== null ? oneYearChange.toFixed(1) : "N/A";
    const oneYearClass =
      oneYearChange !== null
        ? oneYearChange >= 0
          ? "positive"
          : "negative"
        : "";

    // Calculate variability (standard deviation)
    const values = years
      .map((y) => yearlyMap[y])
      .filter((v) => v != null && v !== 0);
    let variabilityStr = "N/A";
    if (values.length >= 2) {
      const mean = values.reduce((a, b) => a + b, 0) / values.length;
      const squaredDiffs = values.map((v) => Math.pow(v - mean, 2));
      const variance = squaredDiffs.reduce((a, b) => a + b, 0) / values.length;
      const stdDev = Math.sqrt(variance);
      variabilityStr = "±" + stdDev.toFixed(1);
    }

    const color = CHART_COLORS[index % CHART_COLORS.length];

    bodyHtml += `<tr>`;
    bodyHtml += `<td><strong style="border-left: 3px solid ${color}; padding-left: 8px;">${escapeHtml(schoolData.school_info.school_name)}</strong></td>`;
    years.forEach((year) => {
      const value = yearlyMap[year];
      bodyHtml += `<td>${value != null ? formatMetricValue(value, metric) : "-"}</td>`;
    });
    if (prevYear) {
      bodyHtml += `<td class="${oneYearClass}">${oneYearChangeStr !== "N/A" ? (oneYearChange >= 0 ? "+" : "") + oneYearChangeStr : oneYearChangeStr}</td>`;
    }
    if (years.length > 2) {
      bodyHtml += `<td>${variabilityStr}</td>`;
    }
    bodyHtml += `</tr>`;
  });
  elements.tableBody.innerHTML = bodyHtml;
}

async function openSchoolModal(urn) {
  // Hide all school maps to prevent z-index overlap with modal
  document.querySelectorAll('.school-map').forEach(el => {
    el.style.visibility = 'hidden';
  });

  // Show loading state immediately
  elements.modal.classList.add("active");
  document.body.style.overflow = "hidden";
  elements.modalStats.innerHTML =
    '<div class="loading"><div class="loading-spinner"></div><p>Loading school data...</p></div>';
  elements.modalSchoolName.textContent = "Loading...";
  elements.modalMeta.innerHTML = "";

  const data = await loadSchoolDetails(urn);
  if (!data) {
    elements.modalStats.innerHTML =
      '<div class="empty-state"><p>Unable to load school data</p></div>';
    return;
  }

  state.currentSchoolData = data;

  elements.modalSchoolName.textContent = data.school_info.school_name;

  // Build meta tags including faith if applicable
  const faithDenom = data.school_info.religious_denomination;
  const showFaith = faithDenom &&
    faithDenom !== "None" &&
    faithDenom !== "Does not apply" &&
    faithDenom !== "";
  const faithTag = showFaith
    ? `<span class="school-tag faith">${escapeHtml(faithDenom)}</span>`
    : "";

  elements.modalMeta.innerHTML = `
        <span class="school-tag">${escapeHtml(data.school_info.local_authority || "")}</span>
        <span class="school-tag type">${escapeHtml(data.school_info.school_type || "")}</span>
        ${faithTag}
    `;

  // Build details section (address and age range)
  const ageRange = data.school_info.age_range;
  const address = data.school_info.address;
  let detailsHtml = "";
  if (address) {
    detailsHtml += `<div class="modal-address">${escapeHtml(address)}</div>`;
  }
  if (ageRange) {
    detailsHtml += `<div class="modal-age-range">Ages ${escapeHtml(ageRange)}</div>`;
  }
  elements.modalDetails.innerHTML = detailsHtml;

  // Get latest year data with actual results
  const sortedData = data.yearly_data.sort((a, b) => b.year - a.year);
  const latest =
    sortedData.find((d) => d.rwm_expected_pct !== null) || sortedData[0];

  // Get previous year for trend calculation
  const latestIndex = sortedData.indexOf(latest);
  const previous = sortedData[latestIndex + 1] || null;
  const prevRwm = previous?.rwm_expected_pct;

  // Find latest year with progress score data (not available for 2023-24, 2024-25)
  const latestWithProgress = sortedData.find(
    (d) => d.reading_progress !== null || d.writing_progress !== null || d.maths_progress !== null
  );
  const progressYear = latestWithProgress?.year || latest.year;
  const progressData = latestWithProgress || latest;

  elements.modalStats.innerHTML = `
        <div class="modal-stats-section">
            <h4>KS2 Results (${latest.year})</h4>
            <div class="modal-stats-grid">
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.rwm_expected_pct, "rwm_expected_pct")} ${getTrendIndicator(latest.rwm_expected_pct, prevRwm)}</div>
                    <div class="modal-stat-label">RWM Expected${createInfoTrigger("rwm_expected")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.rwm_high_pct, "rwm_high_pct")}</div>
                    <div class="modal-stat-label">RWM Higher${createInfoTrigger("rwm_higher")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.gps_expected_pct, "gps_expected_pct")}</div>
                    <div class="modal-stat-label">GPS Expected${createInfoTrigger("gps_expected")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.science_expected_pct, "science_expected_pct")}</div>
                    <div class="modal-stat-label">Science Expected${createInfoTrigger("science_expected")}</div>
                </div>
            </div>
        </div>
        <div class="modal-stats-section">
            <h4>Progress Scores (${progressYear}) ${createWarningTrigger("progress_scores_unavailable")}</h4>
            <div class="modal-stats-grid">
                <div class="modal-stat">
                    <div class="modal-stat-value ${getProgressClass(progressData.reading_progress)}">${formatMetricValue(progressData.reading_progress, "reading_progress")}</div>
                    <div class="modal-stat-label">Reading${createInfoTrigger("reading_progress")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value ${getProgressClass(progressData.writing_progress)}">${formatMetricValue(progressData.writing_progress, "writing_progress")}</div>
                    <div class="modal-stat-label">Writing${createInfoTrigger("writing_progress")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value ${getProgressClass(progressData.maths_progress)}">${formatMetricValue(progressData.maths_progress, "maths_progress")}</div>
                    <div class="modal-stat-label">Maths${createInfoTrigger("maths_progress")}</div>
                </div>
            </div>
        </div>
        <div class="modal-stats-section">
            <h4>School Context (${latest.year})</h4>
            <div class="modal-stats-grid">
                <div class="modal-stat">
                    <div class="modal-stat-value">${latest.total_pupils || "-"}</div>
                    <div class="modal-stat-label">Total Pupils${createInfoTrigger("total_pupils")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.eal_pct, "eal_pct")}</div>
                    <div class="modal-stat-label">% EAL${createInfoTrigger("eal_pct")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.sen_support_pct, "sen_support_pct")}</div>
                    <div class="modal-stat-label">% SEN Support${createInfoTrigger("sen_support_pct")}</div>
                </div>
            </div>
        </div>
        <div class="modal-stats-section">
            <h4>Test Absence (${latest.year})</h4>
            <div class="modal-stats-grid">
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.reading_absence_pct, "reading_absence_pct")}</div>
                    <div class="modal-stat-label">Reading${createInfoTrigger("reading_absence")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.maths_absence_pct, "maths_absence_pct")}</div>
                    <div class="modal-stat-label">Maths${createInfoTrigger("maths_absence")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.gps_absence_pct, "gps_absence_pct")}</div>
                    <div class="modal-stat-label">GPS${createInfoTrigger("gps_absence")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.writing_absence_pct, "writing_absence_pct")}</div>
                    <div class="modal-stat-label">Writing${createInfoTrigger("writing_absence")}</div>
                </div>
                <div class="modal-stat">
                    <div class="modal-stat-value">${formatMetricValue(latest.science_absence_pct, "science_absence_pct")}</div>
                    <div class="modal-stat-label">Science${createInfoTrigger("science_absence")}</div>
                </div>
            </div>
        </div>
    `;

  function getProgressClass(value) {
    if (value === null || value === undefined) return "";
    return value >= 0 ? "positive" : "negative";
  }

  // Create chart
  if (schoolDetailChart) {
    schoolDetailChart.destroy();
  }

  const validData = sortedData
    .filter((d) => d.rwm_expected_pct !== null)
    .reverse();
  const years = validData.map((d) => d.year);
  const ctx = elements.schoolDetailChart.getContext("2d");

  const mobile = isMobile();
  schoolDetailChart = new Chart(ctx, {
    type: "bar",
    data: {
      labels: years,
      datasets: [
        {
          label: mobile ? "Reading" : "Reading %",
          data: validData.map((d) => d.reading_expected_pct),
          backgroundColor: "#2d7d7d",
          borderRadius: 4,
        },
        {
          label: mobile ? "Writing" : "Writing %",
          data: validData.map((d) => d.writing_expected_pct),
          backgroundColor: "#c9a227",
          borderRadius: 4,
        },
        {
          label: mobile ? "Maths" : "Maths %",
          data: validData.map((d) => d.maths_expected_pct),
          backgroundColor: "#e07256",
          borderRadius: 4,
        },
      ],
    },
    options: {
      responsive: true,
      maintainAspectRatio: true,
      aspectRatio: getChartAspectRatio(),
      plugins: {
        legend: {
          position: "bottom",
          labels: {
            font: {
              family: "'DM Sans', sans-serif",
              size: mobile ? 11 : 12,
            },
            usePointStyle: true,
            pointStyle: "circle",
            padding: mobile ? 12 : 16,
            boxWidth: mobile ? 8 : 12,
          },
        },
        title: {
          display: true,
          text: mobile
            ? "KS2 Attainment (% expected)"
            : "KS2 Attainment Over Time (% meeting expected standard)",
          font: {
            family: "'Playfair Display', serif",
            size: mobile ? 14 : 16,
            weight: 600,
          },
          padding: {
            bottom: mobile ? 12 : 16,
          },
        },
        tooltip: {
          callbacks: {
            label: function(context) {
              return `${context.dataset.label}: ${context.parsed.y}%`;
            }
          }
        },
      },
      scales: {
        y: {
          beginAtZero: true,
          max: 100,
          grid: { color: "#e5dfd5" },
          ticks: {
            font: { size: mobile ? 10 : 12 },
            callback: function(value) {
              return value + "%";
            },
          },
        },
        x: {
          grid: { display: false },
          ticks: {
            font: { size: mobile ? 10 : 12 },
          },
        },
      },
    },
  });

  // Update add to compare button
  const isSelected = state.selectedSchools.some(
    (s) => s.urn === data.school_info.urn,
  );
  elements.addToCompare.textContent = isSelected
    ? "Remove from Compare"
    : "Add to Compare";
  elements.addToCompare.dataset.urn = data.school_info.urn;

  // Initialize modal map if coordinates available
  const lat = data.school_info.latitude;
  const lng = data.school_info.longitude;

  if (lat && lng && typeof L !== "undefined") {
    elements.modalMapContainer.style.display = "block";

    // Destroy existing map if any
    if (modalMap) {
      modalMap.remove();
      modalMap = null;
    }

    // Create map after a brief delay to ensure container is visible
    setTimeout(() => {
      try {
        modalMap = L.map(elements.modalMap, {
          scrollWheelZoom: false,
        }).setView([lat, lng], 15);

        L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
          attribution: "&copy; OpenStreetMap contributors",
          maxZoom: 19,
        }).addTo(modalMap);

        L.marker([lat, lng]).addTo(modalMap);

        // Handle click to open fullscreen map
        elements.modalMap.addEventListener("click", () => {
          openMapModal(lat, lng, data.school_info.school_name);
        });
      } catch (err) {
        console.error("Error initializing modal map:", err);
        elements.modalMapContainer.style.display = "none";
      }
    }, 100);
  } else {
    elements.modalMapContainer.style.display = "none";
  }
}

function closeModal() {
  elements.modal.classList.remove("active");
  document.body.style.overflow = "";
  state.currentSchoolData = null;

  // Clean up modal map
  if (modalMap) {
    modalMap.remove();
    modalMap = null;
  }

  // Restore visibility of school maps
  document.querySelectorAll('.school-map').forEach(el => {
    el.style.visibility = 'visible';
  });
}

function addToComparison(school) {
  if (state.selectedSchools.some((s) => s.urn === school.urn)) return;
  if (state.selectedSchools.length >= 5) {
    alert("Maximum 5 schools can be compared at once");
    return;
  }

  state.selectedSchools.push(school);
  saveSelectedSchoolsToStorage();
  renderSelectedSchools();
}

function removeFromComparison(urn) {
  state.selectedSchools = state.selectedSchools.filter((s) => s.urn !== urn);
  saveSelectedSchoolsToStorage();
  renderSelectedSchools();
}

function saveSelectedSchoolsToStorage() {
  try {
    localStorage.setItem("selectedSchools", JSON.stringify(state.selectedSchools));
  } catch (e) {
    console.warn("Failed to save to localStorage:", e);
  }
}

function loadSelectedSchoolsFromStorage() {
  try {
    const stored = localStorage.getItem("selectedSchools");
    if (stored) {
      const schools = JSON.parse(stored);
      if (Array.isArray(schools)) {
        state.selectedSchools = schools;
      }
    }
  } catch (e) {
    console.warn("Failed to load from localStorage:", e);
  }
}

function showEmptyState(container, message) {
  container.innerHTML = `
        <div class="empty-state">
            <svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.5">
                <circle cx="24" cy="24" r="20"/>
                <path d="M16 20h16M16 28h10"/>
            </svg>
            <p>${escapeHtml(message)}</p>
        </div>
    `;
}

function escapeHtml(text) {
  if (!text) return "";
  const div = document.createElement("div");
  div.textContent = text;
  return div.innerHTML;
}

// =============================================================================
// EVENT LISTENERS
// =============================================================================

function setupEventListeners() {
  // Navigation
  document.querySelectorAll(".nav-link").forEach((link) => {
    link.addEventListener("click", (e) => {
      e.preventDefault();
      const view = link.dataset.view;
      const path = view === "home" ? "/" : `/${view}`;
      navigateTo(path);
    });
  });

  // Search mode toggle
  elements.searchModeBtns.forEach((btn) => {
    btn.addEventListener("click", () => {
      const mode = btn.dataset.mode;
      if (mode === state.searchMode) return;

      // Update state
      state.searchMode = mode;
      state.pagination.page = 1;

      // Update toggle buttons
      elements.searchModeBtns.forEach((b) => b.classList.remove("active"));
      btn.classList.add("active");

      // Update panels
      elements.nameSearchPanel.classList.toggle("active", mode === "name");
      elements.locationSearchPanel.classList.toggle(
        "active",
        mode === "location",
      );

      // Clear the inactive mode's state
      if (mode === "name") {
        // Clear location search state
        state.locationSearch = { active: false, postcode: null, radius: 5, coords: null };
        elements.postcodeSearch.value = "";
        elements.radiusSelect.value = "5";
        elements.typeFilterLocation.value = "";
        updateLocationInfoBanner(null);
        // Reset to list view and hide toggle
        setResultsView("list");
        updateViewToggle();
      } else {
        // Clear name search state
        elements.schoolSearch.value = "";
        elements.localAuthorityFilter.value = "";
        elements.typeFilter.value = "";
      }

      // Reload schools (will show featured if no active search)
      loadSchools();
    });
  });

  // View toggle (list/map)
  elements.viewToggleBtns.forEach((btn) => {
    btn.addEventListener("click", () => {
      const view = btn.dataset.view;
      if (view !== state.resultsView) {
        setResultsView(view);
      }
    });
  });

  // Name search and filters
  let searchTimeout;
  elements.schoolSearch.addEventListener("input", () => {
    clearTimeout(searchTimeout);
    state.pagination.page = 1; // Reset to first page on new search
    searchTimeout = setTimeout(loadSchools, 300);
  });

  elements.localAuthorityFilter.addEventListener("change", () => {
    state.pagination.page = 1;
    loadSchools();
  });
  elements.typeFilter.addEventListener("change", () => {
    state.pagination.page = 1;
    loadSchools();
  });

  // Location search
  if (elements.locationSearchBtn) {
    elements.locationSearchBtn.addEventListener("click", searchByLocation);
  }
  if (elements.postcodeSearch) {
    elements.postcodeSearch.addEventListener("keydown", (e) => {
      if (e.key === "Enter") {
        e.preventDefault();
        searchByLocation();
      }
    });
  }
  if (elements.typeFilterLocation) {
    elements.typeFilterLocation.addEventListener("change", () => {
      if (state.locationSearch.active) {
        state.pagination.page = 1;
        loadSchools();
      }
    });
  }

  // Compare search
  let compareSearchTimeout;
  let lastCompareSearchData = null;

  function renderCompareResults(data) {
    if (!data) return;
    lastCompareSearchData = data;

    const results = data.schools.filter(
      (s) => !state.selectedSchools.some((sel) => sel.urn === s.urn),
    );

    const headerHtml = `
            <div class="compare-results-header">
                <span>${results.length} school${results.length !== 1 ? "s" : ""} found</span>
                <button class="compare-results-close" title="Close (Esc)">
                    <svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
                        <path d="M12 4L4 12M4 4l8 8"/>
                    </svg>
                </button>
            </div>
        `;

    if (results.length === 0) {
      elements.compareResults.innerHTML =
        headerHtml +
        '<div class="compare-result-item"><span class="name">No more schools to add</span></div>';
    } else {
      elements.compareResults.innerHTML =
        headerHtml +
        results
          .slice(0, 10)
          .map(
            (school) => `
                <div class="compare-result-item" data-urn="${school.urn}" data-name="${escapeHtml(school.school_name)}">
                    <div class="name">${escapeHtml(school.school_name)}</div>
                    <div class="location">${escapeHtml(school.local_authority || "")}${school.postcode ? " • " + escapeHtml(school.postcode) : ""}</div>
                </div>
            `,
          )
          .join("");

      elements.compareResults
        .querySelectorAll(".compare-result-item")
        .forEach((item) => {
          item.addEventListener("click", (e) => {
            e.stopPropagation(); // Prevent click-outside handler from closing results
            const urn = parseInt(item.dataset.urn);
            const school = data.schools.find((s) => s.urn === urn);
            if (school) {
              addToComparison(school);
              renderCompareResults(data);
            }
          });
        });
    }

    // Add close button handler
    const closeBtn = elements.compareResults.querySelector(
      ".compare-results-close",
    );
    if (closeBtn) {
      closeBtn.addEventListener("click", () => {
        elements.compareResults.classList.remove("active");
        elements.compareSearch.value = "";
      });
    }
  }

  elements.compareSearch.addEventListener("input", async () => {
    clearTimeout(compareSearchTimeout);
    const query = elements.compareSearch.value.trim();

    if (query.length < 2) {
      elements.compareResults.classList.remove("active");
      return;
    }

    compareSearchTimeout = setTimeout(async () => {
      const data = await fetchAPI(
        `/api/schools?search=${encodeURIComponent(query)}`,
        { useCache: false },
      );
      if (!data) return;

      renderCompareResults(data);
      elements.compareResults.classList.add("active");
    }, 300);
  });

  elements.compareSearch.addEventListener("focus", () => {
    if (
      elements.compareSearch.value.trim().length >= 2 &&
      lastCompareSearchData
    ) {
      renderCompareResults(lastCompareSearchData);
      elements.compareResults.classList.add("active");
    }
  });

  // Close compare results when clicking outside
  document.addEventListener("click", (e) => {
    if (!elements.compareResults.classList.contains("active")) return;

    // Don't close if clicking inside the search input or results
    if (
      elements.compareSearch.contains(e.target) ||
      elements.compareResults.contains(e.target)
    ) {
      return;
    }

    // Close the results
    elements.compareResults.classList.remove("active");
  });

  // Metric selector
  elements.metricSelect.addEventListener("change", updateComparisonChart);

  // Rankings
  elements.rankingArea.addEventListener("change", loadRankings);
  elements.rankingMetric.addEventListener("change", loadRankings);
  elements.rankingYear.addEventListener("change", loadRankings);

  // Modal
  elements.modalClose.addEventListener("click", closeModal);
  elements.modal
    .querySelector(".modal-backdrop")
    .addEventListener("click", closeModal);

  elements.addToCompare.addEventListener("click", () => {
    if (!state.currentSchoolData) return;

    const urn = state.currentSchoolData.school_info.urn;
    const isSelected = state.selectedSchools.some((s) => s.urn === urn);

    if (isSelected) {
      removeFromComparison(urn);
      elements.addToCompare.textContent = "Add to Compare";
    } else {
      addToComparison(state.currentSchoolData.school_info);
      elements.addToCompare.textContent = "Remove from Compare";
    }
  });

  // Keyboard
  document.addEventListener("keydown", (e) => {
    if (e.key === "Escape") {
      if (elements.compareResults.classList.contains("active")) {
        elements.compareResults.classList.remove("active");
        elements.compareSearch.value = "";
        return;
      }
      closeModal();
    }
  });
}

// =============================================================================
// TOOLTIP MANAGER
// =============================================================================

class TooltipManager {
  constructor() {
    this.activeTooltip = null;
    this.showTimeout = null;
    this.hideTimeout = null;
    this.isTouchDevice =
      "ontouchstart" in window || navigator.maxTouchPoints > 0;

    this.init();
  }

  init() {
    // Create tooltip container element (singleton)
    this.tooltipEl = document.createElement("div");
    this.tooltipEl.className = "tooltip";
    this.tooltipEl.setAttribute("role", "tooltip");
    this.tooltipEl.setAttribute("aria-hidden", "true");
    document.body.appendChild(this.tooltipEl);

    // Event delegation on document
    this.bindEvents();
  }

  bindEvents() {
    if (this.isTouchDevice) {
      document.addEventListener("click", this.handleTouchClick.bind(this));
    } else {
      document.addEventListener(
        "mouseenter",
        this.handleMouseEnter.bind(this),
        true
      );
      document.addEventListener(
        "mouseleave",
        this.handleMouseLeave.bind(this),
        true
      );
      document.addEventListener("focusin", this.handleFocusIn.bind(this));
      document.addEventListener("focusout", this.handleFocusOut.bind(this));
    }

    // Escape key closes tooltip
    document.addEventListener("keydown", (e) => {
      if (e.key === "Escape" && this.activeTooltip) {
        this.hide();
      }
    });
  }

  handleMouseEnter(e) {
    if (!e.target || !e.target.closest) return;
    const trigger = e.target.closest(".info-trigger, .warning-trigger");
    if (!trigger) return;

    clearTimeout(this.hideTimeout);
    this.showTimeout = setTimeout(() => {
      this.show(trigger);
    }, 150);
  }

  handleMouseLeave(e) {
    if (!e.target || !e.target.closest) return;
    const trigger = e.target.closest(".info-trigger, .warning-trigger");
    const tooltip = e.target.closest(".tooltip");

    if (!trigger && !tooltip) return;

    // Check if moving between trigger and tooltip
    const relatedTarget = e.relatedTarget;
    if (
      relatedTarget?.closest?.(".info-trigger, .warning-trigger") === this.activeTooltip ||
      relatedTarget?.closest?.(".tooltip")
    ) {
      return;
    }

    clearTimeout(this.showTimeout);
    this.hideTimeout = setTimeout(() => {
      this.hide();
    }, 100);
  }

  handleFocusIn(e) {
    if (!e.target || !e.target.closest) return;
    const trigger = e.target.closest(".info-trigger, .warning-trigger");
    if (!trigger) return;

    clearTimeout(this.hideTimeout);
    this.show(trigger);
  }

  handleFocusOut(e) {
    if (!e.target || !e.target.closest) return;
    const trigger = e.target.closest(".info-trigger, .warning-trigger");
    if (!trigger) return;

    this.hideTimeout = setTimeout(() => {
      this.hide();
    }, 100);
  }

  handleTouchClick(e) {
    const trigger = e.target.closest(".info-trigger, .warning-trigger");

    if (trigger) {
      e.preventDefault();
      e.stopPropagation();

      if (this.activeTooltip === trigger) {
        this.hide();
      } else {
        this.show(trigger);
      }
      return;
    }

    // Tap outside closes tooltip
    if (this.activeTooltip && !e.target.closest(".tooltip")) {
      this.hide();
    }
  }

  show(trigger) {
    // Check if it's an info trigger or warning trigger
    const isWarning = trigger.classList.contains("warning-trigger");
    let definition;

    if (isWarning) {
      const warningKey = trigger.dataset.warning;
      definition = WARNING_DEFINITIONS[warningKey];
      if (!definition) {
        console.warn(`No definition found for warning: ${warningKey}`);
        return;
      }
      this.tooltipEl.classList.add("tooltip-warning");
    } else {
      const termKey = trigger.dataset.term;
      definition = TERM_DEFINITIONS[termKey];
      if (!definition) {
        console.warn(`No definition found for term: ${termKey}`);
        return;
      }
      this.tooltipEl.classList.remove("tooltip-warning");
    }

    // Build tooltip content
    let content = "";
    if (definition.title) {
      content += `<div class="tooltip-title">${definition.title}</div>`;
    }
    content += `<div class="tooltip-description">${definition.description}</div>`;
    if (definition.note) {
      content += `<div class="tooltip-note">${definition.note}</div>`;
    }

    this.tooltipEl.innerHTML = content;

    // Make tooltip visible first so we can measure it
    this.tooltipEl.style.visibility = "hidden";
    this.tooltipEl.style.opacity = "0";
    this.tooltipEl.classList.add("visible");

    // Position tooltip
    this.position(trigger);

    // Show tooltip with animation
    this.tooltipEl.style.visibility = "";
    this.tooltipEl.style.opacity = "";
    this.tooltipEl.setAttribute("aria-hidden", "false");
    trigger.setAttribute("aria-expanded", "true");

    this.activeTooltip = trigger;
  }

  hide() {
    if (!this.activeTooltip) return;

    this.tooltipEl.classList.remove("visible");
    this.tooltipEl.setAttribute("aria-hidden", "true");
    this.activeTooltip.setAttribute("aria-expanded", "false");

    this.activeTooltip = null;
  }

  position(trigger) {
    const triggerRect = trigger.getBoundingClientRect();
    const tooltipRect = this.tooltipEl.getBoundingClientRect();

    // Determine placement: prefer top, fall back to bottom if not enough space
    const spaceAbove = triggerRect.top;
    const tooltipHeight = tooltipRect.height || 100;

    let placement = "top";
    let top;

    if (spaceAbove < tooltipHeight + 20) {
      placement = "bottom";
      top = triggerRect.bottom + 10 + window.scrollY;
    } else {
      top = triggerRect.top - tooltipHeight - 10 + window.scrollY;
    }

    // Horizontal centering with edge detection
    let left =
      triggerRect.left +
      triggerRect.width / 2 -
      tooltipRect.width / 2 +
      window.scrollX;

    // Prevent overflow on left
    if (left < 10) {
      left = 10;
    }

    // Prevent overflow on right
    const rightEdge = left + tooltipRect.width;
    if (rightEdge > window.innerWidth - 10) {
      left = window.innerWidth - tooltipRect.width - 10;
    }

    this.tooltipEl.style.top = `${top}px`;
    this.tooltipEl.style.left = `${left}px`;
    this.tooltipEl.dataset.placement = placement;
  }
}

// Global tooltip manager instance
let tooltipManager = null;
