import React, { useEffect, useRef, useState } from "react";
import ReactDOM from "react-dom/client";
import {
  ArrowDown, ArrowLeft, ArrowRight, BookOpen, Check, ChevronLeft, ChevronRight,
  Clock, Compass, Expand, Heart, Lightbulb, Mail, MapPin, Menu, Phone, Quote, ShieldCheck, Star, X
} from "./lib/icons";
import { config } from "./lib/config";
import routeMetaData from "./route-meta.json";
import faqs from "./faqs.json";
import { enquirySchema, submitEnquiry, type Enquiry } from "./services/forms";
import "./styles.css";

function CrayonIcon(props: React.SVGProps<SVGSVGElement>) {
  return <svg viewBox="0 -960 960 960" fill="currentColor" {...props}><path d="M200-120q-17 0-28.5-11.5T160-160v-97q0-16 6-30.5t17-25.5l504-503q12-12 27-18t30-6q16 0 30.5 6t25.5 18l56 56q12 11 18 25.5t6 30.5q0 15-6 30t-18 27L353-143q-11 11-25.5 17t-30.5 6h-97Zm40-80h56l393-392-28-29-29-28-392 393v56Zm560-503-57-57 57 57Zm-139 82-29-28 57 57-28-29ZM560-120q74 0 137-37t63-103q0-32-16-55.5T702-359q-14-10-30-10t-27 12q-11 12-11 29.5t14 27.5q14 11 23 20t9 20q0 23-36.5 41.5T560-200q-17 0-28.5 11.5T520-160q0 17 11.5 28.5T560-120ZM360-720q0 14-17.5 25.5T262-654q-80 35-111 63.5T120-520q0 26 12 46t31 35q13 11 29 9.5t27-14.5q11-13 10-29t-14-27q-7-5-11-10t-4-10q0-12 18-24t76-37q88-38 117-69t29-70q0-55-44-87.5T280-840q-45 0-80.5 16T145-785q-11 13-9 29t15 26q13 11 29 9t27-13q14-14 31-20t42-6q41 0 60.5 12t19.5 28Z" /></svg>;
}
function PaperPlaneIcon(props: React.SVGProps<SVGSVGElement>) {
  return <svg viewBox="0 -960 960 960" fill="currentColor" {...props}><path d="M792-443 176-183q-20 8-38-3.5T120-220v-520q0-22 18-33.5t38-3.5l616 260q25 11 25 37t-25 37ZM200-280l474-200-474-200v140l240 60-240 60v140Zm0 0v-400 400Z" /></svg>;
}
function BlocksIcon(props: React.SVGProps<SVGSVGElement>) {
  return <svg viewBox="0 -960 960 960" fill="currentColor" {...props}><path d="M638-468 468-638q-6-6-8.5-13t-2.5-15q0-8 2.5-15t8.5-13l170-170q6-6 13-8.5t15-2.5q8 0 15 2.5t13 8.5l170 170q6 6 8.5 13t2.5 15q0 8-2.5 15t-8.5 13L694-468q-6 6-13 8.5t-15 2.5q-8 0-15-2.5t-13-8.5Zm-518-92v-240q0-17 11.5-28.5T160-840h240q17 0 28.5 11.5T440-800v240q0 17-11.5 28.5T400-520H160q-17 0-28.5-11.5T120-560Zm400 400v-240q0-17 11.5-28.5T560-440h240q17 0 28.5 11.5T840-400v240q0 17-11.5 28.5T800-120H560q-17 0-28.5-11.5T520-160Zm-400 0v-240q0-17 11.5-28.5T160-440h240q17 0 28.5 11.5T440-400v240q0 17-11.5 28.5T400-120H160q-17 0-28.5-11.5T120-160Zm80-440h160v-160H200v160Zm467 48 113-113-113-113-113 113 113 113Zm-67 352h160v-160H600v160Zm-400 0h160v-160H200v160Zm160-400Zm194-65ZM360-360Zm240 0Z" /></svg>;
}
function RainbowIcon(props: React.SVGProps<SVGSVGElement>) {
  return <svg viewBox="0 -960 960 960" fill="currentColor" {...props}><path d="m706-706-70-32q-6-3-8.5-8t-2.5-10q0-5 2.5-10t8.5-8l70-32 32-70q3-6 8-9t10-3q5 0 10 3t8 9l32 70 70 32q6 3 9 8t3 10q0 5-3 10t-9 8l-70 32-32 70q-3 6-8 8.5t-10 2.5q-5 0-10-2.5t-8-8.5l-32-70ZM260-380l-160-73q-9-4-13-11.5T83-480q0-8 4-15.5t13-11.5l160-73 73-160q4-9 11.5-13t15.5-4q8 0 15.5 4t11.5 13l73 160 160 73q9 4 13 11.5t4 15.5q0 8-4 15.5T620-453l-160 73-73 160q-4 9-11.5 13t-15.5 4q-8 0-15.5-4T333-220l-73-160Zm100 26 40-86 86-40-86-40-40-86-40 86-86 40 86 40 40 86Zm350 204-70-32q-6-3-9-8t-3-10q0-5 3-10t9-8l70-32 32-70q3-6 8-9t10-3q5 0 10 3t8 9l32 70 70 32q6 3 9 8t3 10q0 5-3 10t-9 8l-70 32-32 70q-3 6-8 9t-10 3q-5 0-10-3t-8-9l-32-70ZM360-480Z" /></svg>;
}

function useInView<T extends HTMLElement>(options?: IntersectionObserverInit) {
  const ref = useRef<T | null>(null);
  const [inView, setInView] = useState(false);
  useEffect(() => {
    const node = ref.current;
    if (!node) return;
    const observer = new IntersectionObserver(([entry]) => {
      if (entry.isIntersecting) { setInView(true); observer.disconnect(); }
    }, { threshold: 0.4, ...options });
    observer.observe(node);
    return () => observer.disconnect();
  }, []);
  return { ref, inView };
}

