<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script>
// Swallow the benign "ResizeObserver loop completed with undelivered
// notifications" browser quirk so it doesn't surface as a console error.
(function(){
  var re = /ResizeObserver loop/;
  window.addEventListener('error', function(e){
    if (e && e.message && re.test(e.message)) { e.stopImmediatePropagation(); e.preventDefault(); return true; }
  }, true);
}());
</script>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Broadacres Academy — Learning, naturally.</title>

<!-- ── SEO / AI-search (static so crawlers read without running JS) ──────── -->
<meta name="description" content="Independent school in Broadacres, Johannesburg. Preschool to Grade 12 across three campuses — CAPS and an IEB matric, children known by name. Book a walkabout.">
<link rel="canonical" href="https://broadacres.com/">
<meta name="robots" content="index, follow">

<!-- Icons / PWA / theme -->
<link rel="icon" href="assets/favicon-32.png?v=2" sizes="32x32" type="image/png">
<link rel="icon" href="assets/favicon-16.png?v=2" sizes="16x16" type="image/png">
<link rel="apple-touch-icon" href="assets/apple-touch-icon.png?v=2">
<link rel="manifest" href="site.webmanifest">
<meta name="theme-color" content="#001D39">
<meta name="apple-mobile-web-app-title" content="Broadacres">

<!-- Speed up the largest hero image (LCP) -->
<link rel="preload" as="image" href="assets/photos/web/hero-forest-girl.jpg" fetchpriority="high">

<script>
  /* Keep the Vercel preview out of search results so it never competes with the
     production domain. Production (broadacres.com) stays fully indexable. */
  if (location.hostname.endsWith('.vercel.app')) {
    var m = document.createElement('meta');
    m.name = 'robots'; m.content = 'noindex, nofollow';
    document.head.appendChild(m);
  }
</script>

<!-- Open Graph (link previews on WhatsApp / Facebook / LinkedIn) -->
<meta property="og:type" content="website">
<meta property="og:site_name" content="Broadacres Academy">
<meta property="og:title" content="Broadacres Academy — Learning, naturally.">
<meta property="og:description" content="Independent school in Broadacres, Johannesburg. Preschool to Grade 12 across three campuses — CAPS and an IEB matric, children known by name.">
<meta property="og:url" content="https://broadacres.com/">
<meta property="og:image" content="https://broadacres.com/assets/og-image.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:locale" content="en_ZA">

<!-- Twitter / X card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Broadacres Academy — Learning, naturally.">
<meta name="twitter:description" content="Independent school in Broadacres, Johannesburg. Preschool to Grade 12 across three campuses — CAPS and an IEB matric, children known by name.">
<meta name="twitter:image" content="https://broadacres.com/assets/og-image.jpg">

<!-- School structured data (JSON-LD) — primary signal for AI answer engines.
     Keep addresses/phone/email in sync with the Contact page. -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "School",
  "name": "Broadacres Academy",
  "url": "https://broadacres.com/",
  "logo": "https://broadacres.com/assets/logo-color.png",
  "image": "https://broadacres.com/assets/og-image.jpg",
  "description": "An independent academy in Broadacres, Johannesburg, offering Preschool through Grade 12 across three campuses. CAPS and an internationally benchmarked IEB matric, with children known by name.",
  "telephone": "+27102711700",
  "email": "apply@broadacres.com",
  "areaServed": "Johannesburg, South Africa",
  "department": [
    {
      "@type": "School",
      "name": "Broadacres Academy — Syringa Campus (Preschool & Junior Prep)",
      "address": { "@type": "PostalAddress", "streetAddress": "28 Syringa Avenue (off Cedar Road)", "addressLocality": "Broadacres", "addressRegion": "Gauteng", "postalCode": "2021", "addressCountry": "ZA" },
      "geo": { "@type": "GeoCoordinates", "latitude": -25.9967682, "longitude": 27.9912658 }
    },
    {
      "@type": "School",
      "name": "Broadacres Academy — Chartwell Campus (Senior Prep & High School)",
      "address": { "@type": "PostalAddress", "streetAddress": "249 Fifth Street / 88 Fifth Street (access via Spencer Road)", "addressLocality": "Chartwell", "addressRegion": "Gauteng", "postalCode": "2055", "addressCountry": "ZA" },
      "geo": { "@type": "GeoCoordinates", "latitude": -25.982547, "longitude": 27.9681049 }
    }
  ]
}
</script>

