pages/ValuePartnership/ValuePartnership.jsxjavascript
import { lazy, Suspense, useState } from "react";
import { Navigate, Route, Routes, useLocation } from "react-router";

import { ValuePartnershipNav } from "./ValuePartnershipNav";
import { GlobePage } from "./GlobePage/GlobePage";
import "./ValuePartnership.scss";

// code-split: ValueExcellencePage pulls in venn-diagram-svg.js (a 312KB
// inline SVG string) and PartnershipsPage pulls in the `qrcode` library -
// neither is needed by a visitor who only looks at the globe (the default
// tab, see the index route below), so both load on demand instead of
// bloating the initial Value Partnership bundle
const ValueExcellencePage = lazy(() => import("./ValueExcellencePage/ValueExcellencePage"));
const PartnershipsPage = lazy(() => import("./PartnershipsPage/PartnershipsPage"));

/**
 * "Value Partnership" presentation app. Unlike ValueAdvisoryServices/
 * RadEnablement (linear SlidePage decks), this is a bespoke sub-app with 3
 * independent interactive sections (globe, excellence drivers, partnerships/
 * slideshow) sharing one nav bar and one content file - so it owns its own
 * nested route table instead of going through SlidePage.
 *
 * GlobePage is deliberately kept outside that route table and always
 * rendered (just hidden via CSS - see .value-partnership-page__globe--hidden
 * - while another tab is active) instead of being mounted/unmounted per
 * route like Excellence/Partnerships are. It owns a full Three.js/WebGL
 * scene (react-globe.gl); tearing that down and rebuilding it on every tab
 * switch is both slow and a known memory-leak risk class for this kind of
 * library on repeated unmount. GlobePage's own `active` prop drives pausing
 * its render loop while hidden instead (see InteractiveGlobe.jsx).
 */
export const ValuePartnershipPage = () => {
  const { pathname, search } = useLocation();
  // GlobePage's GlobeBottomNav (marker prev/next/home) and ValueExcellencePage's
  // ValueDriverNav each take over this same bottom-bar slot while a marker/driver
  // is selected - actually unmounting ValuePartnershipNav here (rather than just
  // painting over it) so there's no chance of it showing through underneath.
  const [suppressNav, setSuppressNav] = useState(false);
  // PartnershipsPage's "Show key slides"/"Show all presentations" buttons
  // render into ValuePartnershipNav itself (its `children` slot) rather than
  // in PartnershipsPage's own markup, so they read as part of the shared
  // bottom bar instead of floating over the page content.
  const [partnershipsActions, setPartnershipsActions] = useState(null);
  const isGlobeActive = pathname.endsWith("/globe");

  return (
    <div className="value-partnership-page page">
      <div
        className={
          isGlobeActive
            ? "value-partnership-page__globe"
            : "value-partnership-page__globe value-partnership-page__globe--hidden"
        }>
        <GlobePage active={isGlobeActive} onSelectionChange={setSuppressNav} />
      </div>
      <Suspense fallback={null}>
        <Routes>
          <Route index element={<Navigate to={`globe${search}`} replace />} />
          <Route path="globe" element={null} />
          <Route
            path="excellence"
            element={<ValueExcellencePage onSelectionChange={setSuppressNav} />}
          />
          <Route
            path="partnerships"
            element={<PartnershipsPage onActionsChange={setPartnershipsActions} />}
          />
        </Routes>
      </Suspense>
      {!suppressNav && <ValuePartnershipNav>{partnershipsActions}</ValuePartnershipNav>}
    </div>
  );
};