function CountUp({ value }: { value: string }) {
  const match = value.match(/^(\d+)(.*)$/);
  const { ref, inView } = useInView<HTMLSpanElement>();
  const [display, setDisplay] = useState(match ? 0 : value);
  useEffect(() => {
    if (!match || !inView) return;
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) { setDisplay(Number(match[1])); return; }
    const target = Number(match[1]);
    const duration = 1100;
    const start = performance.now();
    let raf = 0;
    const tick = (now: number) => {
      const progress = Math.min((now - start) / duration, 1);
      const eased = 1 - Math.pow(1 - progress, 3);
      setDisplay(Math.round(eased * target));
      if (progress < 1) raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [inView]);
  return <strong ref={ref}>{match ? `${display}${match[2]}` : display}</strong>;
}

function useSpotlight() {
  const onMouseMove = (event: React.MouseEvent<HTMLElement>) => {
    const rect = event.currentTarget.getBoundingClientRect();
    event.currentTarget.style.setProperty("--spot-x", `${event.clientX - rect.left}px`);
    event.currentTarget.style.setProperty("--spot-y", `${event.clientY - rect.top}px`);
  };
  return { className: "spotlight", onMouseMove };
}

function Photo({ className = "", src, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) {
  const [loaded, setLoaded] = useState(false);
  const webpSrc = typeof src === "string" ? src.replace(/\.(jpe?g|png)$/i, ".webp") : undefined;
  return <picture>
    {webpSrc && webpSrc !== src ? <source srcSet={webpSrc} type="image/webp" /> : null}
    <img
      className={`photo-fade${loaded ? " is-loaded" : ""}${className ? ` ${className}` : ""}`}
      decoding="async"
      onLoad={() => setLoaded(true)}
      src={src}
      {...props}
    />
  </picture>;
}

const salutations = ["Welcome", "Akwaaba", "Miishi", "Woezor", "Sannu"];
function Salutation() {
  const [index, setIndex] = useState(0);
  useEffect(() => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const timer = setInterval(() => setIndex((i) => (i + 1) % salutations.length), 2200);
    return () => clearInterval(timer);
  }, []);
  return <span className="salutation" key={index}>{salutations[index]}</span>;
}

function SplitReveal({ text, inView }: { text: string; inView: boolean }) {
  const words = text.split(" ");
  let charIndex = 0;
  return <span className="split-reveal" role="text" aria-label={text}>
    {words.map((word, wi) => <span className="split-word" key={wi} aria-hidden="true">
      {[...word].map((char, ci) => {
        const order = charIndex++;
        return <span
          className={`split-char${inView ? " is-visible" : ""}`}
          key={ci}
          style={{ ["--char-order" as string]: order }}
        >{char}</span>;
      })}
      {wi < words.length - 1 ? <span className="split-char split-space is-visible"> </span> : null}
    </span>)}
  </span>;
}

function MotionEngine() {
  useEffect(() => {
    const surface = document.querySelector<HTMLElement>(".motion-reactive");
    if (!surface) return;
    const reduced = matchMedia("(prefers-reduced-motion: reduce)");
    if (reduced.matches) return;
    let previousY = scrollY;
    let previousTime = performance.now();
    let current = 0;
    let target = 0;
    let frame = 0;
    const onScroll = () => {
      const now = performance.now();
      const delta = scrollY - previousY;
      target = Math.max(-1, Math.min(1, delta / Math.max(16, now - previousTime) / 1.45));
      previousY = scrollY;
      previousTime = now;
    };
    const tick = () => {
      target *= .82;
      current += (target - current) * .16;
      if (Math.abs(current) < .001 && Math.abs(target) < .001) current = target = 0;
      surface.style.setProperty("--scroll-v", current.toFixed(4));
      surface.style.setProperty("--scroll-speed", Math.abs(current).toFixed(4));
      surface.classList.toggle("is-kinetic", Math.abs(current) > .025);
      frame = requestAnimationFrame(tick);
    };
    addEventListener("scroll", onScroll, { passive: true });
    frame = requestAnimationFrame(tick);
    return () => { removeEventListener("scroll", onScroll); cancelAnimationFrame(frame); surface.style.removeProperty("--scroll-v"); surface.style.removeProperty("--scroll-speed"); surface.classList.remove("is-kinetic"); };
  }, []);
  return null;
}

function MagneticText({ text }: { text: string }) {
  const ref = useRef<HTMLSpanElement>(null);
  const move = (event: React.PointerEvent<HTMLSpanElement>) => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches || event.pointerType === "touch") return;
    const letters = ref.current?.querySelectorAll<HTMLElement>(".magnetic-letter");
    letters?.forEach((letter) => {
      const rect = letter.getBoundingClientRect();
      const dx = event.clientX - (rect.left + rect.width / 2);
      const dy = event.clientY - (rect.top + rect.height / 2);
      const distance = Math.hypot(dx, dy);
      const force = Math.max(0, 1 - distance / 130);
      letter.style.setProperty("--mx", `${dx * force * .1}px`);
      letter.style.setProperty("--my", `${dy * force * .12}px`);
    });
  };
  const reset = () => ref.current?.querySelectorAll<HTMLElement>(".magnetic-letter").forEach((letter) => { letter.style.setProperty("--mx", "0px"); letter.style.setProperty("--my", "0px"); });
  return <span ref={ref} className="magnetic-type" role="text" aria-label={text} onPointerMove={move} onPointerLeave={reset}>
    {[...text].map((char, index) => <span className="magnetic-letter" aria-hidden="true" key={`${char}-${index}`}>{char === " " ? "\u00a0" : char}</span>)}
  </span>;
}

function TypeTransformation() {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches || matchMedia("(max-width: 700px)").matches) return;
    let frame = 0;
    const update = () => {
      frame = 0;
      const section = ref.current;
      if (!section) return;
      const rect = section.getBoundingClientRect();
      const travel = Math.max(1, rect.height - innerHeight);
      const progress = Math.max(0, Math.min(1, -rect.top / travel));
      const outgoing = Math.min(1, progress / .54);
      const incoming = Math.max(0, (progress - .42) / .58);
      section.querySelectorAll<HTMLElement>(".type-exits i").forEach((letter, index, list) => {
        const offset = index - (list.length - 1) / 2;
        letter.style.opacity = String(1 - outgoing);
        letter.style.transform = `translate3d(${offset * 11 * outgoing}vw, ${(index % 2 ? -1 : 1) * (32 + index * 3) * outgoing}vh, ${220 * outgoing}px) rotate(${(index - 4) * 9 * outgoing}deg) scale(${1 - outgoing * .35})`;
      });
      section.querySelectorAll<HTMLElement>(".type-enters i").forEach((letter, index, list) => {
        const offset = index - (list.length - 1) / 2;
        const inverse = 1 - incoming;
        letter.style.opacity = String(incoming);
        letter.style.transform = `translate3d(${offset * -7.92 * inverse}vw, ${(index % 2 ? 1 : -1) * (25 + index * 2.4) * inverse}vh, ${220 * inverse}px) rotate(${(index - 4) * -9 * inverse}deg) scale(${.65 + incoming * .35})`;
      });
    };
    const onScroll = () => { if (!frame) frame = requestAnimationFrame(update); };
    addEventListener("scroll", onScroll, { passive: true });
    addEventListener("resize", onScroll);
    update();
    return () => { removeEventListener("scroll", onScroll); removeEventListener("resize", onScroll); cancelAnimationFrame(frame); };
  }, []);
  const letters = (word: string, className: string) => <strong className={className}>{[...word].map((char, index) => <i key={`${char}-${index}`}>{char}</i>)}</strong>;
  return <section ref={ref} className="type-morph" aria-label="Curiosity becomes belonging"><div className="type-morph-stage"><span>We make room for</span>{letters("CURIOSITY", "type-exits")}{letters("BELONGING", "type-enters")}</div></section>;
}

function movePhysicsCards(event: React.PointerEvent<HTMLDivElement>) {
  if (matchMedia("(prefers-reduced-motion: reduce)").matches || event.pointerType === "touch") return;
  event.currentTarget.classList.add("is-loose");
  event.currentTarget.querySelectorAll<HTMLElement>(".programme-card").forEach((card, index) => {
    const rect = card.getBoundingClientRect();
    const dx = rect.left + rect.width / 2 - event.clientX;
    const dy = rect.top + rect.height / 2 - event.clientY;
    const distance = Math.max(70, Math.hypot(dx, dy));
    const force = Math.max(0, 1 - distance / 360);
    card.style.setProperty("--push-x", `${dx / distance * force * 22}px`);
    card.style.setProperty("--push-y", `${dy / distance * force * 16}px`);
    card.style.setProperty("--loose-r", `${(index - 1) * 1.4}deg`);
  });
}

function resetPhysicsCards(event: React.PointerEvent<HTMLDivElement>) {
  event.currentTarget.classList.remove("is-loose");
  event.currentTarget.querySelectorAll<HTMLElement>(".programme-card").forEach((card) => {
    card.style.setProperty("--push-x", "0px"); card.style.setProperty("--push-y", "0px"); card.style.setProperty("--loose-r", "0deg");
  });
}

function GravityToken({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLSpanElement>(null);
  const motion = useRef({ x: 0, y: 0, vx: 0, vy: 0, px: 0, py: 0, time: 0, frame: 0, dragging: false });
  const paint = () => {
    ref.current?.style.setProperty("--token-x", `${motion.current.x}px`);
    ref.current?.style.setProperty("--token-y", `${motion.current.y}px`);
  };
  useEffect(() => () => cancelAnimationFrame(motion.current.frame), []);
  const down = (event: React.PointerEvent<HTMLSpanElement>) => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    cancelAnimationFrame(motion.current.frame);
    event.currentTarget.setPointerCapture(event.pointerId);
    motion.current.dragging = true;
    motion.current.px = event.clientX;
    motion.current.py = event.clientY;
    motion.current.time = performance.now();
    event.currentTarget.classList.add("is-dragging");
  };
  const move = (event: React.PointerEvent<HTMLSpanElement>) => {
    if (!motion.current.dragging) return;
    const now = performance.now();
    const dt = Math.max(8, now - motion.current.time);
    const dx = event.clientX - motion.current.px;
    const dy = event.clientY - motion.current.py;
    motion.current.x += dx;
    motion.current.y += dy;
    motion.current.vx = dx / dt * 16;
    motion.current.vy = dy / dt * 16;
    motion.current.px = event.clientX;
    motion.current.py = event.clientY;
    motion.current.time = now;
    paint();
  };
  const release = (event: React.PointerEvent<HTMLSpanElement>) => {
    if (!motion.current.dragging) return;
    motion.current.dragging = false;
    event.currentTarget.classList.remove("is-dragging");
    const settle = () => {
      const state = motion.current;
      state.vx += -state.x * .085;
      state.vy += -state.y * .085 + .08;
      state.vx *= .78;
      state.vy *= .78;
      state.x += state.vx;
      state.y += state.vy;
      paint();
      if (Math.abs(state.x) + Math.abs(state.y) + Math.abs(state.vx) + Math.abs(state.vy) > .35) state.frame = requestAnimationFrame(settle);
      else { state.x = state.y = state.vx = state.vy = 0; paint(); }
    };
    motion.current.frame = requestAnimationFrame(settle);
  };
  return <span ref={ref} className="gravity-token" onPointerDown={down} onPointerMove={move} onPointerUp={release} onPointerCancel={release}>{children}</span>;
}

