/* ============================================================
   shared.jsx — site chrome + content data for martinharrison.portfolio
   Composes window.DesignSystem_33e611 primitives. Exposed on window.
   ============================================================ */
const DS = window.DesignSystem_33e611;
const { Button, Card, Badge, GradientSpotlightCard, IconButton } = DS;

/* ---------------- Wordmark (text lockup) ---------------- */
function Mark({ size = 20, color = "var(--color-ink)", style, ...rest }) {
  return (
    <span style={{
      display: "inline-flex", alignItems: "baseline",
      fontFamily: "var(--font-display)", fontWeight: 500, fontSize: size,
      lineHeight: 1, letterSpacing: `${(-0.04 * size).toFixed(2)}px`, color, ...style
    }} {...rest}>
      martin&nbsp;harrison
    </span>);

}

/* ---------------- Scroll reveal ---------------- */
function Reveal({ children, delay = 0, as = "div", className = "", style }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const show = () => el.classList.add("is-in");
    // Hard-force visible without relying on the CSS transition (some preview
    // environments suspend transitions, which would leave content at opacity 0).
    const force = () => {el.classList.add("is-in");el.style.transition = "none";el.style.opacity = "1";el.style.transform = "none";};
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {force();return;}
    const r = el.getBoundingClientRect();
    if (r.top < window.innerHeight && r.bottom > 0) requestAnimationFrame(show);
    let io;
    if ("IntersectionObserver" in window) {
      io = new IntersectionObserver((entries) => {
        entries.forEach((e) => {if (e.isIntersecting) {show();io.unobserve(el);}});
      }, { threshold: 0.1, rootMargin: "0px 0px -6% 0px" });
      io.observe(el);
    }
    // Safety net: guarantee content is visible even if transitions never run.
    const t = setTimeout(force, 900 + delay);
    return () => {if (io) io.disconnect();clearTimeout(t);};
  }, [delay]);
  const Tag = as;
  return <Tag ref={ref} className={"reveal " + className} style={{ transitionDelay: delay + "ms", ...style }}>{children}</Tag>;
}

/* ---------------- Eyebrow ---------------- */
function Eyebrow({ children, noTick, style }) {
  return <span className={"eyebrow" + (noTick ? " no-tick" : "")} style={style}>{children}</span>;
}

/* ---------------- Top navigation ---------------- */
const NAV_LINKS = [
{ label: "Home", href: "/" },
{ label: "Work", href: "Work.html" },
{ label: "About", href: "About.html" }];

function Nav({ active }) {
  const [open, setOpen] = React.useState(false);
  return (
    <React.Fragment>
      <header className="nav">
        <a href="/" aria-label="Martin Harrison — home" style={{ display: "inline-flex" }}>
          <Mark size={18} />
        </a>
        <nav className="nav-links">
          {NAV_LINKS.map((l) =>
          <a key={l.label} href={l.href} className={"nav-link" + (l.label === active ? " active" : "")}>{l.label}</a>
          )}
        </nav>
        <div className="nav-right">
          <Button as="a" href="Contact.html" variant="primary" size="sm" className="nav-cta">Get in touch</Button>
          <span className="nav-burger" style={{ display: "none" }}>
            <IconButton ariaLabel="Menu" onClick={() => setOpen((v) => !v)}>{open ? "✕" : "≡"}</IconButton>
          </span>
        </div>
      </header>
      {open &&
      <div style={{
        position: "sticky", top: 64, zIndex: 49, background: "var(--color-surface-1)",
        borderBottom: "1px solid var(--color-hairline-soft)", padding: "var(--space-md) var(--space-xl)",
        display: "flex", flexDirection: "column", gap: 4
      }}>
          {NAV_LINKS.map((l) =>
        <a key={l.label} href={l.href} style={{
          padding: "12px 4px", color: l.label === active ? "var(--color-ink)" : "var(--color-ink-muted)",
          font: "var(--type-body-lg)", letterSpacing: "var(--ls-body-lg)", borderBottom: "1px solid var(--color-hairline-soft)"
        }}>{l.label}</a>
        )}
        </div>
      }
      <style>{`@media (max-width: 860px){ .nav-burger{ display:inline-flex !important; } }`}</style>
    </React.Fragment>);

}