<!-- FAQ structured data (FAQPage JSON-LD). Static so AI answer engines + Google
     read it without running JS. The visible FAQ accordion on the About page is
     rendered from the same content (window.__FAQS below) so the two stay in sync. -->
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "What ages and grades does Broadacres Academy take?",
      "acceptedAnswer": { "@type": "Answer", "text": "Broadacres takes children from six months old through to matric. Preschool runs from 6 months to Grade 00, Junior Prep covers Grade 0 to Grade 3, Senior Prep covers Grade 4 to Grade 6, and High School runs from Grade 7 to Grade 12." }
    },
    {
      "@type": "Question",
      "name": "What curriculum does Broadacres follow?",
      "acceptedAnswer": { "@type": "Answer", "text": "Broadacres is a CAPS and IEB school. CAPS anchors the Preparatory years, and the High School leads to an IEB matric — the National Senior Certificate written through the IEB, an internationally benchmarked qualification accepted at universities in South Africa and abroad." }
    },
    {
      "@type": "Question",
      "name": "Does Broadacres offer the IEB matric?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes. Broadacres High School students sit the South African National Senior Certificate under the IEB (Independent Examinations Board), a nationally recognised and internationally benchmarked matric." }
    },
    {
      "@type": "Question",
      "name": "How much are the fees at Broadacres Academy?",
      "acceptedAnswer": { "@type": "Answer", "text": "For 2026, fees start at around R5 725 per month for Preschool, R7 300 for Junior Prep, R8 000 for Senior Prep and R8 325 for High School. Discounts are available for siblings and for fees paid upfront, and fees can be paid annually, half-yearly or monthly. The full fee schedule is published at fees.broadacres.com." }
    },
    {
      "@type": "Question",
      "name": "Where are the Broadacres campuses?",
      "acceptedAnswer": { "@type": "Answer", "text": "Broadacres has three campuses a short drive apart, all in Johannesburg. Preschool and Junior Prep share the Syringa campus at 28 Syringa Avenue (off Cedar Road), Broadacres. Senior Prep and High School sit opposite each other in Chartwell, on Fifth Street (access via Spencer Road)." }
    },
    {
      "@type": "Question",
      "name": "What are the school hours?",
      "acceptedAnswer": { "@type": "Answer", "text": "Hours vary by phase. Preschool runs roughly 08:00–12:30 (with age splits), Junior Prep 07:30–13:30 or 14:00, Senior Prep 07:30–14:00, and High School 07:30–14:30 (ending 13:30 on Wednesdays and Fridays). Aftercare runs to 17:00, with gates closing at 17:30." }
    },
    {
      "@type": "Question",
      "name": "How many terms are in the Broadacres school year?",
      "acceptedAnswer": { "@type": "Answer", "text": "Broadacres runs a three-term academic year. The current calendar is published at calendar.broadacres.com." }
    },
    {
      "@type": "Question",
      "name": "Can I visit the school or book a walkabout?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes. Broadacres runs small-group, Campus Head-led walkabouts every week: Preschool on Tuesdays at 09:00 and Junior Prep on Thursdays at 09:00 at the Syringa campus, Senior Prep on Fridays at 09:00 at the Chartwell campus, and High School by appointment. Curious families are welcome — book a walkabout to come and see." }
    },
    {
      "@type": "Question",
      "name": "Does Broadacres offer aftercare and a holiday programme?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes. Aftercare is available for children up to Grade 7, running to 17:30 through our in-house programme, Our Little Village. A holiday care programme runs during school breaks, with a few closed days over December and January." }
    },
    {
      "@type": "Question",
      "name": "Is there a bus service?",
      "acceptedAnswer": { "@type": "Answer", "text": "Yes, on selected routes across Broadacres, Fourways, Dainfern and the surrounding areas, for the Preparatory and High School phases. Routes and capacity change year to year, so please speak to admissions for the current options. Preschool families arrange their own transport, with an inter-campus shuttle available for those with older siblings." }
    },
    {
      "@type": "Question",
      "name": "What is Learning, naturally?",
      "acceptedAnswer": { "@type": "Answer", "text": "Learning, naturally is the Broadacres teaching philosophy, used across every phase. It is a relationship-centred approach grounded in the Vygotskian tradition: teachers work alongside each child, pitching tasks just beyond what they can already do, with real materials, real tasks and language-rich classrooms where play is treated as serious work." }
    }
  ]
}
</script>