function DepthStage({ className = "", children }: { className?: string; children: React.ReactNode }) {
  const onMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const rect = event.currentTarget.getBoundingClientRect();
    const x = (event.clientX - rect.left) / rect.width - 0.5;
    const y = (event.clientY - rect.top) / rect.height - 0.5;
    event.currentTarget.style.setProperty("--tiltx", `${(-y * 6).toFixed(2)}deg`);
    event.currentTarget.style.setProperty("--tilty", `${(x * 6).toFixed(2)}deg`);
    event.currentTarget.style.setProperty("--shiftx", `${(x * 14).toFixed(2)}px`);
    event.currentTarget.style.setProperty("--shifty", `${(y * 14).toFixed(2)}px`);
  };
  const onMouseLeave = (event: React.MouseEvent<HTMLDivElement>) => {
    event.currentTarget.style.setProperty("--tiltx", "0deg");
    event.currentTarget.style.setProperty("--tilty", "0deg");
    event.currentTarget.style.setProperty("--shiftx", "0px");
    event.currentTarget.style.setProperty("--shifty", "0px");
  };
  return <div className={`depth-stage${className ? ` ${className}` : ""}`} onMouseMove={onMouseMove} onMouseLeave={onMouseLeave}>{children}</div>;
}

function useCarousel(length: number, { autoplayMs }: { autoplayMs?: number } = {}) {
  const [index, setIndex] = useState(0);
  const [direction, setDirection] = useState(1);
  const [paused, setPaused] = useState(false);
  const go = (next: number) => {
    setDirection(next > index || (index === length - 1 && next === 0) ? 1 : -1);
    setIndex(((next % length) + length) % length);
  };
  useEffect(() => {
    if (!autoplayMs || paused || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
    const timer = setInterval(() => go(index + 1), autoplayMs);
    return () => clearInterval(timer);
  }, [index, paused, autoplayMs]);

  const dragRef = useRef({ startX: 0, dragging: false });
  const onPointerDown = (event: React.PointerEvent) => {
    if (event.target !== event.currentTarget && (event.target as HTMLElement).closest("button")) return;
    dragRef.current.dragging = true;
    dragRef.current.startX = event.clientX;
  };
  const onPointerUp = (event: React.PointerEvent) => {
    if (!dragRef.current.dragging) return;
    dragRef.current.dragging = false;
    const delta = event.clientX - dragRef.current.startX;
    if (Math.abs(delta) > 40) go(index + (delta < 0 ? 1 : -1));
  };

  return {
    index, direction, setPaused,
    next: () => go(index + 1),
    prev: () => go(index - 1),
    to: go,
    dragHandlers: { onPointerDown, onPointerUp, onPointerCancel: onPointerUp },
  };
}

function normalisePath(path: string) {
  const clean = path.replace(/\/+$/, "");
  return clean || "/";
}
function usePathname() {
  const read = () => normalisePath(location.pathname);
  const [pathname, setPathname] = useState(read);
  useEffect(() => {
    const update = () => setPathname(read());
    addEventListener("popstate", update);
    return () => removeEventListener("popstate", update);
  }, []);
  return pathname;
}
function Link({ to, className = "", children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement> & { to: string }) {
  const navigate = (event: React.MouseEvent<HTMLAnchorElement>) => {
    props.onClick?.(event);
    if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
    event.preventDefault();
    history.pushState({}, "", to);
    dispatchEvent(new PopStateEvent("popstate"));
  };
  return <a href={to} className={className} {...props} onClick={navigate}>{children}</a>;
}
function NavLink({ to, children }: { to: string; children: React.ReactNode }) {
  const pathname = usePathname();
  return <Link to={to} className={pathname === to ? "active" : ""} aria-current={pathname === to ? "page" : undefined}>{children}</Link>;
}

const programmes = [
  { age: "12 months — 2 years", title: "Crèche & Nursery", copy: "A warm first chapter shaped by care, sensory discovery, movement and happy friendships.", image: "/images/nursery-room.jpg", tone: "orange" },
  { age: "2 — 5 years", title: "Kindergarten", copy: "Play-led EYFS and IEYC experiences that turn every small question into a big discovery.", image: "/images/puzzle-table.jpg", tone: "lilac" },
  { age: "5+ years", title: "Primary", copy: "Strong foundations, confident communication and the character to thrive in a changing world.", image: "/images/classroom-labels.jpg", tone: "lime" },
];
const testimonials = [
  { quote: "My son looks forward to school with a smile that warms our hearts. Glow-Up has provided safety, growth and lasting joy since his first day.", name: "Mrs Evelyn Boadu", role: "Parent" },
  { quote: "As parents, we wanted a school that builds confidence and strong values. Glow-Up has given us both assurance and pride every single day.", name: "Mr Joseph Darko", role: "Parent" },
  { quote: "Glow-Up has made learning meaningful and fun for our daughter. Each morning she is eager, and we feel truly blessed with peace of mind.", name: "Mrs Lydia Owusu", role: "Parent" },
];
const schoolLifeGallery = [
  { image: "/images/playground-tube-slide.jpg", caption: "Playground adventures" },
  { image: "/images/monkey-bridge.jpg", caption: "Climbing and courage" },
  { image: "/images/outdoor-xylophone.jpg", caption: "Music in the open air" },
  { image: "/images/roundabout-play.jpg", caption: "Spinning with friends" },
  { image: "/images/frog-reading-corner.jpg", caption: "A cosy reading corner" },
  { image: "/images/classroom-tech.jpg", caption: "Guided learning" },
  { image: "/images/garden.png", caption: "Garden discovery" },
];
const aboutGallery = [
  { image: "/images/montessori-classroom.jpg", caption: "Our classrooms" },
  { image: "/images/puzzle-table.jpg", caption: "Everyday learning" },
  { image: "/images/music-trio.jpg", caption: "Creative expression" },
  { image: "/images/reading-nook-friends.jpg", caption: "Learning through play" },
];
const fullGallery = [
  { image: "/images/hero.jpg", caption: "Story time together" },
  { image: "/images/ball-pit-slide.jpg", caption: "Ball pit adventures" },
  { image: "/images/playground-tube-slide.jpg", caption: "Outdoor playground" },
  { image: "/images/music-trio.jpg", caption: "Music & rhythm" },
  { image: "/images/puzzle-table.jpg", caption: "Everyday classroom life" },
  { image: "/images/welcome.jpg", caption: "A warm welcome" },
  { image: "/images/outdoor-xylophone.jpg", caption: "Creative expression" },
  { image: "/images/montessori-classroom.jpg", caption: "Our classrooms" },
  { image: "/images/classroom-labels.jpg", caption: "Guided learning" },
  { image: "/images/reading-nook-friends.jpg", caption: "Hands-on discovery" },
  { image: "/images/monkey-bridge.jpg", caption: "Climbing and courage" },
  { image: "/images/ball-pit-giggles.jpg", caption: "Giggles and slides" },
  { image: "/images/frog-reading-corner.jpg", caption: "A cosy reading corner" },
  { image: "/images/roundabout-play.jpg", caption: "Spinning with friends" },
  { image: "/images/phonics-wall.jpg", caption: "Words all around us" },
  { image: "/images/garden.png", caption: "Garden discovery" },
];

function BrandLoader() {
  const [leaving, setLeaving] = useState(false);
  const [hidden, setHidden] = useState(false);
  useEffect(() => {
    let active = true;
    let hideTimer = 0;
    const fontReady = document.fonts?.ready ?? Promise.resolve();
    const safetyTimeout = new Promise<void>((resolve) => window.setTimeout(resolve, 400));
    Promise.race([fontReady, safetyTimeout]).then(() => {
      if (!active) return;
      requestAnimationFrame(() => {
        if (!active) return;
        setLeaving(true);
        hideTimer = window.setTimeout(() => setHidden(true), 150);
      });
    });
    return () => { active = false; window.clearTimeout(hideTimer); };
  }, []);
  if (hidden) return null;
  return <div className={`brand-loader${leaving ? " is-leaving" : ""}`} aria-hidden="true">
    <span className="brand-loader-mark">
      <svg className="brand-loader-ring" viewBox="0 0 100 100" aria-hidden="true"><circle cx="50" cy="50" r="46" /></svg>
      <img src="/images/logo.png" alt="" />
    </span>
    <span className="brand-loader-word">The Glow-Up<small>International School</small></span>
  </div>;
}

function ScrollManager({ pathname }: { pathname: string }) {
  const initialPath = useRef(pathname);
  useEffect(() => {
    scrollTo({ top: 0, behavior: "instant" });
    if (pathname !== initialPath.current) {
      requestAnimationFrame(() => {
        const heading = document.querySelector<HTMLElement>("#main-content h1");
        heading?.setAttribute("tabindex", "-1");
        heading?.focus({ preventScroll: true });
      });
    }
  }, [pathname]);
  useEffect(() => {
    const nodes = document.querySelectorAll(".reveal, .section-label");
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) {
      nodes.forEach((node) => node.classList.add("is-visible"));
      return;
    }
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) { entry.target.classList.add("is-visible"); observer.unobserve(entry.target); }
      });
    }, { threshold: 0.2, rootMargin: "0px 0px -8% 0px" });
    nodes.forEach((node) => observer.observe(node));
    return () => observer.disconnect();
  }, [pathname]);
  return null;
}