/* ---------------- Conversion band (reused on every page) ---------------- */
function CTABand({
  title, // white lead of the headline
  body, // muted trail of the headline
  cta = "Start your project",
  href = "Contact.html",
  // accepted for backward-compatible call sites; not rendered in this layout
  eyebrow, tone, actions
}) {
  const lead = title || "Built for teams that need complex software made clear,";
  const trail = body || "trusted quickly, and easy to live with as it scales.";
  return (
    <section className="section cta-band">
      <div className="wrap">
        <div className="cta-grid">
          <Reveal>
            <h2 className="cta-headline fluid-lg">
              <span>{lead} </span>
              {trail ? <span className="muted">{trail}</span> : null}
            </h2>
          </Reveal>
          <Reveal delay={120}>
            <a href={href} className="cta-btn lift" aria-label={cta}>
              <span className="cta-btn-label">{cta}</span>
              <span className="cta-btn-arrow" aria-hidden="true">→</span>
            </a>
          </Reveal>
        </div>
      </div>
    </section>);

}

/* ---------------- Footer ---------------- */
function SiteFooter() {
  const colA = {
    title: "Explore",
    links: [["Work", "Work.html"], ["About", "About.html"], ["Contact", "Contact.html"], ["Product Design", "/#services"], ["Design Systems", "/#services"], ["Advisory & Audits", "/#services"]]
  };
  const colB = {
    title: "Connect",
    links: [["Email", "mailto:martinharrison27@gmail.com"], ["LinkedIn", "https://www.linkedin.com/in/martin-harrison-405aa529/"], ["Download résumé", "resume.pdf"]]
  };
  return (
    <footer className="site-footer">
      <div className="wrap">
        <div className="footer-top">
          <div className="footer-intro">
            <p className="footer-blurb">
              Senior Product Designer based in Bangalore, working with product teams worldwide on enterprise, GRC, and platform software.
            </p>
            <p className="footer-blurb muted">
              A small number of engagements each quarter — strategy through shipped pixels, systems built to scale.
            </p>
            <div className="footer-contact">
              <span className="footer-label">Contact</span>
              <a href="tel:+910000000000" className="footer-contact-line">+91 9962927800</a>
              <a href="mailto:martinharrison27@gmail.com" className="footer-contact-line">martinharrison27@gmail.com</a>
            </div>
          </div>
          <div className="footer-cols">
            {[colA, colB].map((c) =>
            <div key={c.title} className="footer-col">
                <span className="footer-label">{c.title}</span>
                {c.links.map(([l, h]) =>
              <a key={l} href={h} target={h.startsWith("http") ? "_blank" : undefined} rel={h.startsWith("http") ? "noopener noreferrer" : undefined} className="footer-link">{l}</a>
              )}
              </div>
            )}
          </div>
        </div>

        <div className="footer-bottom">
          <span className="footer-fine muted">© 2026 Martin Harrison · Senior Product Designer · Bangalore, India</span>
          <div className="footer-fine-links">
            <a href="Contact.html" className="muted">Privacy</a>
            <a href="Contact.html" className="muted">Terms</a>
            <span className="muted">Designed &amp; built on a dark canvas ♥</span>
          </div>
        </div>
      </div>
    </footer>);

}