<link rel="stylesheet" href="styles/faq.css?v=2">
<link rel="stylesheet" href="styles/site.css?v=144">
<link rel="stylesheet" href="styles/offer.css?v=17">
<link rel="stylesheet" href="styles/phases.css?v=27">
<link rel="stylesheet" href="styles/preschool.css?v=9">
<link rel="stylesheet" href="styles/juniorprep.css?v=3">
<link rel="stylesheet" href="styles/seniorprep.css?v=3">
<link rel="stylesheet" href="styles/blog.css?v=5">
<link rel="stylesheet" href="styles/contact.css?v=6">
<meta name="ext-resource-dependency" content="assets/photos/hs-students-cutout.png">
<meta name="ext-resource-dependency" content="assets/photos/preschool-cutout.png">
<meta name="ext-resource-dependency" content="assets/photos/preschool-cutout-waist.png">
<meta name="ext-resource-dependency" content="assets/photos/preschool-hero-bg.jpg">
<meta name="ext-resource-dependency" content="assets/photos/preschool-natural.jpg">
<meta name="ext-resource-dependency" content="assets/photos/preschool-play.jpg">
<meta name="ext-resource-dependency" content="assets/photos/preschool-known.jpg">
<meta name="ext-resource-dependency" content="assets/photos/preschool-walkabout.jpg">
<meta name="ext-resource-dependency" content="assets/photos/jp-cutout.png">
<meta name="ext-resource-dependency" content="assets/photos/jp-cutout-waist.png">
<meta name="ext-resource-dependency" content="assets/photos/jp-hero-bg.jpg">
<meta name="ext-resource-dependency" content="assets/photos/jp-foundations.jpg">
<meta name="ext-resource-dependency" content="assets/photos/jp-curiosity.jpg">
<meta name="ext-resource-dependency" content="assets/photos/jp-movement.jpg">
<meta name="ext-resource-dependency" content="assets/photos/jp-walkabout.jpg">
<meta name="ext-resource-dependency" content="assets/photos/sp-cutout.png">
<meta name="ext-resource-dependency" content="assets/photos/sp-cutout-waist.png">
<meta name="ext-resource-dependency" content="assets/photos/sp-hero-bg.jpg">
<meta name="ext-resource-dependency" content="assets/photos/sp-forest.jpg">
<meta name="ext-resource-dependency" content="assets/photos/sp-friendships.jpg">
<meta name="ext-resource-dependency" content="assets/photos/sp-reading.jpg">
<meta name="ext-resource-dependency" content="assets/photos/sp-walkabout.jpg">
<meta name="ext-resource-dependency" content="assets/photos/hs-soccer.jpg">
<!-- Explicit asset dependencies (referenced dynamically in JSX) -->
<meta name="ext-resource-dependency" content="assets/logo-white-transparent.png">
<meta name="ext-resource-dependency" content="assets/photos/web/hero-forest-girl.jpg">
<meta name="ext-resource-dependency" content="assets/photos/web/about-hero-house.jpg">
<meta name="ext-resource-dependency" content="assets/photos/web/about-hero-stumps.jpg">
<meta name="ext-resource-dependency" content="assets/photos/hero-forest-girl-cutout.png">
<meta name="ext-resource-dependency" content="assets/photos/hero-running.png">
<meta name="ext-resource-dependency" content="assets/photos/web/students-highschool-pair.jpg">
<meta name="ext-resource-dependency" content="assets/photos/web/campus-gathering.jpg">
<meta name="ext-resource-dependency" content="assets/photos/learning-naturally-girl.png">
<meta name="ext-resource-dependency" content="assets/photos/natural-beginning.png">
<meta name="ext-resource-dependency" content="assets/photos/natural-learning-deepens.png">
<meta name="ext-resource-dependency" content="assets/phase-preschool.jpg">
<meta name="ext-resource-dependency" content="assets/phase-junior.jpg">
<meta name="ext-resource-dependency" content="assets/phase-senior.jpg">
<meta name="ext-resource-dependency" content="assets/phase-high.jpg">
<meta name="ext-resource-dependency" content="assets/photo-hs-walking.jpg">
<meta name="ext-resource-dependency" content="assets/photo-hs-chatting.jpg">
<meta name="ext-resource-dependency" content="assets/story-video-placeholder.png">
<meta name="ext-resource-dependency" content="assets/dandelion-mint.png">
<meta name="ext-resource-dependency" content="assets/dandelion-navy.png">
<meta name="ext-resource-dependency" content="assets/dandelion-navy-transparent.png">
<meta name="ext-resource-dependency" content="assets/dandelion-red.png">
<meta name="ext-resource-dependency" content="assets/dandelion-teal.png">
<meta name="ext-resource-dependency" content="assets/dandelion-white.png">
<meta name="ext-resource-dependency" content="assets/dandelion-white-transparent.png">
<meta name="ext-resource-dependency" content="assets/photos/web/offer-hero-reading-circle.jpg">
<meta name="ext-resource-dependency" content="assets/photo-prep-laughing.jpg">
<style>
  /* Route transition */
  .route-fade { animation: routeFade 420ms cubic-bezier(.22,.61,.36,1) both; }
  @keyframes routeFade { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
</style>
</head>
<body>
<template id="__bundler_thumbnail">
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400">
    <rect width="400" height="400" fill="#001D39"/>
    <g transform="translate(200 200)" fill="none" stroke="#53F5C4" stroke-width="1.4" stroke-linecap="round" opacity="0.9">
      <circle r="4" fill="#53F5C4" stroke="none"/>
      <g id="s">
        <line y2="-70"/>
        <circle cy="-74" r="3" fill="#53F5C4" stroke="none"/>
      </g>
      <use href="#s" transform="rotate(30)"/>
      <use href="#s" transform="rotate(60)"/>
      <use href="#s" transform="rotate(90)"/>
      <use href="#s" transform="rotate(120)"/>
      <use href="#s" transform="rotate(150)"/>
      <use href="#s" transform="rotate(180)"/>
      <use href="#s" transform="rotate(210)"/>
      <use href="#s" transform="rotate(240)"/>
      <use href="#s" transform="rotate(270)"/>
      <use href="#s" transform="rotate(300)"/>
      <use href="#s" transform="rotate(330)"/>
    </g>
    <text x="200" y="340" font-family="Georgia, serif" font-size="22" fill="#fff" text-anchor="middle" font-style="italic">Broadacres Academy</text>
  </svg>
</template>
<div id="root"></div>

<!-- Tweaks panel markup (lives outside React root so toggling doesn't remount) -->
<div id="tweaks-root"></div>

<script>
// Persistence
window.__loadTweaks = function() {
  try {
    const saved = JSON.parse(localStorage.getItem('ba_tweaks_v2') || 'null');
    if (saved) return saved;
  } catch(e) {}
  return /*EDITMODE-BEGIN*/{
    "heroVariant": "portrait",
    "accent": "teal",
    "dandelion": true,
    "hsHero": "natural",
    "blogLayout": "editorial"
  }/*EDITMODE-END*/;
};
window.__saveTweaks = function(t) {
  localStorage.setItem('ba_tweaks_v2', JSON.stringify(t));
};
// Route persistence
window.__loadRoute = function() {
  return localStorage.getItem('ba_route') || 'home';
};
// Walkabout booking link — single source of truth for every "Book a walkabout" button
window.WALKABOUT_URL = 'https://bookings.cloud.microsoft/book/Walkabouts1@thriveed.co/?ismsaljsauthenabled=true';
window.__bookWalkabout = function() {
  // Use a real anchor click rather than window.open() — far less likely to be
  // treated as a blockable popup. (In the sandboxed design preview new tabs are
  // blocked regardless; on the live deployed site this opens normally.)
  var a = document.createElement('a');
  a.href = window.WALKABOUT_URL;
  a.target = '_blank';
  a.rel = 'noopener noreferrer';
  document.body.appendChild(a);
  a.click();
  a.remove();
};

// Single source for the visible FAQ accordion (About page). The text here MUST
// match the FAQPage JSON-LD in <head> so Google's FAQ rich-result rules are met.
window.__FAQS = [
  { cat: 'Admissions & structure', q: 'What ages and grades does Broadacres Academy take?',
    a: 'Broadacres takes children from six months old through to matric. Preschool runs from 6 months to Grade 00, Junior Prep covers Grade 0 to Grade 3, Senior Prep covers Grade 4 to Grade 6, and High School runs from Grade 7 to Grade 12.' },
  { cat: 'Admissions & structure', q: 'What curriculum does Broadacres follow?',
    a: 'Broadacres is a CAPS and IEB school. CAPS anchors the Preparatory years, and the High School leads to an IEB matric — the National Senior Certificate written through the IEB, an internationally benchmarked qualification accepted at universities in South Africa and abroad.' },
  { cat: 'Admissions & structure', q: 'Does Broadacres offer the IEB matric?',
    a: 'Yes. Broadacres High School students sit the South African National Senior Certificate under the IEB (Independent Examinations Board), a nationally recognised and internationally benchmarked matric.' },
  { cat: 'Admissions & structure', q: 'How much are the fees at Broadacres Academy?',
    a: 'For 2026, fees start at around R5 725 per month for Preschool, R7 300 for Junior Prep, R8 000 for Senior Prep and R8 325 for High School. Discounts are available for siblings and for fees paid upfront, and fees can be paid annually, half-yearly or monthly. The full fee schedule is published at <a href="https://fees.broadacres.com" target="_blank" rel="noopener noreferrer">fees.broadacres.com</a>.' },
  { cat: 'Admissions & structure', q: 'What are the school hours?',
    a: 'Hours vary by phase. Preschool runs roughly 08:00–12:30 (with age splits), Junior Prep 07:30–13:30 or 14:00, Senior Prep 07:30–14:00, and High School 07:30–14:30 (ending 13:30 on Wednesdays and Fridays). Aftercare runs to 17:00, with gates closing at 17:30.' },
  { cat: 'Admissions & structure', q: 'How many terms are in the Broadacres school year?',
    a: 'Broadacres runs a three-term academic year. The current calendar is published at <a href="https://calendar.broadacres.com" target="_blank" rel="noopener noreferrer">calendar.broadacres.com</a>.' },
  { cat: 'Campuses & visits', q: 'Where are the Broadacres campuses?',
    a: 'Broadacres has three campuses a short drive apart, all in Johannesburg. Preschool and Junior Prep share the Syringa campus at 28 Syringa Avenue (off Cedar Road), Broadacres. Senior Prep and High School sit opposite each other in Chartwell, on Fifth Street (access via Spencer Road).' },
  { cat: 'Campuses & visits', q: 'Can I visit the school or book a walkabout?',
    a: 'Yes. Broadacres runs small-group, Campus Head-led walkabouts every week: Preschool on Tuesdays at 09:00 and Junior Prep on Thursdays at 09:00 at the Syringa campus, Senior Prep on Fridays at 09:00 at the Chartwell campus, and High School by appointment. Curious families are welcome — book a walkabout to come and see.' },
  { cat: 'School life & practicalities', q: 'Does Broadacres offer aftercare and a holiday programme?',
    a: 'Yes. Aftercare is available for children up to Grade 7, running to 17:30 through our in-house programme, Our Little Village. A holiday care programme runs during school breaks, with a few closed days over December and January.' },
  { cat: 'School life & practicalities', q: 'Is there a bus service?',
    a: 'Yes, on selected routes across Broadacres, Fourways, Dainfern and the surrounding areas, for the Preparatory and High School phases. Routes and capacity change year to year, so please <a href="mailto:apply@broadacres.com">speak to admissions</a> for the current options. Preschool families arrange their own transport, with an inter-campus shuttle available for those with older siblings.' },
  { cat: 'School life & practicalities', q: 'What is Learning, naturally?',
    a: '<em>Learning, naturally</em> is the Broadacres teaching philosophy, used across every phase. It is a relationship-centred approach grounded in the Vygotskian tradition: teachers work alongside each child, pitching tasks just beyond what they can already do, with real materials, real tasks and language-rich classrooms where play is treated as serious work.' }
];
</script>

<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>

<script src="components/image-slot.js"></script>
<script type="text/babel" src="components/Dandelion.jsx"></script>
<script type="text/babel" src="components/PhaseGallery.jsx"></script>
<script type="text/babel" src="components/SubNav.jsx"></script>
<script type="text/babel" src="components/DayRhythm.jsx"></script>
<script type="text/babel" src="components/Footer.jsx?v=2"></script>
<script type="text/babel" src="components/Header.jsx?v=3"></script>
<script type="text/babel" src="components/Home.jsx"></script>
<script type="text/babel" src="components/About.jsx"></script>
<script type="text/babel" src="components/Offer.jsx?v=3"></script>
<script type="text/babel" src="components/HighSchool.jsx?v=2"></script>
<script type="text/babel" src="components/Preschool.jsx"></script>
<script type="text/babel" src="components/JuniorPrep.jsx"></script>
<script type="text/babel" src="components/SeniorPrep.jsx"></script>
<script type="text/babel" src="components/Blog.jsx?v=3"></script>
<script type="text/babel" src="components/Contact.jsx?v=4"></script>
<script type="text/babel" src="components/FaqSection.jsx"></script>

<script type="text/babel">
// --- Real-URL routing (History API) -----------------------------------------
const ROUTES = ['home','about','offer','preschool','juniorprep','seniorprep','highschool','blog','contact'];
function parsePath() {
  let p = (window.location.pathname || '/').replace(/^\/+|\/+$/g, '');
  // Back-compat: honour a legacy #hash link (e.g. an old /#about bookmark)
  if (!p) { const h = (window.location.hash || '').replace(/^#\/?/, ''); if (h) p = h; }
  if (!p) return { route: 'home', blogSlug: null };
  if (p === 'blog') return { route: 'blog', blogSlug: null };
  if (p.indexOf('blog/') === 0) return { route: 'blog', blogSlug: p.substring(5) };
  if (ROUTES.indexOf(p) !== -1) return { route: p, blogSlug: null };
  return { route: 'home', blogSlug: null };
}
function routeToPath(route, blogSlug) {
  if (route === 'blog' && blogSlug) return '/blog/' + blogSlug;
  if (route === 'home') return '/';
  return '/' + route;
}
window.routeToPath = routeToPath;

function App() {
  const __init = parsePath();
  const [route, setRoute] = React.useState(__init.route);
  const [tweaks, setTweaks] = React.useState(window.__loadTweaks());
  const [editMode, setEditMode] = React.useState(false);
  const [blogSlug, setBlogSlug] = React.useState(__init.blogSlug);

  React.useEffect(() => {
    // Stop the browser auto-restoring each hash entry's previous scroll position
    // on navigation — that was overriding our scroll-to-top below.
    if ('scrollRestoration' in history) history.scrollRestoration = 'manual';
    localStorage.setItem('ba_route', route);
    // Reset scroll to top on every route change. Use the two-arg form (some
    // mobile browsers ignore scrollTo({top}) ) and reset the scrolling element
    // directly; run again on the next frame so it wins after the mobile menu's
    // overflow-lock is released and the new <main> has committed.
    const toTop = () => {
      window.scrollTo(0, 0);
      if (document.scrollingElement) document.scrollingElement.scrollTop = 0;
    };
    toTop();
    requestAnimationFrame(toTop);
  }, [route]);

  // §4 — Per-page <title> + meta description so crawlers (and AI engines) can
  // distinguish each route, and so address-bar / share titles read correctly.
  React.useEffect(() => {
    const META = {
      home:       ['Broadacres Academy — Learning, naturally.', 'Independent school in Broadacres, Johannesburg. Preschool to Grade 12 across three campuses — CAPS and an IEB matric, children known by name. Book a walkabout.'],
      about:      ['About — Broadacres Academy', 'Who we are and how we teach: an independent academy in Broadacres, Johannesburg where children are known by name, learning through three connected campuses.'],
      offer:      ['What We Offer — Broadacres Academy', 'Our curriculum and approach from Preschool to Grade 12 — CAPS foundations and an internationally benchmarked IEB matric at Broadacres Academy.'],
      preschool:  ['Preschool — Broadacres Academy', 'A gentle, play-based start for our youngest learners on the Syringa Campus in Broadacres, Johannesburg — children known by name from day one.'],
      juniorprep: ['Junior Prep — Broadacres Academy', 'Junior Prep at Broadacres Academy: strong foundations, curiosity and movement on the Syringa Campus in Broadacres, Johannesburg.'],
      seniorprep: ['Senior Prep — Broadacres Academy', 'Senior Prep at Broadacres Academy on the Chartwell Campus — confident, capable learners growing in The Forest, Johannesburg.'],
      highschool: ['High School (Gr 7–12, IEB matric) — Broadacres Academy', 'High School at Broadacres Academy: Grades 7–12 leading to an internationally benchmarked IEB matric on the Chartwell Campus, Johannesburg.'],
      blog:       ['Stories & News — Broadacres Academy', 'Stories, news and answers from Broadacres Academy — fees, the IEB matric, ages and campuses, written for families in Johannesburg.'],
      contact:    ['Contact & Walkabouts — Broadacres Academy', 'Get in touch with Broadacres Academy or book a walkabout across our three campuses in Broadacres and Chartwell, Johannesburg.'],
    };
    const [title, desc] = META[route] || META.home;
    document.title = title;
    let m = document.querySelector('meta[name="description"]');
    if (!m) { m = document.createElement('meta'); m.setAttribute('name', 'description'); document.head.appendChild(m); }
    m.setAttribute('content', desc);
  }, [route, blogSlug]);

  // Keep the address bar in sync with the current page (real URLs via the
  // History API) so links are shareable and back/forward work. On first mount
  // we only clean the URL (replaceState) — e.g. a legacy /#about link becomes
  // /about — without adding a history entry; later changes push a new entry.
  const firstSync = React.useRef(true);
  React.useEffect(() => {
    const path = routeToPath(route, blogSlug);
    if (firstSync.current) {
      firstSync.current = false;
      if (window.location.pathname !== path || window.location.hash) {
        history.replaceState({ route, blogSlug }, '', path);
      }
      return;
    }
    if (window.location.pathname !== path) {
      history.pushState({ route, blogSlug }, '', path);
    }
  }, [route, blogSlug]);

  // Listen for browser back/forward (popstate) and postMessage route changes
  // (the design preview uses postMessage).
  React.useEffect(() => {
    const onPop = () => {
      const parsed = parsePath();
      setRoute(parsed.route);
      setBlogSlug(parsed.blogSlug);
    };
    const onRouteMsg = (e) => {
      const d = e.data || {};
      if (d.type === 'ba_set_route' && ROUTES.indexOf(d.route) !== -1) {
        setRoute(d.route);
        setBlogSlug(null);
      }
    };
    window.addEventListener('popstate', onPop);
    window.addEventListener('message', onRouteMsg);
    return () => {
      window.removeEventListener('popstate', onPop);
      window.removeEventListener('message', onRouteMsg);
    };
  }, []);
  React.useEffect(() => { window.__saveTweaks(tweaks); }, [tweaks]);

  // Tweaks host protocol
  React.useEffect(() => {
    const onMsg = (e) => {
      const d = e.data || {};
      if (d.type === '__activate_edit_mode') setEditMode(true);
      else if (d.type === '__deactivate_edit_mode') setEditMode(false);
    };
    window.addEventListener('message', onMsg);
    window.parent.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const setTweak = (k, v) => setTweaks(prev => {
    const next = { ...prev, [k]: v };
    window.parent.postMessage({ type: '__edit_mode_set_keys', edits: next }, '*');
    return next;
  });

  // Scroll reveal
  React.useEffect(() => {
    const els = document.querySelectorAll('.reveal');
    const io = new IntersectionObserver((entries) => {
      entries.forEach(en => { if (en.isIntersecting) en.target.classList.add('in'); });
    }, { threshold: 0.12 });
    els.forEach(el => io.observe(el));
    return () => io.disconnect();
  });

  return (
    <>
      <Header route={route} onNavigate={setRoute} forceScrolled={route === 'blog' && !!blogSlug}/>
      <main key={route} className="route-fade" data-screen-label={route === 'highschool' ? '06 High School' : route === 'preschool' ? '03 Preschool' : route === 'juniorprep' ? '04 Junior Prep' : route === 'seniorprep' ? '05 Senior Prep' : route === 'about' ? '02 About' : route === 'offer' ? '05 What we offer' : route === 'blog' ? '07 Blog' : route === 'contact' ? '08 Contact' : '01 Home'}>
        {route === 'home' && <Home onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'about' && <About onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'offer' && <Offer onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'highschool' && <HighSchool onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'preschool' && <Preschool onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'juniorprep' && <JuniorPrep onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'seniorprep' && <SeniorPrep onNavigate={setRoute} tweaks={tweaks}/>}
        {route === 'blog' && <Blog onNavigate={setRoute} tweaks={tweaks} blogSlug={blogSlug} setBlogSlug={setBlogSlug}/>}
        {route === 'contact' && <Contact onNavigate={setRoute} tweaks={tweaks}/>}
      </main>

      {editMode && <TweaksPanel tweaks={tweaks} setTweak={setTweak} />}
    </>
  );
}

function TweaksPanel({ tweaks, setTweak }) {
  return (
    <div className="tweaks-panel open">
      <h4>Tweaks</h4>
      <div className="tweak-row">
        <label>Hero photo</label>
        <div className="choices">
          {[['portrait','Forest'],['running','Running'],['studio','Studio']].map(([v,l]) => (
            <button key={v} className={`chip ${tweaks.heroVariant===v?'active':''}`} onClick={() => setTweak('heroVariant', v)}>{l}</button>
          ))}
        </div>
      </div>
      <div className="tweak-row">
        <label>Accent</label>
        <div className="choices" style={{gap:8}}>
          {[['teal','#039CAC'],['mint','#1CBC91'],['red','#E20630']].map(([v,c]) => (
            <button key={v} className={`swatch ${tweaks.accent===v?'active':''}`} onClick={() => setTweak('accent', v)} style={{background:c}} aria-label={v}/>
          ))}
        </div>
      </div>
      <div className="tweak-row">
        <label>HS Hero</label>
        <div className="choices">
          {[['wedge','Wedge'],['natural','Natural'],['duotone','Duotone'],['postcard','Postcard'],['portrait','Portrait']].map(([v,l]) => (
            <button key={v} className={`chip ${tweaks.hsHero===v?'active':''}`} onClick={() => setTweak('hsHero', v)}>{l}</button>
          ))}
        </div>
      </div>
      <div className="tweak-row">
        <label>Blog layout</label>
        <div className="choices">
          {[['editorial','Editorial'],['uniform','Uniform grid']].map(([v,l]) => (
            <button key={v} className={`chip ${tweaks.blogLayout===v?'active':''}`} onClick={() => setTweak('blogLayout', v)}>{l}</button>
          ))}
        </div>
      </div>
      <div className="tweak-row">
        <label>Dandelion</label>
        <div className="choices">
          <button className={`chip ${tweaks.dandelion?'active':''}`} onClick={() => setTweak('dandelion', true)}>On</button>
          <button className={`chip ${!tweaks.dandelion?'active':''}`} onClick={() => setTweak('dandelion', false)}>Off</button>
        </div>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
</script>
<script src="reveal.js?v=6"></script>
</body>
</html>