const routeMeta: Record<string, { title: string; description: string; crumb: string }> = routeMetaData;
function setMetaTag(selector: string, attr: string, content: string) {
  document.querySelector(selector)?.setAttribute(attr, content);
}
function useRouteMeta(pathname: string) {
  useEffect(() => {
    const entry = routeMeta[pathname] || { title: "Page not found | The Glow-Up International School", description: "The page you requested could not be found.", crumb: "Not found" };
    const url = `${config.siteUrl}${pathname === "/" ? "" : pathname}`;
    document.title = entry.title;
    setMetaTag('meta[name="description"]', "content", entry.description);
    setMetaTag('meta[property="og:title"]', "content", entry.title);
    setMetaTag('meta[property="og:description"]', "content", entry.description);
    setMetaTag('meta[property="og:url"]', "content", url);
    setMetaTag('meta[name="twitter:title"]', "content", entry.title);
    setMetaTag('meta[name="twitter:description"]', "content", entry.description);
    let robots = document.querySelector('meta[name="robots"]');
    if (!robots) {
      robots = document.createElement("meta");
      robots.setAttribute("name", "robots");
      document.head.appendChild(robots);
    }
    robots.setAttribute("content", routeMeta[pathname] && pathname !== "/thank-you" ? "index, follow" : "noindex, follow");
    let canonical = document.querySelector('link[rel="canonical"]');
    if (!canonical) {
      canonical = document.createElement("link");
      canonical.setAttribute("rel", "canonical");
      document.head.appendChild(canonical);
    }
    canonical.setAttribute("href", url);
  }, [pathname]);
  return routeMeta[pathname]?.crumb;
}

function JsonLd({ data }: { data: unknown }) {
  return <script type="application/ld+json">{JSON.stringify(data).replace(/</g, "\\u003c")}</script>;
}
function Breadcrumbs({ pathname, crumb }: { pathname: string; crumb?: string }) {
  if (pathname === "/" || !crumb) return null;
  const items = [
    { name: "Home", to: "/" },
    { name: crumb, to: pathname },
  ];
  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    itemListElement: items.map((item, index) => ({
      "@type": "ListItem",
      position: index + 1,
      name: item.name,
      item: `${config.siteUrl}${item.to === "/" ? "" : item.to}`,
    })),
  };
  return <nav className="breadcrumbs" aria-label="Breadcrumb">
    <JsonLd data={jsonLd} />
    <ol>
      {items.map((item, index) => index === items.length - 1
        ? <li key={item.to} aria-current="page">{item.name}</li>
        : <li key={item.to}><Link to={item.to}>{item.name}</Link><ChevronRight size={13} aria-hidden="true" /></li>)}
    </ol>
  </nav>;
}

function Layout() {
  const [menuOpen, setMenuOpen] = useState(false);
  const [scrolled, setScrolled] = useState(false);
  const [progress, setProgress] = useState(0);
  const pathname = usePathname();
  const crumb = useRouteMeta(pathname);
  useEffect(() => setMenuOpen(false), [pathname]);
  useEffect(() => {
    const onScroll = () => {
      setScrolled(scrollY > 24);
      const h = document.documentElement.scrollHeight - innerHeight;
      setProgress(h > 0 ? Math.min(scrollY / h, 1) : 0);
    };
    const onKey = (e: KeyboardEvent) => e.key === "Escape" && setMenuOpen(false);
    addEventListener("scroll", onScroll); addEventListener("keydown", onKey); onScroll();
    return () => {
      removeEventListener("scroll", onScroll); removeEventListener("keydown", onKey);
    };
  }, [pathname]);
  useEffect(() => {
    document.body.classList.toggle("menu-is-open", menuOpen);
    return () => document.body.classList.remove("menu-is-open");
  }, [menuOpen]);

  return <>
    <BrandLoader />
    <MotionEngine />
    <ScrollManager pathname={pathname} />
    <a className="skip-link" href="#main-content">Skip to content</a>
    <div className="scroll-progress" aria-hidden="true"><span style={{ transform: `scaleX(${progress})` }} /></div>
    <header className={`site-nav ${scrolled ? "is-scrolled" : ""}`}>
      <Link className="brand" to="/" aria-label="The Glow-Up International School home"><img src="/images/logo.png" alt="" /><span><strong>The Glow-Up</strong><small>International School</small></span></Link>
      <nav id="primary-navigation" className={menuOpen ? "nav-links open" : "nav-links"} aria-label="Primary navigation">
        <NavLink to="/">Home</NavLink><NavLink to="/about">About Us</NavLink><NavLink to="/learning">Learning</NavLink><NavLink to="/school-life">School Life</NavLink><NavLink to="/admissions">Admission</NavLink><NavLink to="/gallery">Gallery</NavLink><NavLink to="/parents">Parents</NavLink><NavLink to="/contact">Contact</NavLink>
        <Link className="mobile-nav-cta" to="/admissions">Apply now <ArrowRight size={16} /></Link>
      </nav>
      <Link className="pill pill-dark nav-apply" to="/admissions">Apply now <ArrowRight size={16} /></Link>
      <button className="menu-toggle" onClick={() => setMenuOpen(!menuOpen)} aria-label={menuOpen ? "Close navigation" : "Open navigation"} aria-expanded={menuOpen} aria-controls="primary-navigation">{menuOpen ? <X /> : <Menu />}</button>
    </header>
    <main id="main-content" className="page-shell motion-reactive" key={pathname}>
      <Breadcrumbs pathname={pathname} crumb={crumb} />
      {pathname === "/" ? <Home /> :
       pathname === "/about" ? <About /> :
       pathname === "/learning" ? <Learning /> :
       pathname === "/school-life" ? <SchoolLife /> :
       pathname === "/admissions" ? <Admissions /> :
       pathname === "/gallery" ? <Gallery /> :
       pathname === "/parents" ? <Parents /> :
       pathname === "/contact" ? <Contact /> :
       pathname === "/faq" ? <FAQ /> :
       pathname === "/privacy-policy" ? <PrivacyPolicy /> :
       pathname === "/thank-you" ? <ThankYou /> : <NotFound />}
    </main>
    <aside className="mobile-utility" aria-label="Quick actions">
      <a href="tel:+233542973500"><Phone size={17} /> Call</a>
      <Link to="/admissions">Apply now <ArrowRight size={16} /></Link>
    </aside>
    <Footer />
  </>;
}