/* ---------------- Project case-cover (designed tile, on-brand) ---------------- */
function ProjectCover({ project, aspect = "4 / 3", big = false, hideLabel = false, radius = "var(--radius-xl)" }) {
  const grounds = {
    violet: "var(--gradient-violet)", magenta: "var(--gradient-magenta)",
    orange: "var(--gradient-orange)", coral: "var(--gradient-coral)", blue: "radial-gradient(120% 120% at 20% 0%, #4db4ff 0%, #0099ff 40%, #064f86 100%)"
  };
  if (project.thumb) {
    return (
      <div style={{
        position: "relative", aspectRatio: aspect, width: "100%", overflow: "hidden",
        borderRadius: radius, background: "var(--color-surface-1)",
        border: "1px solid var(--color-hairline-soft)"
      }}>
        <img src={project.thumb} alt={project.name} loading="lazy" style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", transform: project.thumbScale ? "scale(" + project.thumbScale + ")" : undefined, transformOrigin: "center" }} />
        {!hideLabel &&
        <React.Fragment>
            <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, rgba(12,11,10,0) 40%, rgba(12,11,10,0.86) 100%)" }}></div>
            <div style={{ position: "absolute", left: 20, right: 20, bottom: 18 }}>
              <div style={{ font: "var(--type-caption)", letterSpacing: "0.14em", textTransform: "uppercase", color: "rgba(255,255,255,0.85)", marginBottom: 6 }}>{project.industry}</div>
              <div className={big ? "fluid-lg" : "fluid-md"} style={{ color: "#fff" }}>{project.name}</div>
            </div>
          </React.Fragment>
        }
      </div>);

  }
  return (
    <div style={{
      position: "relative", aspectRatio: aspect, width: "100%", overflow: "hidden",
      borderRadius: radius, background: "var(--color-surface-1)",
      border: "1px solid var(--color-hairline-soft)"
    }}>
      {/* atmospheric glow in the project tone */}
      <div style={{ position: "absolute", inset: 0, background: grounds[project.tone] || grounds.violet, opacity: 0.92 }}></div>
      <div style={{ position: "absolute", inset: 0, background: "linear-gradient(180deg, rgba(12,11,10,0.05) 0%, rgba(12,11,10,0.55) 70%, rgba(12,11,10,0.86) 100%)" }}></div>
      {/* faux app chrome strip */}
      <div style={{ position: "absolute", top: 16, left: 16, right: 16, display: "flex", alignItems: "center", gap: 10 }}>
        <span style={{ display: "flex", gap: 6 }}>
          {[0, 1, 2].map((i) => <span key={i} style={{ width: 8, height: 8, borderRadius: "50%", background: "rgba(255,255,255,0.45)" }}></span>)}
        </span>
        <span style={{ flex: 1, height: 18, borderRadius: 100, background: "rgba(255,255,255,0.14)" }}></span>
      </div>
      {/* abstract content lines */}
      <div style={{ position: "absolute", left: 20, right: 20, top: "44%", display: "flex", flexDirection: "column", gap: 9, opacity: 0.5 }}>
        <span style={{ height: 7, width: "62%", borderRadius: 100, background: "rgba(255,255,255,0.7)" }}></span>
        <span style={{ height: 7, width: "84%", borderRadius: 100, background: "rgba(255,255,255,0.4)" }}></span>
        <span style={{ height: 7, width: "48%", borderRadius: 100, background: "rgba(255,255,255,0.4)" }}></span>
      </div>
      {/* label */}
      {!hideLabel &&
      <div style={{ position: "absolute", left: 20, right: 20, bottom: 18 }}>
        <div style={{ font: "var(--type-caption)", letterSpacing: "0.14em", textTransform: "uppercase", color: "rgba(255,255,255,0.85)", marginBottom: 6 }}>{project.industry}</div>
        <div className={big ? "fluid-lg" : "fluid-md"} style={{ color: "#fff" }}>{project.name}</div>
      </div>
      }
    </div>);

}

/* ---------------- CONTENT DATA ---------------- */
const STATS = [
{ value: "14+", label: "Years designing products" },
{ value: "10+", label: "Products shipped to production" },
{ value: "5+", label: "Design systems built from scratch" },
{ value: "180+", label: "Countries reached at scale" }];