function Home() {
  const carousel = useCarousel(testimonials.length, { autoplayMs: 6500 });
  const slide = carousel.index;
  const [heroIn, setHeroIn] = useState(false);
  useEffect(() => { const raf = requestAnimationFrame(() => setHeroIn(true)); return () => cancelAnimationFrame(raf); }, []);
  return <>
    <section className="hero page-home">
      <div className="hero-copy"><Link className="announce" to="/admissions"><span className="salutation-bubble"><Salutation /></span><span className="announce-copy"><span>Admissions open</span><span className="announce-detail"> — enrolment available all year</span></span><ArrowRight size={14} /></Link><h1><span className="text-reveal"><SplitReveal text="Growing bright minds" inView={heroIn} /></span><br /><span className="text-reveal accent" style={{ ["--reveal-order" as string]: 1 }}><MagneticText text="for a brilliant future." /></span></h1><p>Purposeful learning, genuine care and room to wonder—an international education in Accra built around who your child is becoming.</p><div className="actions"><Link className="pill pill-dark" to="/admissions">Start an application <ArrowRight size={17} /></Link><Link className="pill pill-light" to="/contact">Book a school tour <ArrowDown size={17} /></Link></div></div>
      <DepthStage className="hero-stage"><div className="scene-orbit scene-orbit-left"><Photo src="/images/play.jpg" alt="" /></div><div className="scene-orbit scene-orbit-right"><Photo src="/images/learning.jpg" alt="" /></div><div className="learning-tokens" aria-hidden="true"><GravityToken>A</GravityToken><GravityToken>1</GravityToken><GravityToken>+</GravityToken></div><div className="doodle doodle-star"><Star fill="currentColor" /></div><div className="doodle doodle-flower"><PaperPlaneIcon /></div><div className="doodle doodle-blocks"><BlocksIcon /></div><div className="doodle doodle-crayon"><CrayonIcon /></div><div className="doodle doodle-rainbow"><RainbowIcon /></div><div className="photo-main"><Photo src="/images/hero.jpg" alt="Two Glow-Up learners reading together" loading="eager" fetchPriority="high" /></div><div className="float-card curriculum-card"><span className="round-icon"><BookOpen /></span><div><b>International learning</b><small>British EYFS + IEYC</small></div></div><div className="float-card happiness-card"><div className="faces"><span>G</span><span>U</span><span><Star size={13} fill="currentColor" /></span></div><div><b>Built for belonging</b><small>Small classes. Big care.</small></div></div></DepthStage>
      <div className="hero-proof" aria-label="School highlights"><p>Trusted by families who want childhood and achievement to thrive together.</p><div><span><CountUp value="12m+" /> entry age</span><span><strong>EYFS</strong> foundations</span><span><strong>IEYC</strong> discovery-led</span></div></div>
    </section>
    <TypeTransformation />
    <section className="home-pathways section"><div className="section-label">Find their place</div><div className="section-heading-row"><h2><span className="text-reveal reveal"><span>One school.</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">Every beginning.</span></span></h2><p>Explore a learning pathway designed around your child’s developmental stage—not a one-size-fits-all timetable.</p></div><div className="pathway-preview">{programmes.map((p, i) => <Link to="/learning" className="pathway-row reveal" key={p.title}><span>0{i + 1}</span><h3>{p.title}</h3><small>{p.age}</small><span className="pathway-photo" aria-hidden="true"><Photo src={p.image} alt="" loading="lazy" /></span><ArrowRight /></Link>)}</div></section>
    <section className="testimonial-zone" onMouseEnter={() => carousel.setPaused(true)} onMouseLeave={() => carousel.setPaused(false)} onFocus={() => carousel.setPaused(true)} onBlur={(event) => { if (!event.currentTarget.contains(event.relatedTarget)) carousel.setPaused(false); }}><div className="testimonial-visual"><Photo src="/images/ball-pit-slide.jpg" alt="Happy Glow-Up learners at play" loading="lazy" /><span>Families say it best</span></div><div className="testimonial-carousel" aria-live="polite" {...carousel.dragHandlers}><Quote /><div className="slide-key" key={slide} style={{ ["--slide-dir" as string]: carousel.direction }}><blockquote>“{testimonials[slide].quote}”</blockquote><p><b>{testimonials[slide].name}</b><span>{testimonials[slide].role}</span></p></div><div className="carousel-controls"><button onClick={carousel.prev} aria-label="Previous testimonial"><ChevronLeft /></button><div className="carousel-dots" role="tablist" aria-label="Choose testimonial">{testimonials.map((t, i) => <button key={t.name} role="tab" className={i === slide ? "active" : ""} aria-selected={i === slide} aria-label={`Show testimonial from ${t.name}`} onClick={() => carousel.to(i)} />)}</div><button onClick={carousel.next} aria-label="Next testimonial"><ChevronRight /></button></div>{config.googleReviewsUrl ? <a className="google-reviews-link" href={config.googleReviewsUrl} target="_blank" rel="noopener noreferrer">Read more reviews on Google <ArrowRight size={15} /></a> : <span className="google-reviews-link is-placeholder">Google reviews link coming soon</span>}</div></section>
    <Callout title="Come see childhood in full colour." copy="Visit our classrooms, meet our facilitators and experience the warmth for yourself." action="Plan your visit" to="/contact" />
  </>;
}

function PageHero({ label, title, accent, copy, image }: { label:string; title:string; accent:string; copy:string; image:string }) {
  return <section className="inner-hero"><div className="inner-copy"><div className="section-label">{label}</div><h1><span className="text-reveal reveal"><span>{title}</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">{accent}</span></span></h1><p>{copy}</p></div><DepthStage className="inner-image"><Photo src={image} alt={`${label} at The Glow-Up International School`} loading="eager" fetchPriority="high" /><span className="image-orbit"><Star fill="currentColor" /></span><span className="doodle doodle-pencil"><CrayonIcon /></span><span className="doodle doodle-book"><BookOpen /></span></DepthStage></section>;
}
const caseStudyPlaceholders = [
  { title: "A confident first term", summary: "How one Nursery family saw their child settle in and start speaking up within weeks. Full story coming soon." },
  { title: "Finding their voice in Primary", summary: "A look at how our IEYC-led classrooms help a shy reader become a class leader. Full story coming soon." },
];
function CaseStudiesSection() {
  return <section className="case-studies section"><div className="section-label">Family stories</div><h2><span className="text-reveal reveal"><span>Real journeys,</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">real growth.</span></span></h2><div className="case-study-grid">{caseStudyPlaceholders.map((cs) => <article className="case-study-card reveal" key={cs.title}><h3>{cs.title}</h3><p>{cs.summary}</p></article>)}</div></section>;
}
const schoolTeam = [
  { role: "Head Teacher", name: "Miss Dorcas Adom", tone: "coral" },
  { role: "Assistant Head Teacher", name: "Kelvin Ofori", tone: "violet" },
  { role: "Early Years Assistant", name: "Miss Gladys Adom", tone: "sun" },
];
function TeamSection() {
  return <section className="team-section section"><div className="section-label">Our team</div><h2><span className="text-reveal reveal"><span>The people behind</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">every glow-up.</span></span></h2><div className="team-grid">{schoolTeam.map((member) => <article className="team-card reveal" key={member.role}><div className={`team-photo-placeholder tone-${member.tone}`} aria-hidden="true"><Heart /></div><h3>{member.name}</h3><p>{member.role}</p></article>)}</div><p className="team-photo-note">Staff portraits will be added after the school approves the current official photographs.</p></section>;
}
function About() { const spotlight = useSpotlight(); return <><PageHero label="Our school" title="A place to belong." accent="A world to discover." copy="The Glow-Up International School is a caring learning community in Accra, created to make every child’s earliest years count." image="/images/welcome.jpg" /><section className="manifesto section"><div className="section-label">Our purpose</div><div className="manifesto-grid"><h2 className="text-reveal reveal"><span>We nurture the whole child—not just the student.</span></h2><div><p>Our vision is to become a nationally recognised private school known for safe, excellent early-learning experiences that empower little minds for a brighter future.</p><p>We bring that vision to life through thoughtful teaching, close family partnerships and an environment that gives children the confidence to explore.</p></div></div></section><PhotoCarousel label="Life at Glow-Up" items={aboutGallery} /><section className="values section"><div className="section-label">What guides us</div><h2><span className="text-reveal reveal"><span>Bright minds need</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">kind hearts.</span></span></h2><div className="value-grid">{[["Curiosity",Compass,"Ask boldly, explore freely and keep wondering.","coral"],["Creativity",BookOpen,"Imagine possibilities and bring ideas to life.","blue"],["Compassion",Heart,"Care deeply for others and the world we share.","pink"],["Integrity",ShieldCheck,"Choose honesty, responsibility and what is right.","yellow"]].map(([t,I,c,color],i)=>{const Icon=I as typeof Compass;return <article className={`reveal ${spotlight.className}`} onMouseMove={spotlight.onMouseMove} key={t as string}><div className={`value-icon ${color}`}><Icon /></div><span>0{i+1}</span><h3>{t as string}</h3><p>{c as string}</p></article>})}</div></section><TeamSection /><CaseStudiesSection /><Callout title="Meet the community behind the learning." copy="A visit is the best way to feel what makes Glow-Up different." action="Book a school tour" to="/contact" /></>; }
function Learning() { const spotlight = useSpotlight(); return <><PageHero label="Learning" title="Curiosity leads." accent="Confidence follows." copy="Our programmes combine the British EYFS and International Early Years Curriculum with purposeful, locally relevant experiences." image="/images/learning.jpg" /><section className="programmes section"><div className="section-label">Programmes — move to explore</div><div className="programme-grid physics-wall" onPointerMove={movePhysicsCards} onPointerLeave={resetPhysicsCards}>{programmes.map((p)=><article className={`programme-card ${p.tone} reveal ${spotlight.className}`} onMouseMove={spotlight.onMouseMove} key={p.title}><div className="card-top"><small>{p.age}</small></div><div className="card-photo"><Photo src={p.image} alt="" loading="lazy" /></div><h3>{p.title}</h3><p>{p.copy}</p><Link to="/admissions">Ask about availability <span><ArrowRight size={17}/></span></Link></article>)}</div></section><section className="learning-method"><div><span className="tone-coral"><Lightbulb /></span><h3>Explore</h3><p>Children begin with a meaningful question, object or experience.</p></div><div><span className="tone-blue"><BookOpen /></span><h3>Connect</h3><p>Guided activities help them build language, ideas and understanding.</p></div><div><span className="tone-sun"><Star /></span><h3>Express</h3><p>They share what they know through words, art, movement and play.</p></div></section><Callout title="Choose a programme that meets them where they are." copy="Our admissions team will help you identify the most appropriate class." action="Start an enquiry" to="/admissions" /></>; }
function PhotoCarousel({ label, items }: { label: string; items: { image: string; caption: string }[] }) {
  const carousel = useCarousel(items.length, { autoplayMs: 4800 });
  const item = items[carousel.index];
  return <section className="photo-carousel section" onMouseEnter={() => carousel.setPaused(true)} onMouseLeave={() => carousel.setPaused(false)}>
    <div className="section-label">{label}</div>
    <div className="photo-carousel-stage" {...carousel.dragHandlers}>
      <div className="photo-carousel-frame" key={carousel.index} style={{ ["--slide-dir" as string]: carousel.direction }}>
        <Photo src={item.image} alt={item.caption} loading="lazy" />
        <span className="photo-carousel-caption">{item.caption}</span>
      </div>
      <button className="carousel-edge prev" onClick={carousel.prev} aria-label="Previous photo"><ChevronLeft /></button>
      <button className="carousel-edge next" onClick={carousel.next} aria-label="Next photo"><ChevronRight /></button>
    </div>
    <div className="carousel-dots" role="tablist" aria-label="Choose photo">
      {items.map((g, i) => <button key={g.image} role="tab" className={i === carousel.index ? "active" : ""} aria-selected={i === carousel.index} aria-label={`Show photo: ${g.caption}`} onClick={() => carousel.to(i)} />)}
    </div>
  </section>;
}

function Lightbox({ items, index, onClose, onNav }: { items: { image: string; caption: string }[]; index: number; onClose: () => void; onNav: (next: number) => void }) {
  const dialogRef = useRef<HTMLDivElement>(null);
  const closeRef = useRef<HTMLButtonElement>(null);
  useEffect(() => {
    const previouslyFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
    document.body.style.overflow = "hidden";
    closeRef.current?.focus();
    return () => {
      document.body.style.overflow = "";
      previouslyFocused?.focus();
    };
  }, []);
  useEffect(() => {
    const onKey = (event: KeyboardEvent) => {
      if (event.key === "Escape") onClose();
      if (event.key === "ArrowRight") onNav(index + 1);
      if (event.key === "ArrowLeft") onNav(index - 1);
      if (event.key === "Tab") {
        const controls = dialogRef.current?.querySelectorAll<HTMLElement>('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])');
        if (!controls?.length) return;
        const first = controls[0];
        const last = controls[controls.length - 1];
        if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
        else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
      }
    };
    addEventListener("keydown", onKey);
    return () => removeEventListener("keydown", onKey);
  }, [index]);
  const dragRef = useRef({ startX: 0 });
  const item = items[index];
  return <div ref={dialogRef} className="lightbox" role="dialog" aria-modal="true" aria-label={item.caption} onClick={onClose}>
    <button ref={closeRef} className="lightbox-close" onClick={onClose} aria-label="Close image viewer"><X /></button>
    <button className="lightbox-edge prev" onClick={(e) => { e.stopPropagation(); onNav(index - 1); }} aria-label="Previous photo"><ChevronLeft /></button>
    <div
      className="lightbox-frame"
      key={index}
      onClick={(e) => e.stopPropagation()}
      onPointerDown={(e) => { dragRef.current.startX = e.clientX; }}
      onPointerUp={(e) => { const delta = e.clientX - dragRef.current.startX; if (Math.abs(delta) > 50) onNav(index + (delta < 0 ? 1 : -1)); }}
    >
      <img src={item.image} alt={item.caption} />
      <span className="lightbox-caption">{item.caption} <small>{index + 1} / {items.length}</small></span>
    </div>
    <button className="lightbox-edge next" onClick={(e) => { e.stopPropagation(); onNav(index + 1); }} aria-label="Next photo"><ChevronRight /></button>
  </div>;
}
function useLightbox(length: number) {
  const [index, setIndex] = useState<number | null>(null);
  return {
    open: index,
    show: (i: number) => setIndex(i),
    close: () => setIndex(null),
    nav: (next: number) => setIndex(((next % length) + length) % length),
  };
}
function SchoolLife() { return <><PageHero label="School life" title="Serious learning." accent="Unmistakable joy." copy="A Glow-Up day balances focused discovery with movement, friendship, creativity and time to simply be a child." image="/images/ball-pit-giggles.jpg" /><section className="gallery-mosaic section"><div className="mosaic-tall"><Photo src="/images/classroom-tech.jpg" alt="Learners in a guided classroom session" /></div><div className="mosaic-copy"><div className="section-label">Beyond the classroom</div><h2><span className="text-reveal reveal"><span>Room to read,</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">run and imagine.</span></span></h2><p>Our air-conditioned classrooms, digital learning tools, library, playgrounds and modern infirmary support every part of a child’s day.</p></div><div><Photo src="/images/reading-nook-friends.jpg" alt="Learners reading together in the library nook" /></div><div><Photo src="/images/garden.png" alt="Learners exploring outdoors" /></div></section><PhotoCarousel label="A day in pictures" items={schoolLifeGallery} /><section className="day-strip"><span><Clock /> 8:00 AM</span><b>Welcome & settle</b><b>Guided discovery</b><b>Outdoor play</b><b>Creative expression</b><span>3:00 PM <ArrowRight /></span></section><Callout title="See the school day come alive." copy="Arrange a guided visit during school hours." action="Plan a visit" to="/contact" /></>; }
type EnquiryFormState = { parentName: string; email: string; phone: string; childAge: string; consent: boolean };
const emptyEnquiry: EnquiryFormState = { parentName: "", email: "", phone: "", childAge: "", consent: false };

function EnquiryForm() {
  const formRef = useRef<HTMLFormElement>(null);
  const [values, setValues] = useState<EnquiryFormState>(emptyEnquiry);
  const [errors, setErrors] = useState<Partial<Record<keyof EnquiryFormState, string>>>({});
  const [submitting, setSubmitting] = useState(false);
  const [formError, setFormError] = useState("");

  const update = <K extends keyof EnquiryFormState>(key: K, value: EnquiryFormState[K]) => setValues((v) => ({ ...v, [key]: value }));

  const onSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setFormError("");
    const parsed = enquirySchema.safeParse(values as Enquiry);
    if (!parsed.success) {
      const fieldErrors: Partial<Record<keyof EnquiryFormState, string>> = {};
      for (const issue of parsed.error.issues) {
        const key = issue.path[0] as keyof EnquiryFormState;
        if (key === "consent") fieldErrors.consent = "Please confirm you consent to be contacted.";
        else fieldErrors[key] = issue.message;
      }
      setErrors(fieldErrors);
      requestAnimationFrame(() => formRef.current?.querySelector<HTMLElement>('[aria-invalid="true"]')?.focus());
      return;
    }
    setErrors({});
    setSubmitting(true);
    try {
      await submitEnquiry(parsed.data);
      history.pushState({}, "", "/thank-you");
      dispatchEvent(new PopStateEvent("popstate"));
    } catch {
      setFormError("Online enquiries are currently unavailable. Please call +233 (0) 54 297 3500 or email theglowupschool@gmail.com.");
    } finally {
      setSubmitting(false);
    }
  };

  return <form ref={formRef} className="enquiry-form" onSubmit={onSubmit} noValidate>
    <div className="enquiry-grid">
      <label>
        <span>Parent / guardian name</span>
        <input type="text" autoComplete="name" maxLength={100} value={values.parentName} onChange={(e) => update("parentName", e.target.value)} aria-invalid={!!errors.parentName} aria-describedby={errors.parentName ? "err-parentName" : undefined} />
        {errors.parentName && <small id="err-parentName" className="field-error">{errors.parentName}</small>}
      </label>
      <label>
        <span>Email address</span>
        <input type="email" autoComplete="email" maxLength={254} value={values.email} onChange={(e) => update("email", e.target.value)} aria-invalid={!!errors.email} aria-describedby={errors.email ? "err-email" : undefined} />
        {errors.email && <small id="err-email" className="field-error">{errors.email}</small>}
      </label>
      <label>
        <span>Phone number</span>
        <input type="tel" autoComplete="tel" inputMode="tel" maxLength={24} value={values.phone} onChange={(e) => update("phone", e.target.value)} aria-invalid={!!errors.phone} aria-describedby={errors.phone ? "err-phone" : undefined} />
        {errors.phone && <small id="err-phone" className="field-error">{errors.phone}</small>}
      </label>
      <label>
        <span>Child's age</span>
        <input type="text" autoComplete="off" maxLength={40} placeholder="e.g. 2 years" value={values.childAge} onChange={(e) => update("childAge", e.target.value)} aria-invalid={!!errors.childAge} aria-describedby={errors.childAge ? "err-childAge" : undefined} />
        {errors.childAge && <small id="err-childAge" className="field-error">{errors.childAge}</small>}
      </label>
    </div>
    <label className="enquiry-consent">
      <input type="checkbox" checked={values.consent} onChange={(e) => update("consent", e.target.checked)} aria-invalid={!!errors.consent} aria-describedby={errors.consent ? "err-consent" : undefined} />
      <span>I consent to The Glow-Up International School contacting me about this enquiry. See our <Link to="/privacy-policy">Privacy Policy</Link>.</span>
    </label>
    {errors.consent && <small id="err-consent" className="field-error">{errors.consent}</small>}
    {formError && <p className="form-error" role="alert">{formError}</p>}
    <button className="pill pill-dark" type="submit" disabled={submitting}>{submitting ? "Sending…" : "Send enquiry"} <ArrowRight size={17} /></button>
  </form>;
}

function AdmissionsContact() {
  if (config.admissionsEndpoint) return <EnquiryForm />;
  return <aside className="enquiry-fallback" aria-labelledby="enquiry-fallback-title">
    <span><Mail aria-hidden="true" /></span>
    <div>
      <h3 id="enquiry-fallback-title">Let’s start personally.</h3>
      <p>Online enquiries are being prepared. Call or email our admissions team and we’ll help you choose the right class.</p>
      <div className="actions">
        <a className="pill pill-dark" href="tel:+233542973500"><Phone size={17} /> Call admissions</a>
        <a className="pill pill-light" href="mailto:theglowupschool@gmail.com"><Mail size={17} /> Email the school</a>
      </div>
    </div>
  </aside>;
}

const stepTones = ["coral", "sun", "lime", "violet"];
function Admissions() { const steps=["Tell us about your child","Visit and meet the team","Confirm the right class","Welcome to Glow-Up"]; return <><PageHero label="Admissions" title="Their next chapter." accent="Your simple next steps." copy="We welcome enquiries throughout the year, subject to class availability. Our process is personal, clear and designed to help your family decide with confidence." image="/images/community.jpg" /><section className="admission-steps section"><div className="section-label">How it works</div><h2><span className="text-reveal reveal"><span>From hello to</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">first day.</span></span></h2><div>{steps.map((s,i)=><article key={s}><span>0{i+1}</span><h3>{s}</h3><Check className={`tone-${stepTones[i % stepTones.length]}`} /></article>)}</div></section><section className="admission-info"><div><h3>What to bring</h3><p>Your child’s birth certificate, previous school report where applicable, medical notes and two passport photographs.</p></div><div><h3>When to apply</h3><p>Applications are accepted year-round. Earlier enquiries offer the best chance of securing your preferred class.</p></div><div><h3>Need guidance?</h3><p>Call <a href="tel:+233542973500">+233 (0) 54 297 3500</a> and our admissions team will help.</p></div></section><section className="enquiry-section section"><div className="section-label">Start an enquiry</div><h2><span className="text-reveal reveal"><span>Tell us about</span></span><br /><span className="text-reveal reveal" style={{ ["--reveal-order" as string]: 1 }}><span className="accent">your child.</span></span></h2><AdmissionsContact /></section></>; }
function Gallery() {
  const lightbox = useLightbox(fullGallery.length);
  return <><PageHero label="Gallery" title="Real moments." accent="Real childhood." copy="A closer look at classrooms, play and everyday life at The Glow-Up International School." image="/images/playground.jpg" /><section className="photo-grid section">{fullGallery.map((g, i) => <figure className="photo-grid-item reveal" key={g.image} role="button" tabIndex={0} aria-label={`Open photo: ${g.caption}`} onClick={() => lightbox.show(i)} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); lightbox.show(i); } }}><Photo src={g.image} alt={g.caption} loading="lazy" /><figcaption>{g.caption}</figcaption><span className="photo-grid-expand"><Expand size={15} /></span></figure>)}</section>{lightbox.open !== null && <Lightbox items={fullGallery} index={lightbox.open} onClose={lightbox.close} onNav={lightbox.nav} />}<Callout title="Come see it all in person." copy="Photos only tell part of the story—visit us for the rest." action="Plan your visit" to="/contact" /></>;
}
function Parents() { const spotlight = useSpotlight(); return <><PageHero label="Parents" title="Everything you" accent="need, in one place." copy="Resources, updates and support to help your family stay connected with The Glow-Up International School." image="/images/nursery-room.jpg" /><section className="contact-grid section">{config.parentPortalUrl?<a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href={config.parentPortalUrl}><ShieldCheck/><small>Parent portal</small><b>Sign in for updates</b><ArrowRight/></a>:<Link className={spotlight.className} onMouseMove={spotlight.onMouseMove} to="/contact"><ShieldCheck/><small>Parent support</small><b>Ask us for portal access</b><ArrowRight/></Link>}<a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href="mailto:theglowupschool@gmail.com"><Mail/><small>Email the school</small><b>theglowupschool@gmail.com</b><ArrowRight/></a><div className={spotlight.className} onMouseMove={spotlight.onMouseMove}><Clock/><small>School hours</small><b>Monday–Friday</b><span>8:00 AM–3:00 PM</span></div><a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href="tel:+233542973500"><Phone/><small>Call the office</small><b>+233 (0) 54 297 3500</b><ArrowRight/></a></section><Callout title="Have a question about your child's day?" copy="Our team is always happy to help." action="Get in touch" to="/contact" /></>; }
function Contact() { const spotlight = useSpotlight(); return <><PageHero label="Contact" title="Let’s begin with" accent="a conversation." copy="Ask a question, arrange a school visit or speak with our admissions team. We would love to hear about your child." image="/images/hero.jpg" /><section className="contact-grid section"><a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href="tel:+233542973500"><Phone/><small>Call admissions</small><b>+233 (0) 54 297 3500</b><span>Alternative: +233 (0) 20 884 3017</span><ArrowRight/></a><a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href="mailto:theglowupschool@gmail.com"><Mail/><small>Email the school</small><b>theglowupschool@gmail.com</b><ArrowRight/></a><a className={spotlight.className} onMouseMove={spotlight.onMouseMove} href="https://maps.app.goo.gl/sebTFqndTd2sGoez9" target="_blank" rel="noopener noreferrer"><MapPin/><small>Visit us</small><b>Adenta–Pantang, Accra</b><span>Open the school’s verified directions</span><ArrowRight/></a><div className={spotlight.className} onMouseMove={spotlight.onMouseMove}><Clock/><small>School hours</small><b>Monday–Friday</b><span>8:00 AM–3:00 PM</span></div></section></>; }
function FAQItem({ q, a }: { q: string; a: string }) {
  const [open, setOpen] = useState(false);
  const answerId = React.useId();
  return <article className={`faq-item${open ? " is-open" : ""}`}>
    <button className="faq-question" onClick={() => setOpen(!open)} aria-expanded={open} aria-controls={answerId}>
      <span>{q}</span><ArrowDown size={16} />
    </button>
    {open && <p id={answerId} className="faq-answer" role="region">{a}</p>}
  </article>;
}
function FAQ() {
  return <><PageHero label="FAQ" title="Questions," accent="answered simply." copy="The things families ask us most before their first visit." image="/images/community.jpg" /><section className="faq-section section"><div className="faq-list">{faqs.map((f) => <FAQItem key={f.q} q={f.q} a={f.a} />)}</div></section><Callout title="Still have a question?" copy="Our admissions team is happy to help with anything not covered here." action="Contact us" to="/contact" /></>;
}