const PROJECTS = [
{
  id: "sentinel", name: "Reimagining Reporting", thumb: "site/reporting-dashboard-thumb.jpg", longform: "reporting", industry: "Power BI · Design Strategy", year: "2026", tone: "violet",
  role: "Lead Product Designer", duration: "2023–2026", team: "Dashboard branding · Design system", teamLabel: "Scope",
  metric: "", metricLabel: "",
  disciplines: ["Product Design", "Design Systems", "Enterprise UX"],
  summary: "The organization is migrating dashboards from Qlik to Power BI to modernize its analytics and user experience.",
  challenge: "The organization was migrating a large suite of dashboards from Qlik to Power BI. Each report had its own layout, chart formatting, and visual style, and the tool's limited customization meant every design decision had to be feasible for developers to build. The brief was to bring consistency, brand alignment, and a scalable reporting standard to the whole ecosystem.",
  process: [
  "Audited the existing dashboards and defined a reusable Power BI theme, brand-aligned palette, and standardized layouts.",
  "Built a shared style guide and component library so designers and developers worked from one foundation.",
  "Created a low-fidelity wireframe kit to keep early conversations focused on structure and flow.",
  "Published templates, themes, and how-to guides to a central hub to drive adoption across teams."],

  deliverables: ["Power BI theme & template", "Reporting design system", "Wireframe kit", "Dashboard guidelines", "Adoption toolkit & docs"],
  impact: ["One consistent reporting standard across products", "Faster dashboard creation from reusable templates", "Brand-aligned, legible data visualization", "A scalable framework the teams could extend"],
  lessons: "Designing within a tool's real constraints is the job. The wins came from building a shared foundation teams could reuse, not from perfecting any single dashboard."
},
{
  id: "ledgerline", name: "One System, Infinite Scale", thumb: "site/design-system-thumb.png", banner: "site/design-system-banner.png", longform: "ds", industry: "Healthcare · Design System", year: "2024 - 2026", tone: "blue",
  role: "Product Design Lead", duration: "Ongoing", team: "1 Engineer",
  metric: "", metricLabel: "",
  disciplines: ["Design Systems", "Interface Direction", "Design–Dev Collaboration"],
  summary: "A scalable healthcare design system enabling faster design, consistent experiences, and seamless design–development collaboration.",
  challenge: "A fragmented legacy framework had left teams building their own components and applying styles inconsistently, causing design drift, duplicated effort, and slower delivery. The brief was to transform it into a scalable, brand-aligned design system on a tight timeline — one solid enough to support multiple products and future multi-brand expansion.",
  process: [
  "Established a three-tier token architecture (brand, alias, mapped) for color, type, and spacing.",
  "Prioritized 50+ core components using an atoms → molecules → organisms methodology.",
  "Aligned every component with development through Storybook so design and code stayed in sync.",
  "Ran onboarding workshops and gathered feedback to drive adoption and keep the system lean."],

  deliverables: ["Three-tier token system", "50+ component library", "Storybook integration", "Design guidelines & docs", "Onboarding & governance"],
  impact: ["2× faster project turnaround", "Consistent output across teams and modules", "Duplication eliminated with shared components", "A scalable foundation for multi-brand growth"],
  lessons: "A design system is a living product, not a component library. Strong governance keeps it sustainable; too much flexibility just recreates the complexity you set out to remove."
},
{
  id: "atlas", name: "Reinventing Risk & Compliance", thumb: "site/risk-compliance-thumb.png", thumbScale: 1.4, longform: "atlas", industry: "Design Systems · Platform", year: "2021 - 2023", tone: "orange",
  role: "Design Systems Lead", duration: "2 Years 4 months", team: "Systems team of 3",
  metric: "3× faster", metricLabel: "Time from design to shipped UI",
  disciplines: ["Design Systems", "Design Leadership", "Governance"],
  summary: "Transforming legacy workflows into a modern, intuitive experience. Built for clarity, efficiency, and scalability.",
  challenge: "A feature-rich GRC platform relied on a one-size-fits-all dashboard that ignored the distinct needs of each persona. Years of developer-led additions had left deep navigation, inconsistent patterns, and workflows that pushed users to spreadsheets and workarounds. The challenge was accumulated UX debt, not missing capability.",
  process: [
  "Ran a comprehensive UX audit — session recordings, support tickets, stakeholder and user interviews.",
  "Restructured the information architecture around role-based experiences for each persona.",
  "Streamlined core workflows, cutting incident registration from three steps to one.",
  "Built a scalable, token-based design system with Storybook integration and UX governance."],

  deliverables: ["UX audit & research report", "Role-based IA", "Streamlined incident & risk flows", "Decision-centric dashboards", "Design system & governance"],
  impact: ["Risk & incident resolution from 15 min to 5 min", "66.7% reduction in registration effort", "Role-based dashboards for real-time visibility", "A scalable foundation for future enhancements"],
  lessons: "Great enterprise design makes complex systems feel intuitive. Simplifying intricate workflows without losing functionality mattered more than any single new screen."
},
{
  id: "northwind", name: "Transforming the Payroll Experience", thumb: "site/payroll-app-thumb.jpg", longform: "northwind", industry: "HR Tech · Payroll", year: "2021", tone: "coral",
  role: "Senior UX Designer", duration: "2020 — 2021", team: "2 designers",
  metric: "8.07 / 10", metricLabel: "Avg. first-round test rating",
  disciplines: ["Product Design", "Complex Workflows", "Enterprise UX"],
  summary: "A UX redesign that unifies payroll modules into a modern, intuitive, and efficient platform.",
  challenge: "The payroll platform had grown into disconnected modules, each with its own patterns, leaving users to navigate dense, unintuitive workflows for everyday tasks. The brief was to unify the modules into one modern, efficient experience without disrupting the people who relied on it daily.",
  process: [
  "Audited the existing modules and mapped where users hit friction and dropped to workarounds.",
  "Unified the experience around consistent patterns and a clearer information architecture.",
  "Made timesheets and payroll data legible with clearer hierarchy and terminology.",
  "Validated the redesign through moderated usability testing and iterated on feedback."],

  deliverables: ["Unified payroll experience", "Consistent module patterns", "Legible timesheet views", "Usability-tested prototype"],
  impact: ["8.07 / 10 average first-round test rating", "Everyday tasks made faster and clearer", "Consistent experience across modules", "A modern foundation for the platform to grow"],
  lessons: "Unifying a fragmented product is mostly about restoring consistency. When patterns repeat, users stop relearning the interface and start trusting it."
},
{
  id: "meridian", name: "Learn through cooking", thumb: "site/cooking-app-thumb.jpg", longform: "cooking", industry: "EdTech · iPad App", year: "2020-2021", tone: "magenta",
  role: "Senior UX Designer", duration: "Jul 2020 - Mar 2021", team: "2 Designers",
  metric: "100%", metricLabel: "Recipe-flow completion in testing",
  disciplines: ["Product Design", "UX Research", "EdTech"],
  summary: "A gamified application that helps teach STEAM (science, technology, engineering and arts), through healthy cooking and dynamic technology.",
  challenge: "An iPad learning platform taught STEAM through cooking, but poor UX planning made it hard to use — a survey found 60% of students couldn't follow the existing design, steps were too long, and lessons carried a heavy cognitive load. The brief was a research-grounded redesign and rebrand that made learning feel magical.",
  process: [
  "Evaluated the existing app and ran 1-on-1 interviews with students, a teacher, and a parent.",
  "Built a persona and mapped the recipe-cooking task flow end to end.",
  "Restructured the information architecture and designed low- then high-fidelity prototypes.",
  "Usability-tested the prototype with five users and folded findings into the next iteration."],

  deliverables: ["UX research & persona", "Information architecture", "Recipe task flow", "Low- & hi-fi prototypes", "Rebranded iPad experience"],
  impact: ["100% recipe-flow completion in testing", "Engaging, assessment-driven cooking lessons", "Clearer, more accessible learning steps", "A foundation targeting +25% signups, +50% school registrations"],
  lessons: "Designing for kids rewards clarity over cleverness. Grounding every decision in research kept the experience genuinely usable, not just playful."
}];