function PrivacyPolicy() {
  return <><PageHero label="Privacy" title="Your family's" accent="privacy matters." copy="How The Glow-Up International School collects, uses and protects information you share with us." image="/images/welcome.jpg" /><section className="policy-section section"><div className="policy-grid">
    <div className="policy-body">
      <p className="policy-effective"><strong>Effective date:</strong> 13 August 2026</p>
      <h2>Information we collect</h2>
      <p>When you submit an admissions enquiry, we collect the details you provide: parent or guardian name, email address, phone number, your child's age and your consent choice. We may also receive information you choose to send by email or share during a school visit.</p>
      <h2>How we use your information</h2>
      <p>We use this information to respond to your enquiry, discuss admissions, arrange visits and follow up where you have asked us to. We do not sell your information or share it with third parties for their own marketing.</p>
      <h2>Service providers and sharing</h2>
      <p>Information may be handled by service providers that support our website, communications or school administration. They should only process it on the school's instructions and with appropriate confidentiality and security safeguards. We may also disclose information where required by law or needed to protect a child's safety.</p>
      <h2>Data retention</h2>
      <p>We retain enquiry information only while it is needed for the admissions conversation, record-keeping or a legal obligation, then securely delete or anonymise it. Records for enrolled families may be subject to separate school retention requirements.</p>
      <h2>Security</h2>
      <p>We use reasonable organisational and technical safeguards designed to protect information from unauthorised access, loss or misuse. No internet transmission or storage system can be guaranteed completely secure.</p>
      <h2>Cookies and external services</h2>
      <p>This public website does not use advertising cookies. Links to Google Maps, the parent portal and other external services are governed by those services' own privacy notices.</p>
      <h2>Your rights</h2>
      <p>You may ask to access, correct or delete information we hold about you, or withdraw consent for future contact, by emailing <a href="mailto:theglowupschool@gmail.com">theglowupschool@gmail.com</a>. Some requests may be limited where the school must keep information for safety, contractual or legal reasons.</p>
      <h2>Changes to this notice</h2>
      <p>We may update this notice when our services or information practices change. The effective date above shows when this version took effect.</p>
      <h2>Contact us</h2>
      <p>Questions about this policy can be directed to The Glow-Up International School, Adenta–Pantang, Accra, Ghana, or by phone at <a href="tel:+233542973500">+233 (0) 54 297 3500</a>.</p>
    </div>
    <aside className="policy-summary">
      <h3>At a glance</h3>
      <ul>
        <li><ShieldCheck size={17} /><span>We only collect what's needed to respond to your enquiry.</span></li>
        <li><ShieldCheck size={17} /><span>We never sell or share your data for marketing.</span></li>
        <li><ShieldCheck size={17} /><span>You can request access, correction or deletion any time.</span></li>
      </ul>
      <p className="policy-note">This is a general privacy notice. It has not been reviewed by legal counsel and should be replaced with a policy prepared or approved by a qualified lawyer before relying on it for compliance purposes.</p>
    </aside>
  </div></section></>;
}

function ThankYou() {
  return <section className="thank-you"><span><Check /></span><div className="section-label">Enquiry received</div><h1>Thank you.</h1><p>Your enquiry has been received. A member of our admissions team will be in touch shortly. If your question is urgent, call us during school hours.</p><div className="actions"><Link className="pill pill-dark" to="/">Back home</Link><a className="pill pill-light" href="tel:+233542973500"><Phone size={17} /> Call admissions</a></div><nav className="completion-links" aria-label="Continue exploring"><Link to="/learning">Explore learning <ArrowRight size={15} /></Link><Link to="/school-life">See school life <ArrowRight size={15} /></Link><Link to="/faq">Read FAQs <ArrowRight size={15} /></Link></nav></section>;
}

function Callout({title,copy,action,to,href}:{title:string;copy:string;action:string;to?:string;href?:string}) { const inner=<>{action}<ArrowRight size={17}/></>; return <section className="global-callout"><div className="reveal"><h2 className="text-reveal reveal"><span>{title}</span></h2><p>{copy}</p></div>{to?<Link className="pill pill-dark reveal" to={to} style={{ ["--reveal-order" as string]: 1 }}>{inner}</Link>:<a className="pill pill-dark reveal" href={href} style={{ ["--reveal-order" as string]: 1 }}>{inner}</a>}</section>; }
function NotFound(){return <section className="not-found"><span>404 · Page not found</span><h1>This page wandered off.</h1><p>The address may have changed, or the page may no longer be available. Try one of these useful places.</p><div className="actions"><Link className="pill pill-dark" to="/"><ArrowLeft/>Back home</Link><Link className="pill pill-light" to="/contact">Contact the school <ArrowRight size={17}/></Link></div><nav className="completion-links" aria-label="Popular pages"><Link to="/learning">Learning programmes <ArrowRight size={15}/></Link><Link to="/admissions">Admission steps <ArrowRight size={15}/></Link><Link to="/gallery">School gallery <ArrowRight size={15}/></Link><Link to="/faq">Frequently asked questions <ArrowRight size={15}/></Link></nav></section>}
function Footer(){return <footer><div className="footer-main"><div><Link className="brand footer-brand" to="/"><img src="/images/logo.png" alt=""/><span><strong>The Glow-Up</strong><small>International School</small></span></Link><p>Where little minds grow, glow and become ready for a brilliant future.</p></div><div className="footer-links"><div><b>Explore</b><Link to="/about">About Us</Link><Link to="/learning">Learning</Link><Link to="/school-life">School Life</Link><Link to="/admissions">Admission</Link><Link to="/gallery">Gallery</Link><Link to="/parents">Parents</Link><Link to="/faq">FAQ</Link></div><div><b>Connect</b><span>Adenta–Pantang, Accra</span><a href="tel:+233542973500">+233 (0) 54 297 3500</a><a href="mailto:theglowupschool@gmail.com">theglowupschool@gmail.com</a><a href="https://maps.app.goo.gl/sebTFqndTd2sGoez9" target="_blank" rel="noopener noreferrer">Google Maps directions</a>{config.parentPortalUrl?<a href={config.parentPortalUrl}>Parent portal</a>:<Link to="/contact">Request portal access</Link>}<Link to="/privacy-policy">Privacy Policy</Link></div></div></div><div className="footer-bottom"><span>© 2026 The Glow-Up International School</span><a className="sys-credit" href="https://www.smileyouresecuredsystems.com/" target="_blank" rel="noopener noreferrer">Designed &amp; developed by <strong>SYS Systems</strong> <ArrowRight size={13}/></a></div></footer>}

ReactDOM.createRoot(document.getElementById("root")!).render(<React.StrictMode><Layout /></React.StrictMode>);