const SERVICES = [
{
  n: "01", name: "Product Design", tags: ["End-to-end", "0→1 & scale"],
  blurb: "End-to-end product design for complex software — from early strategy and user flows to polished, shipped interfaces. I turn tangled workflows into products teams actually want to use.",
  problem: "Complex products that have outgrown their original design and now confuse the people who depend on them.",
  value: "Strategy-through-execution design that turns tangled workflows into products teams actually want to use — shipped, not just shown.",
  client: "Founders and product leaders building serious software in regulated or complex domains."
},
{
  n: "02", name: "Design Systems", tags: ["Tokens-first", "Governance"],
  blurb: "Scalable, tokens-first design systems with the governance to keep them alive — so multiple teams ship consistently, accessibly, and far faster than before.",
  problem: "Multiple products drifting apart, teams rebuilding the same components, brand and accessibility slipping.",
  value: "A scalable, governed system — tokens, components, and the operating model to keep it alive — so teams ship consistently and faster.",
  client: "Scaling orgs with several products and no single source of truth."
},
{
  n: "03", name: "Interface Direction", tags: ["UX vision", "Leadership"],
  blurb: "Experience direction and design leadership that set a clear UX vision and raise the craft bar across every surface of the product.",
  problem: "A product with no coherent point of view — every screen a different opinion, no one steering the experience.",
  value: "Experience direction and design leadership that set a clear vision and raise the bar across every surface.",
  client: "Teams that need senior craft and direction without a full-time hire."
},
{
  n: "04", name: "Brand Identity", tags: ["Digital-first", "Systems"],
  blurb: "Digital-first brand systems that hold together from the marketing site to the deepest admin screen — one coherent experience, end to end.",
  problem: "A brand that looks one way in marketing and another inside the product, eroding trust at every touchpoint.",
  value: "Digital-first brand systems that hold together from the homepage to the deepest admin screen.",
  client: "Companies whose brand and product experience have fallen out of sync."
},
{
  n: "05", name: "Advisory & Audits", tags: ["Reviews", "Strategy"],
  blurb: "UX audits, design reviews, and maturity assessments that turn a vague 'something's off' into a ranked, actionable roadmap you can move on.",
  problem: "A nagging sense the experience is holding growth back — but no clear, prioritized plan to fix it.",
  value: "UX audits, design reviews, and maturity assessments that turn a vague concern into a ranked, actionable roadmap.",
  client: "Leaders who need an expert outside read before committing budget."
}];


const PRINCIPLES = [
{ n: "01", title: "Start with the real workflow", body: "Not the one in the docs. I shadow the people who'll use the product until I understand the job they're actually doing — then design for that." },
{ n: "02", title: "Make complexity legible", body: "Enterprise software is complex for real reasons. The work isn't to hide that — it's to make it understandable, so people can act with confidence." },
{ n: "03", title: "Design the system, not the screen", body: "One good screen is a sketch. A system of decisions — tokens, patterns, principles — is what scales across products and survives the next feature." },
{ n: "04", title: "Tie design to business outcomes", body: "Beautiful that doesn't move a number is decoration. I design toward the metric that matters and measure whether it moved." }];


const TESTIMONIALS = [
{ quote: "Martin doesn't just design screens — he reframes the problem. He cut our audit prep from weeks to days and changed how the whole team thinks about the product.", name: "Priya Nair", role: "VP Product, Sentinel" },
{ quote: "The most senior design thinking we've worked with. Martin built a system that's still the backbone of every product we ship two years later.", name: "Daniel Roth", role: "Head of Design, Atlas Platform" },
{ quote: "He made a deeply technical product feel obvious. Adoption quadrupled. I'd hire him again in a heartbeat.", name: "Mei Lin", role: "Founder & CEO, Meridian" }];


const CLIENT_LOGOS = [];

const FAQS = [
{ q: "What kinds of projects do you take on?", a: "Complex, high-stakes software — enterprise platforms, GRC and compliance tools, fintech, data products, and design systems. If the problem is genuinely hard and the stakes are real, it's interesting to me." },
{ q: "How do you usually engage?", a: "Three common shapes: a fixed-scope project (a redesign, a 0→1 product, a design system), an ongoing advisory retainer, or a focused audit. We'll pick the one that fits your moment on a first call." },
{ q: "Do you work with teams outside India?", a: "Yes — most of my work is with distributed teams across the US, Europe, and Asia. I'm based in Bangalore and overlap comfortably with most time zones." },
{ q: "What does a typical timeline look like?", a: "Audits run 2–3 weeks. Project engagements typically run 2–6 months depending on scope. I take on a small number at a time so each gets senior attention end to end." },
{ q: "Is there a minimum budget?", a: "Engagements start around the cost of a senior in-house designer for the period. But reach out regardless — if the problem is a fit, we'll find a sensible shape." },
{ q: "Can you work with our existing team?", a: "Almost always. I plug into your product, engineering, and design partners directly — raising the bar with the team, not around it." }];


/* ---------------- Scroll progress (pinned below nav) ---------------- */
function ScrollProgress() {
  const [pct, setPct] = React.useState(0);
  React.useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      setPct(max > 0 ? (h.scrollTop / max) * 100 : 0);
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, []);
  return (
    <div className="scroll-progress" aria-hidden="true">
      <div className="scroll-progress-fill" style={{ transform: `scaleX(${pct / 100})` }}></div>
    </div>
  );
}

/* ---------------- expose ---------------- */
Object.assign(window, {
  Reveal, Eyebrow, Nav, CTABand, SiteFooter, ProjectCover, ScrollProgress,
  STATS, PROJECTS, SERVICES, PRINCIPLES, TESTIMONIALS, CLIENT_LOGOS, FAQS
});
